commit
179e825e60
@ -479,6 +479,10 @@ convert -scale 48x48 "$APP_ICON" "$DIRNAME/res/drawable-mdpi/icon.png"
|
||||
convert -scale 96x96 "$APP_ICON" "$DIRNAME/res/drawable-xhdpi/icon.png"
|
||||
convert -scale 144x144 "$APP_ICON" "$DIRNAME/res/drawable-xxhdpi/icon.png"
|
||||
|
||||
if [ -f "/usr/lib/jvm/java-8-openjdk-amd64/bin/java" ]; then
|
||||
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
fi
|
||||
|
||||
if [ "$BUILD_TOOL" = "gradle" ]; then
|
||||
export ANDROID_HOME="$SDK_PATH"
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="abyss" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="0"/>
|
||||
|
||||
<best>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="150"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements position="1" time="160"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1" time="165"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="197"/>
|
||||
</medium>
|
||||
<easy>
|
||||
|
@ -1,21 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="candela_city" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="70"/>
|
||||
<requirements trophies="75"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
|
||||
<unlock kart="sara_the_wizard"/>
|
||||
<unlock kart="sara_the_racer"/>
|
||||
</challenge>
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="cocoa_temple" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="5"/>
|
||||
|
||||
|
||||
<best>
|
||||
<karts number="8"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="170"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="6"/>
|
||||
<requirements time="210"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="300"/>
|
||||
</easy>
|
||||
</challenge>
|
@ -1,15 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="cornfield_crossing" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="0"/>
|
||||
|
||||
<best>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1" time="165"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="195"/>
|
||||
</medium>
|
||||
<easy>
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="fortmagma" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="180"/>
|
||||
<requirements trophies="190"/>
|
||||
|
||||
<best>
|
||||
<karts number="2" aiIdent="nolok" superPower="nolokBoss"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="2" aiIdent="nolok" superPower="nolokBoss"/>
|
||||
<requirements position="1"/>
|
||||
@ -19,6 +24,5 @@
|
||||
|
||||
<unlock kart="gnu"/>
|
||||
<unlock kart="nolok"/>
|
||||
<unlock difficulty="difficulty_best"/>
|
||||
<unlock track="fortmagma"/>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<grandprix id="1_penguinplayground"/>
|
||||
<mode major="grandprix" minor="quickrace"/>
|
||||
<requirements trophies="40"/>
|
||||
<requirements trophies="30"/>
|
||||
|
||||
<best>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="4"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<grandprix id="2_offthebeatentrack"/>
|
||||
<mode major="grandprix" minor="quickrace"/>
|
||||
<requirements trophies="85"/>
|
||||
<requirements trophies="70"/>
|
||||
|
||||
<best>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="5"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<grandprix id="3_tothemoonandback"/>
|
||||
<mode major="grandprix" minor="quickrace"/>
|
||||
<requirements trophies="125"/>
|
||||
<requirements trophies="120"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<grandprix id="4_atworldsend"/>
|
||||
<mode major="grandprix" minor="quickrace"/>
|
||||
<requirements trophies="165"/>
|
||||
|
||||
<best>
|
||||
<karts number="10"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,20 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="gran_paradiso_island" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="95"/>
|
||||
<requirements trophies="80"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="greenvalley" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="95"/>
|
||||
<requirements trophies="90"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="hacienda" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="45"/>
|
||||
<requirements trophies="25"/>
|
||||
|
||||
<best>
|
||||
<karts number="8"/>
|
||||
<requirements position="1" time="130"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements position="2" time="170"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="160"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<requirements time="187"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="2" time="187"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="260"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,20 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<track id="lighthouse" laps="3"/>
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="lighthouse" laps="4"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="135"/>
|
||||
<requirements trophies="125"/>
|
||||
|
||||
<best>
|
||||
<karts number="10"/>
|
||||
<requirements position="1" time="120"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements time="110" position="1"/>
|
||||
<karts number="9"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<requirements time="145" position="1"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1" time="190"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<requirements time="185"/>
|
||||
<karts number="7"/>
|
||||
<requirements time="250"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
||||
|
@ -1,18 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="mansion" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="110"/>
|
||||
<requirements trophies="100"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1" time="100"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements time="110"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1" time="115"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<requirements time="130"/>
|
||||
<karts number="7"/>
|
||||
<requirements time="140"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<requirements time="160"/>
|
||||
<karts number="6"/>
|
||||
<requirements time="180"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="mines" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="150"/>
|
||||
<requirements trophies="140"/>
|
||||
|
||||
<best>
|
||||
<karts number="10"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="4"/>
|
||||
<requirements time="160"/>
|
||||
<karts number="9"/>
|
||||
<requirements position="1" time="160"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<requirements time="190"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="2" time="190"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements time="255"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<track id="minigolf" laps="3"/>
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="minigolf" laps="4"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="150"/>
|
||||
<requirements trophies="130"/>
|
||||
|
||||
<best>
|
||||
<karts number="10"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="3"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,20 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<track id="olivermath" laps="3"/>
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="olivermath" laps="5"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="0"/>
|
||||
|
||||
|
||||
<best>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="95"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements position="1" time="65"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1" time="110"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="5"/>
|
||||
<requirements position="1" time="85"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="5"/>
|
||||
<requirements position="1" time="130"/>
|
||||
<karts number="4"/>
|
||||
<requirements position="1" time="210"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="sandtrack" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="0"/>
|
||||
|
||||
<best>
|
||||
<karts number="1"/>
|
||||
<requirements energy="20" time="135"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="1"/>
|
||||
<requirements energy="18" time="167"/>
|
||||
<requirements energy="18" time="165"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="1"/>
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="scotland" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="0"/>
|
||||
|
||||
<best>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1" time="165"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="185"/>
|
||||
</medium>
|
||||
<easy>
|
||||
|
@ -1,18 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="snowmountain" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="60"/>
|
||||
<requirements trophies="115"/>
|
||||
|
||||
<best>
|
||||
<karts number="10"/>
|
||||
<requirements position="1" time="120"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements time="145"/>
|
||||
<karts number="9"/>
|
||||
<requirements position="2" time="145"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="8"/>
|
||||
<requirements time="187"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="7"/>
|
||||
<requirements time="250"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="snowtuxpeak" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="60"/>
|
||||
<requirements trophies="45"/>
|
||||
|
||||
<best>
|
||||
<karts number="8"/>
|
||||
<requirements position="1" time="120"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<requirements position="1" time="145"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1" time="140"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="6"/>
|
||||
<requirements time="170"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<requirements time="195"/>
|
||||
<karts number="5"/>
|
||||
<requirements time="210"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<track id="stk_enterprise" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="110"/>
|
||||
|
||||
<hard>
|
||||
<karts number="6"/>
|
||||
<requirements position="2"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="6"/>
|
||||
<requirements position="2"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="5"/>
|
||||
<requirements position="2"/>
|
||||
</easy>
|
||||
</challenge>
|
24
data/challenges/stk_enterprise.challenge
Normal file
24
data/challenges/stk_enterprise.challenge
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="stk_enterprise" laps="3"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="105"/>
|
||||
|
||||
<best>
|
||||
<karts number="9"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
8
data/challenges/unlock_bonus_kart1.challenge
Normal file
8
data/challenges/unlock_bonus_kart1.challenge
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="3">
|
||||
<unlock_list list="true"/>
|
||||
<!-- This is the (rounded) point equivalent of finishing all challenges
|
||||
in easy, except the final challenge -->
|
||||
<requirements trophies="190"/>
|
||||
<unlock kart="amanda"/>
|
||||
</challenge>
|
8
data/challenges/unlock_bonus_kart2.challenge
Normal file
8
data/challenges/unlock_bonus_kart2.challenge
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="3">
|
||||
<unlock_list list="true"/>
|
||||
<!-- This is the point equivalent of finishing the
|
||||
story mode with 8 supertux challenges and the rest in gold -->
|
||||
<requirements trophies="280"/>
|
||||
<unlock kart="sara_the_wizard"/>
|
||||
</challenge>
|
8
data/challenges/unlock_supertux.challenge
Normal file
8
data/challenges/unlock_supertux.challenge
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="3">
|
||||
<unlock_list list="true"/>
|
||||
<!-- This is the point equivalent of finishing the
|
||||
story mode with all gold except 14 silver challenges -->
|
||||
<requirements trophies="250"/>
|
||||
<unlock difficulty="difficulty_best"/>
|
||||
</challenge>
|
@ -1,19 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="volcano_island" laps="2"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="15"/>
|
||||
|
||||
<best>
|
||||
<karts number="8"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="5"/>
|
||||
<karts number="7"/>
|
||||
<requirements position="1"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="4"/>
|
||||
<karts number="6"/>
|
||||
<requirements position="1"/>
|
||||
</medium>
|
||||
<easy>
|
||||
<karts number="4"/>
|
||||
<karts number="5"/>
|
||||
<requirements position="1"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
@ -1,12 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="xr591" laps="2"/>
|
||||
<mode major="single" minor="quickrace"/>
|
||||
<requirements trophies="135"/>
|
||||
|
||||
<best>
|
||||
<karts number="1"/>
|
||||
<requirements energy="20" time="100"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="1"/>
|
||||
<requirements energy="18" time="120"/>
|
||||
<requirements energy="18" time="115"/>
|
||||
</hard>
|
||||
<medium>
|
||||
<karts number="1"/>
|
||||
@ -17,5 +22,3 @@
|
||||
<requirements energy="12" time="180"/>
|
||||
</easy>
|
||||
</challenge>
|
||||
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<challenge version="2">
|
||||
<track id="zengarden" laps="3"/>
|
||||
<challenge version="3">
|
||||
<unlock_list list="false"/>
|
||||
<track id="zengarden" laps="4"/>
|
||||
<mode major="single" minor="timetrial"/>
|
||||
<requirements trophies="45"/>
|
||||
<requirements trophies="35"/>
|
||||
|
||||
<best>
|
||||
<karts number="3"/>
|
||||
<requirements position="1"/>
|
||||
</best>
|
||||
<hard>
|
||||
<karts number="2"/>
|
||||
<requirements position="1"/>
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
<track id="sandtrack" laps="3" reverse="false" />
|
||||
<track id="cornfield_crossing" laps="3" reverse="false" />
|
||||
<track id="olivermath" laps="4" reverse="false" />
|
||||
<track id="olivermath" laps="5" reverse="false" />
|
||||
<track id="abyss" laps="3" reverse="false" />
|
||||
<track id="scotland" laps="3" reverse="false" />
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
<supertuxkart_grand_prix name="Off the beaten track">
|
||||
|
||||
<track id="cocoa_temple" laps="3" reverse="false" />
|
||||
<track id="snowmountain" laps="3" reverse="false" />
|
||||
<track id="hacienda" laps="3" reverse="false" />
|
||||
<track id="zengarden" laps="4" reverse="false" />
|
||||
<track id="snowtuxpeak" laps="3" reverse="false" />
|
||||
<track id="cocoa_temple" laps="3" reverse="false" />
|
||||
<track id="hacienda" laps="3" reverse="false" />
|
||||
<track id="zengarden" laps="4" reverse="false" />
|
||||
<track id="volcano_island" laps="2" reverse="false" />
|
||||
<track id="snowtuxpeak" laps="3" reverse="false" />
|
||||
|
||||
</supertuxkart_grand_prix>
|
||||
|
@ -2,10 +2,9 @@
|
||||
<supertuxkart_grand_prix name="To the moon and back">
|
||||
|
||||
<track id="gran_paradiso_island" laps="3" reverse="false" />
|
||||
<track id="greenvalley" laps="2" reverse="false" />
|
||||
<track id="greenvalley" laps="3" reverse="false" />
|
||||
<track id="mansion" laps="3" reverse="false" />
|
||||
<track id="stk_enterprise" laps="3" reverse="false" />
|
||||
<track id="volcano_island" laps="2" reverse="false" />
|
||||
<track id="candela_city" laps="3" reverse="false" />
|
||||
|
||||
</supertuxkart_grand_prix>
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
|
||||
<supertuxkart_grand_prix name="At World's End">
|
||||
|
||||
<track id="fortmagma" laps="3" reverse="false" />
|
||||
<track id="minigolf" laps="3" reverse="false" />
|
||||
<track id="lighthouse" laps="4" reverse="false" />
|
||||
<track id="snowmountain" laps="3" reverse="false" />
|
||||
<track id="minigolf" laps="4" reverse="false" />
|
||||
<track id="xr591" laps="3" reverse="false" />
|
||||
<track id="mines" laps="3" reverse="false" />
|
||||
<track id="lighthouse" laps="4" reverse="false" />
|
||||
|
||||
</supertuxkart_grand_prix>
|
||||
|
||||
|
BIN
data/gui/cup_platinum.png
Normal file
BIN
data/gui/cup_platinum.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -136,7 +136,7 @@
|
||||
<div layout="horizontal-row" width="100%" proportion="1">
|
||||
<label text="Particles Effects" I18N="Video settings" width="40%"/>
|
||||
<spacer width="10" height="10"/>
|
||||
<gauge id="particles_effects" min_value="0" max_value="2" width="50%" />
|
||||
<gauge id="particles_effects" min_value="1" max_value="2" width="50%" />
|
||||
</div>
|
||||
|
||||
<spacer height="4" width="10" />
|
||||
|
BIN
data/gui/mystery_unlock.png
Normal file
BIN
data/gui/mystery_unlock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
@ -21,7 +21,7 @@
|
||||
<label id="novice_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
<spacer height="6%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="intermediate" icon="gui/difficulty_medium.png"
|
||||
@ -30,7 +30,7 @@
|
||||
<label id="intermediate_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
<spacer height="6%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="expert" icon="gui/difficulty_hard.png"
|
||||
@ -39,7 +39,16 @@
|
||||
<label id="difficult_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
<spacer height="6%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="supertux" icon="gui/difficulty_best.png"
|
||||
I18N="Difficulty" text="SuperTux" height="100%"/>
|
||||
<spacer width="5%" height="1"/>
|
||||
<label id="supertux_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="2%" width="1"/>
|
||||
|
||||
</div>
|
||||
</stkgui>
|
||||
|
46
data/gui/select_challenge_nobest.stkgui
Normal file
46
data/gui/select_challenge_nobest.stkgui
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<stkgui>
|
||||
<div x="5%" y="5%" width="90%" height="90%" layout="vertical-row" >
|
||||
|
||||
<header id="title" width="100%" text="Race Setup" align="center" text_align="center" />
|
||||
|
||||
<spacer height="2%" width="1"/>
|
||||
|
||||
<div width="100%" layout="horizontal-row" height="fit">
|
||||
<label id="race_type" text="Type:" I18N="Type of race, in a challenge"/>
|
||||
<spacer width="5" height="1"/>
|
||||
<label id="race_type_val" proportion="1"/>
|
||||
</div>
|
||||
|
||||
<spacer height="2%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="novice" icon="gui/difficulty_easy.png"
|
||||
I18N="Difficulty" text="Novice" height="100%"/>
|
||||
<spacer width="5%" height="1"/>
|
||||
<label id="novice_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="intermediate" icon="gui/difficulty_medium.png"
|
||||
I18N="Difficulty" text="Intermediate" height="100%"/>
|
||||
<spacer width="5%" height="1"/>
|
||||
<label id="intermediate_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
|
||||
<div width="100%" proportion="1" layout="horizontal-row">
|
||||
<icon-button id="expert" icon="gui/difficulty_hard.png"
|
||||
I18N="Difficulty" text="Expert" height="100%"/>
|
||||
<spacer width="5%" height="1"/>
|
||||
<label id="difficult_label" proportion="1" height="100%"/>
|
||||
</div>
|
||||
|
||||
<spacer height="8%" width="1"/>
|
||||
|
||||
</div>
|
||||
</stkgui>
|
||||
|
@ -403,7 +403,7 @@
|
||||
<player-characteristics>
|
||||
<characteristic name="normal" />
|
||||
<characteristic name="handicap">
|
||||
<engine brake-factor="*0.8" brake-time-increase="*0.85" max-speed-reverse-ratio="*0.8" />
|
||||
<engine power="*0.9" max-speed="*0.9" brake-factor="*0.8" brake-time-increase="*0.85" max-speed-reverse-ratio="*0.8" />
|
||||
<bubblegum duration="*1.5" speed-fraction="*1.5" torque="*1.5" />
|
||||
<zipper duration="*0.8" force="*0.8" speed-gain="*0.8" max-speed-increase="*0.8" />
|
||||
<swatter duration="*0.8" squash-duration="*1.5" squash-slowdown="*1.8" />
|
||||
|
@ -43,6 +43,7 @@ ChallengeData::ChallengeData(const std::string& filename)
|
||||
m_gp_id = "";
|
||||
m_version = 0;
|
||||
m_num_trophies = 0;
|
||||
m_is_unlock_list = false;
|
||||
m_is_ghost_replay = false;
|
||||
|
||||
for (int d=0; d<RaceManager::DIFFICULTY_COUNT; d++)
|
||||
@ -66,7 +67,7 @@ ChallengeData::ChallengeData(const std::string& filename)
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
|
||||
setId(StringUtils::removeExtension(StringUtils::getBasename(filename)));
|
||||
setChallengeId(StringUtils::removeExtension(StringUtils::getBasename(filename)));
|
||||
|
||||
root->get("version", &m_version);
|
||||
// No need to get the rest of the data if this challenge
|
||||
@ -79,7 +80,48 @@ ChallengeData::ChallengeData(const std::string& filename)
|
||||
return;
|
||||
}
|
||||
|
||||
m_is_unlock_list = false;
|
||||
const XMLNode* unlock_list_node = root->getNode("unlock_list");
|
||||
if (unlock_list_node != NULL)
|
||||
{
|
||||
std::string list;
|
||||
unlock_list_node->get("list", &list);
|
||||
m_is_unlock_list = (list=="true");
|
||||
}
|
||||
|
||||
std::vector<XMLNode*> unlocks;
|
||||
root->getNodes("unlock", unlocks);
|
||||
for(unsigned int i=0; i<unlocks.size(); i++)
|
||||
{
|
||||
std::string s;
|
||||
if(unlocks[i]->get("kart", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_KART);
|
||||
else if(unlocks[i]->get("track", &s))
|
||||
addUnlockTrackReward(s);
|
||||
else if(unlocks[i]->get("gp", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_GP);
|
||||
else if(unlocks[i]->get("mode", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_MODE);
|
||||
else if(unlocks[i]->get("difficulty", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_DIFFICULTY);
|
||||
else
|
||||
{
|
||||
Log::warn("ChallengeData", "Unknown unlock entry. Must be one of kart, track, gp, mode, difficulty.");
|
||||
throw std::runtime_error("Unknown unlock entry");
|
||||
}
|
||||
}
|
||||
|
||||
const XMLNode* requirements_node = root->getNode("requirements");
|
||||
if (requirements_node == NULL)
|
||||
{
|
||||
throw std::runtime_error("Challenge file " + filename +
|
||||
" has no <requirements> node!");
|
||||
}
|
||||
requirements_node->get("trophies", &m_num_trophies);
|
||||
|
||||
//Don't check further if this is an unlock list
|
||||
if(m_is_unlock_list)
|
||||
return;
|
||||
|
||||
const XMLNode* mode_node = root->getNode("mode");
|
||||
if (mode_node == NULL)
|
||||
@ -149,27 +191,19 @@ ChallengeData::ChallengeData(const std::string& filename)
|
||||
}
|
||||
}
|
||||
|
||||
const XMLNode* requirements_node = root->getNode("requirements");
|
||||
if (requirements_node == NULL)
|
||||
{
|
||||
throw std::runtime_error("Challenge file " + filename +
|
||||
" has no <requirements> node!");
|
||||
}
|
||||
requirements_node->get("trophies", &m_num_trophies);
|
||||
|
||||
const XMLNode* difficulties[RaceManager::DIFFICULTY_COUNT];
|
||||
difficulties[0] = root->getNode("easy");
|
||||
difficulties[1] = root->getNode("medium");
|
||||
difficulties[2] = root->getNode("hard");
|
||||
difficulties[3] = root->getNode("best");
|
||||
|
||||
// Note that the challenges can only be done in three difficulties
|
||||
if (difficulties[0] == NULL || difficulties[1] == NULL ||
|
||||
difficulties[2] == NULL)
|
||||
difficulties[2] == NULL || difficulties[3] == NULL)
|
||||
{
|
||||
error("<easy> or <medium> or <hard>");
|
||||
error("<easy> or <medium> or <hard> or <best>");
|
||||
}
|
||||
|
||||
for (int d=0; d<=RaceManager::DIFFICULTY_HARD; d++)
|
||||
for (int d=0; d<=RaceManager::DIFFICULTY_BEST; d++)
|
||||
{
|
||||
const XMLNode* karts_node = difficulties[d]->getNode("karts");
|
||||
if (karts_node == NULL) error("<karts .../>");
|
||||
@ -229,28 +263,6 @@ ChallengeData::ChallengeData(const std::string& filename)
|
||||
if (requirements_node->get("energy", &energy)) m_energy[d] = energy;
|
||||
|
||||
}
|
||||
|
||||
std::vector<XMLNode*> unlocks;
|
||||
root->getNodes("unlock", unlocks);
|
||||
for(unsigned int i=0; i<unlocks.size(); i++)
|
||||
{
|
||||
std::string s;
|
||||
if(unlocks[i]->get("kart", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_KART);
|
||||
else if(unlocks[i]->get("track", &s))
|
||||
addUnlockTrackReward(s);
|
||||
else if(unlocks[i]->get("gp", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_GP);
|
||||
else if(unlocks[i]->get("mode", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_MODE);
|
||||
else if(unlocks[i]->get("difficulty", &s))
|
||||
setUnlocks(s, ChallengeData::UNLOCK_DIFFICULTY);
|
||||
else
|
||||
{
|
||||
Log::warn("ChallengeData", "Unknown unlock entry. Must be one of kart, track, gp, mode, difficulty.");
|
||||
throw std::runtime_error("Unknown unlock entry");
|
||||
}
|
||||
}
|
||||
} // ChallengeData
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -486,7 +498,7 @@ bool ChallengeData::isChallengeFulfilled() const
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns true if this GP challenge is fulfilled.
|
||||
*/
|
||||
bool ChallengeData::isGPFulfilled() const
|
||||
ChallengeData::GPLevel ChallengeData::isGPFulfilled() const
|
||||
{
|
||||
int d = race_manager->getDifficulty();
|
||||
|
||||
@ -496,14 +508,25 @@ bool ChallengeData::isGPFulfilled() const
|
||||
race_manager->getMinorMode() != m_minor ||
|
||||
race_manager->getGrandPrix().getId() != m_gp_id ||
|
||||
race_manager->getNumberOfKarts() < (unsigned int)m_default_num_karts[d] ||
|
||||
race_manager->getNumPlayers() > 1) return false;
|
||||
race_manager->getNumPlayers() > 1) return GP_NONE;
|
||||
|
||||
// check if the player came first.
|
||||
// rank == 0 if first, 1 if second, etc.
|
||||
const int rank = race_manager->getLocalPlayerGPRank(0);
|
||||
|
||||
if (rank != 0) return false;
|
||||
|
||||
return true;
|
||||
// In superior difficulty levels, losing a place means
|
||||
// getting a cup of the inferior level rather than
|
||||
// nothing at all
|
||||
int unlock_level = d - rank;
|
||||
if (unlock_level == 3)
|
||||
return GP_BEST;
|
||||
if (unlock_level == 2)
|
||||
return GP_HARD;
|
||||
if (unlock_level == 1)
|
||||
return GP_MEDIUM;
|
||||
if (unlock_level == 0)
|
||||
return GP_EASY;
|
||||
return GP_NONE;
|
||||
} // isGPFulfilled
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -641,4 +664,3 @@ void ChallengeData::addUnlockKartReward(const std::string &internal_name,
|
||||
feature.m_user_name = user_name;
|
||||
m_feature.push_back(feature);
|
||||
} // addUnlockKartReward
|
||||
|
||||
|
@ -44,6 +44,17 @@ public:
|
||||
UNLOCK_KART,
|
||||
UNLOCK_DIFFICULTY
|
||||
};
|
||||
|
||||
/** The level of completion of a GP challenge
|
||||
*/
|
||||
enum GPLevel
|
||||
{
|
||||
GP_NONE,
|
||||
GP_EASY,
|
||||
GP_MEDIUM,
|
||||
GP_HARD,
|
||||
GP_BEST
|
||||
};
|
||||
// ------------------------------------------------------------------------
|
||||
class UnlockableFeature
|
||||
{
|
||||
@ -95,6 +106,7 @@ private:
|
||||
std::string m_filename;
|
||||
/** Version number of the challenge. */
|
||||
int m_version;
|
||||
bool m_is_unlock_list;
|
||||
bool m_is_ghost_replay;
|
||||
|
||||
void setUnlocks(const std::string &id,
|
||||
@ -120,7 +132,7 @@ public:
|
||||
|
||||
virtual void check() const;
|
||||
virtual bool isChallengeFulfilled() const;
|
||||
virtual bool isGPFulfilled() const;
|
||||
virtual GPLevel isGPFulfilled() const;
|
||||
void addUnlockTrackReward(const std::string &track_name);
|
||||
void addUnlockModeReward(const std::string &internal_mode_name,
|
||||
const irr::core::stringw &user_mode_name);
|
||||
@ -142,11 +154,11 @@ public:
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the id of the challenge. */
|
||||
const std::string &getId() const { return m_id; }
|
||||
const std::string &getChallengeId() const { return m_id; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets the id of this challenge. */
|
||||
void setId(const std::string& s) { m_id = s; }
|
||||
void setChallengeId(const std::string& s) { m_id = s; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the track associated with this challenge. */
|
||||
@ -185,6 +197,9 @@ public:
|
||||
/** Returns if this challenge is using ghost replay. */
|
||||
bool isGhostReplay() const { return m_is_ghost_replay; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if this challenge is an unlock list. */
|
||||
bool isUnlockList() const { return m_is_unlock_list; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the challenge mode of this challenge. */
|
||||
ChallengeModeType getMode() const { return m_mode; }
|
||||
// ------------------------------------------------------------------------
|
||||
@ -196,9 +211,9 @@ public:
|
||||
const irr::core::stringw getChallengeDescription() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the minimum position the player must have in order to win.
|
||||
/** Returns the maximum position the player must have in order to win.
|
||||
*/
|
||||
int getPosition(RaceManager::Difficulty difficulty) const
|
||||
int getMaxPosition(RaceManager::Difficulty difficulty) const
|
||||
{
|
||||
return m_position[difficulty];
|
||||
} // getPosition
|
||||
|
@ -35,18 +35,19 @@
|
||||
*/
|
||||
void ChallengeStatus::load(const XMLNode* challenges_node)
|
||||
{
|
||||
const XMLNode* node = challenges_node->getNode( m_data->getId() );
|
||||
const XMLNode* node = challenges_node->getNode( m_data->getChallengeId() );
|
||||
if(node == NULL)
|
||||
{
|
||||
Log::info("ChallengeStatus", "Couldn't find node <%s> in challenge list."
|
||||
"(If this is the first time you play this is normal)\n",
|
||||
m_data->getId().c_str());
|
||||
m_data->getChallengeId().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_state[0] = CH_INACTIVE;
|
||||
m_state[1] = CH_INACTIVE;
|
||||
m_state[2] = CH_INACTIVE;
|
||||
m_state[3] = CH_INACTIVE;
|
||||
|
||||
std::string solved;
|
||||
if (node->get("solved", &solved))
|
||||
@ -64,6 +65,13 @@ void ChallengeStatus::load(const XMLNode* challenges_node)
|
||||
m_state[1] = CH_SOLVED;
|
||||
m_state[2] = CH_SOLVED;
|
||||
}
|
||||
else if (solved == "best")
|
||||
{
|
||||
m_state[0] = CH_SOLVED;
|
||||
m_state[1] = CH_SOLVED;
|
||||
m_state[2] = CH_SOLVED;
|
||||
m_state[3] = CH_SOLVED;
|
||||
}
|
||||
} // if has 'solved' attribute
|
||||
|
||||
} // load
|
||||
@ -78,14 +86,28 @@ void ChallengeStatus::setSolved(RaceManager::Difficulty d)
|
||||
{
|
||||
m_state[curr] = CH_SOLVED;
|
||||
}
|
||||
}
|
||||
} // setSolved
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
bool ChallengeStatus::isUnlockList()
|
||||
{
|
||||
return m_data->isUnlockList();
|
||||
} // isUnlockList
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
bool ChallengeStatus::isGrandPrix()
|
||||
{
|
||||
return m_data->isGrandPrix();
|
||||
} // isUnlockList
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ChallengeStatus::save(UTFWriter& writer)
|
||||
{
|
||||
writer << L" <" << m_data->getId();
|
||||
if (isSolved(RaceManager::DIFFICULTY_HARD))
|
||||
writer << L" <" << m_data->getChallengeId();
|
||||
if (isSolved(RaceManager::DIFFICULTY_BEST))
|
||||
writer << L" solved=\"best\"/>\n";
|
||||
else if (isSolved(RaceManager::DIFFICULTY_HARD))
|
||||
writer << L" solved=\"hard\"/>\n";
|
||||
else if (isSolved(RaceManager::DIFFICULTY_MEDIUM))
|
||||
writer << L" solved=\"medium\"/>\n";
|
||||
|
@ -57,6 +57,7 @@ private:
|
||||
enum {CH_INACTIVE, // challenge not yet possible
|
||||
CH_ACTIVE, // challenge possible, but not yet solved
|
||||
CH_SOLVED} // challenge was solved
|
||||
|
||||
m_state[RaceManager::DIFFICULTY_COUNT];
|
||||
|
||||
/** Pointer to the original challenge data. */
|
||||
@ -69,6 +70,7 @@ public:
|
||||
m_state[RaceManager::DIFFICULTY_EASY] = CH_INACTIVE;
|
||||
m_state[RaceManager::DIFFICULTY_MEDIUM] = CH_INACTIVE;
|
||||
m_state[RaceManager::DIFFICULTY_HARD] = CH_INACTIVE;
|
||||
m_state[RaceManager::DIFFICULTY_BEST] = CH_INACTIVE;
|
||||
}
|
||||
virtual ~ChallengeStatus() {};
|
||||
void load(const XMLNode* config);
|
||||
@ -88,7 +90,7 @@ public:
|
||||
bool isSolvedAtAnyDifficulty() const
|
||||
{
|
||||
return m_state[0]==CH_SOLVED || m_state[1]==CH_SOLVED ||
|
||||
m_state[2]==CH_SOLVED;
|
||||
m_state[2]==CH_SOLVED || m_state[3]==CH_SOLVED;
|
||||
} // isSolvedAtAnyDifficulty
|
||||
// ------------------------------------------------------------------------
|
||||
/** True if this challenge is active at the given difficulty.
|
||||
@ -105,6 +107,13 @@ public:
|
||||
m_state[d] = CH_ACTIVE;
|
||||
} // setActive
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if this challenge is only an unlock list */
|
||||
bool isUnlockList();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if this challenge is a grand prix */
|
||||
bool isGrandPrix();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns a pointer to the actual Challenge data.
|
||||
*/
|
||||
const ChallengeData* getData() const { return m_data; }
|
||||
|
@ -30,12 +30,14 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
StoryModeStatus::StoryModeStatus(const XMLNode *node)
|
||||
{
|
||||
m_points = 0;
|
||||
m_first_time = true;
|
||||
m_easy_challenges = 0;
|
||||
m_medium_challenges = 0;
|
||||
m_hard_challenges = 0;
|
||||
m_current_challenge = NULL;
|
||||
m_points = 0;
|
||||
m_next_unlock_points = 0;
|
||||
m_first_time = true;
|
||||
m_easy_challenges = 0;
|
||||
m_medium_challenges = 0;
|
||||
m_hard_challenges = 0;
|
||||
m_best_challenges = 0;
|
||||
m_current_challenge = NULL;
|
||||
|
||||
// If there is saved data, load it
|
||||
if(node)
|
||||
@ -62,7 +64,7 @@ StoryModeStatus::~StoryModeStatus()
|
||||
*/
|
||||
void StoryModeStatus::addStatus(ChallengeStatus *cs)
|
||||
{
|
||||
m_challenges_state[cs->getData()->getId()] = cs;
|
||||
m_challenges_state[cs->getData()->getChallengeId()] = cs;
|
||||
} // addStatus
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -78,9 +80,11 @@ bool StoryModeStatus::isLocked(const std::string& feature)
|
||||
void StoryModeStatus::computeActive()
|
||||
{
|
||||
m_points = 0;
|
||||
m_next_unlock_points = 0;
|
||||
m_easy_challenges = 0;
|
||||
m_medium_challenges = 0;
|
||||
m_hard_challenges = 0;
|
||||
m_best_challenges = 0;
|
||||
|
||||
m_locked_features.clear(); // start afresh
|
||||
|
||||
@ -111,20 +115,32 @@ void StoryModeStatus::computeActive()
|
||||
unlockFeature(i->second, RaceManager::DIFFICULTY_HARD,
|
||||
/*save*/ false);
|
||||
}
|
||||
|
||||
if (i->second->isSolved(RaceManager::DIFFICULTY_HARD))
|
||||
if (i->second->isSolved(RaceManager::DIFFICULTY_BEST))
|
||||
{
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD];
|
||||
unlockFeature(i->second, RaceManager::DIFFICULTY_BEST,
|
||||
/*save*/ false);
|
||||
}
|
||||
|
||||
int gp_factor = i->second->isGrandPrix() ? GP_FACTOR : 1;
|
||||
|
||||
if (i->second->isSolved(RaceManager::DIFFICULTY_BEST) && !i->second->isUnlockList())
|
||||
{
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_BEST]*gp_factor;
|
||||
m_best_challenges++;
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_HARD) && !i->second->isUnlockList())
|
||||
{
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD]*gp_factor;
|
||||
m_hard_challenges++;
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_MEDIUM))
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_MEDIUM) && !i->second->isUnlockList())
|
||||
{
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_MEDIUM];
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_MEDIUM]*gp_factor;
|
||||
m_medium_challenges++;
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_EASY))
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_EASY) && !i->second->isUnlockList())
|
||||
{
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY];
|
||||
m_points += CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY]*gp_factor;
|
||||
m_easy_challenges++;
|
||||
}
|
||||
}
|
||||
@ -135,29 +151,40 @@ void StoryModeStatus::computeActive()
|
||||
lockFeature(i->second);
|
||||
}
|
||||
|
||||
if (i->second->isSolved(RaceManager::DIFFICULTY_HARD))
|
||||
if (i->second->isSolved(RaceManager::DIFFICULTY_BEST))
|
||||
{
|
||||
// challenge beaten at hardest, nothing more to do here
|
||||
continue;
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_HARD))
|
||||
{
|
||||
i->second->setActive(RaceManager::DIFFICULTY_BEST);
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_MEDIUM))
|
||||
{
|
||||
i->second->setActive(RaceManager::DIFFICULTY_BEST);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_HARD);
|
||||
}
|
||||
else if (i->second->isSolved(RaceManager::DIFFICULTY_EASY))
|
||||
{
|
||||
i->second->setActive(RaceManager::DIFFICULTY_BEST);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_HARD);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_MEDIUM);
|
||||
}
|
||||
else
|
||||
{
|
||||
i->second->setActive(RaceManager::DIFFICULTY_BEST);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_HARD);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_MEDIUM);
|
||||
i->second->setActive(RaceManager::DIFFICULTY_EASY);
|
||||
}
|
||||
} // for i
|
||||
|
||||
// now we have the number of points. Actually lock the tracks
|
||||
// now we have the number of points.
|
||||
|
||||
unlockFeatureByList();
|
||||
|
||||
//Actually lock the tracks
|
||||
for (i = m_challenges_state.begin(); i != m_challenges_state.end(); i++)
|
||||
{
|
||||
if (m_points < i->second->getData()->getNumTrophies())
|
||||
@ -173,12 +200,37 @@ void StoryModeStatus::computeActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearUnlocked();
|
||||
|
||||
|
||||
} // computeActive
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StoryModeStatus::unlockFeatureByList()
|
||||
{
|
||||
// test if we have unlocked a feature requiring a certain number of points
|
||||
std::map<std::string, ChallengeStatus*>::const_iterator i;
|
||||
for(i = m_challenges_state.begin();
|
||||
i != m_challenges_state.end(); i++)
|
||||
{
|
||||
if (i->second->isUnlockList())
|
||||
{
|
||||
if (i->second->isSolvedAtAnyDifficulty())
|
||||
continue;
|
||||
|
||||
bool newly_solved = unlock_manager->unlockByPoints(m_points,i->second);
|
||||
|
||||
// Add to list of recently unlocked features
|
||||
if(newly_solved)
|
||||
m_unlocked_features.push_back(i->second->getData());
|
||||
|
||||
//Retrieve the smallest number of points for the next unlockable
|
||||
if (i->second->getData()->getNumTrophies() > m_points && (m_next_unlock_points == 0
|
||||
|| i->second->getData()->getNumTrophies() < m_next_unlock_points) )
|
||||
m_next_unlock_points = i->second->getData()->getNumTrophies();
|
||||
}
|
||||
}
|
||||
} //unlockFeatureByList
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StoryModeStatus::lockFeature(ChallengeStatus *challenge_status)
|
||||
@ -216,8 +268,10 @@ void StoryModeStatus::unlockFeature(ChallengeStatus* c, RaceManager::Difficulty
|
||||
m_locked_features.erase(p);
|
||||
}
|
||||
|
||||
// Add to list of recently unlocked features
|
||||
m_unlocked_features.push_back(c->getData());
|
||||
// Add to list of recently unlocked features if the challenge is newly completed
|
||||
if (!c->isSolvedAtAnyDifficulty())
|
||||
m_unlocked_features.push_back(c->getData());
|
||||
|
||||
c->setSolved(d); // reset isActive flag
|
||||
|
||||
// Save the new unlock information
|
||||
@ -250,6 +304,10 @@ void StoryModeStatus::raceFinished()
|
||||
unlockFeature(const_cast<ChallengeStatus*>(m_current_challenge),
|
||||
race_manager->getDifficulty());
|
||||
} // if isActive && challenge solved
|
||||
|
||||
//This updates the number of points.
|
||||
//It then calls unlockFeatureByList which checks the specially unlocked features (by points, etc)
|
||||
computeActive();
|
||||
} // raceFinished
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -259,11 +317,33 @@ void StoryModeStatus::raceFinished()
|
||||
void StoryModeStatus::grandPrixFinished()
|
||||
{
|
||||
if(m_current_challenge &&
|
||||
m_current_challenge->isActive(race_manager->getDifficulty()) &&
|
||||
m_current_challenge->getData()->isGPFulfilled() )
|
||||
m_current_challenge->isActive(race_manager->getDifficulty()) )
|
||||
{
|
||||
unlockFeature(const_cast<ChallengeStatus*>(m_current_challenge),
|
||||
race_manager->getDifficulty());
|
||||
ChallengeData::GPLevel unlock_level = m_current_challenge->getData()->isGPFulfilled();
|
||||
|
||||
RaceManager::Difficulty difficulty = RaceManager::DIFFICULTY_EASY;
|
||||
|
||||
switch (unlock_level)
|
||||
{
|
||||
case ChallengeData::GP_NONE:
|
||||
race_manager->setCoinTarget(0);
|
||||
return; //No cup unlocked
|
||||
case ChallengeData::GP_EASY:
|
||||
difficulty = RaceManager::DIFFICULTY_EASY;
|
||||
break;
|
||||
case ChallengeData::GP_MEDIUM:
|
||||
difficulty = RaceManager::DIFFICULTY_MEDIUM;
|
||||
break;
|
||||
case ChallengeData::GP_HARD:
|
||||
difficulty = RaceManager::DIFFICULTY_HARD;
|
||||
break;
|
||||
case ChallengeData::GP_BEST:
|
||||
difficulty = RaceManager::DIFFICULTY_BEST;
|
||||
break;
|
||||
}
|
||||
|
||||
race_manager->setDifficulty(difficulty);
|
||||
unlockFeature(const_cast<ChallengeStatus*>(m_current_challenge), difficulty);
|
||||
} // if isActive && challenge solved
|
||||
|
||||
race_manager->setCoinTarget(0);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef GAME_SLOT_HPP
|
||||
#define GAME_SLOT_HPP
|
||||
|
||||
#include "challenges/challenge_data.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
|
||||
#include <irrString.h>
|
||||
@ -33,7 +34,8 @@ class ChallengeStatus;
|
||||
class UTFWriter;
|
||||
class XMLNode;
|
||||
|
||||
const int CHALLENGE_POINTS[] = { 8, 9, 10 };
|
||||
const int CHALLENGE_POINTS[] = { 6, 7, 8, 10 };
|
||||
const int GP_FACTOR = 3;
|
||||
|
||||
/** This class contains the progression through challenges for the story mode.
|
||||
* It maintains a list of all challenges in a mapping of challenge id to
|
||||
@ -60,6 +62,7 @@ private:
|
||||
const ChallengeStatus *m_current_challenge;
|
||||
|
||||
int m_points;
|
||||
int m_next_unlock_points;
|
||||
|
||||
/** Set to false after the initial stuff (intro, select kart, etc.) */
|
||||
bool m_first_time;
|
||||
@ -67,6 +70,7 @@ private:
|
||||
int m_easy_challenges;
|
||||
int m_medium_challenges;
|
||||
int m_hard_challenges;
|
||||
int m_best_challenges;
|
||||
|
||||
public:
|
||||
|
||||
@ -75,6 +79,7 @@ public:
|
||||
|
||||
void computeActive();
|
||||
bool isLocked (const std::string& feature);
|
||||
void unlockFeatureByList();
|
||||
void lockFeature (ChallengeStatus *challenge);
|
||||
void unlockFeature (ChallengeStatus* c, RaceManager::Difficulty d,
|
||||
bool do_save=true);
|
||||
@ -96,15 +101,21 @@ public:
|
||||
/** Returns the number of points accumulated. */
|
||||
int getPoints () const { return m_points; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of points needed by the next unlockable. 0 if none. */
|
||||
int getNextUnlockPoints () const { return m_next_unlock_points; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of fulfilled challenges at easy level. */
|
||||
int getNumEasyTrophies () const { return m_easy_challenges; }
|
||||
// ------------------------------------------------------------------------
|
||||
/* Returns the number of fulfilled challenges at medium level. */
|
||||
int getNumMediumTrophies() const { return m_medium_challenges; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of fulfilled challenges at har level. */
|
||||
/** Returns the number of fulfilled challenges at hard level. */
|
||||
int getNumHardTrophies () const { return m_hard_challenges; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the number of fulfilled challenges at best level. */
|
||||
int getNumBestTrophies () const { return m_best_challenges; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Sets if this is the first time the intro is shown. */
|
||||
void setFirstTime(bool ft) { m_first_time = ft; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "audio/sfx_manager.hpp"
|
||||
#include "challenges/challenge_data.hpp"
|
||||
#include "challenges/challenge_status.hpp"
|
||||
#include "challenges/story_mode_status.hpp"
|
||||
#include "config/player_manager.hpp"
|
||||
#include "config/player_profile.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
@ -142,19 +143,30 @@ void UnlockManager::addOrFreeChallenge(ChallengeData *c)
|
||||
{
|
||||
if(isSupportedVersion(*c))
|
||||
{
|
||||
m_all_challenges[c->getId()]=c;
|
||||
m_all_challenges[c->getChallengeId()]=c;
|
||||
if (c->isUnlockList())
|
||||
addListChallenge(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::warn("Challenge", "Challenge '%s' is not supported - ignored.",
|
||||
c->getId().c_str());
|
||||
c->getChallengeId().c_str());
|
||||
delete c;
|
||||
}
|
||||
} // addOrFreeChallenge
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Add a challenge to the unlock challenges list
|
||||
* \param c The challenge that is either stored or freed.
|
||||
*/
|
||||
void UnlockManager::addListChallenge(ChallengeData *c)
|
||||
{
|
||||
m_list_challenges[c->getChallengeId()]=c;
|
||||
} // addListChallenge
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Reads a challenge from the given filename. The challenge will then either
|
||||
* be stored, or (if the challenge version is not supported anymore
|
||||
* be stored, or (if the challenge version is not supported anymore, freed)
|
||||
* \param filename Name of the challenge file to read.
|
||||
*/
|
||||
void UnlockManager::addChallenge(const std::string& filename)
|
||||
@ -228,20 +240,25 @@ bool UnlockManager::isSupportedVersion(const ChallengeData &challenge)
|
||||
{
|
||||
// Test if challenge version number is in between minimum
|
||||
// and maximum supported version.
|
||||
return (challenge.getVersion()>=2 && challenge.getVersion()<=2);
|
||||
return (challenge.getVersion()>=3 && challenge.getVersion()<=3);
|
||||
} // isSupportedVersion
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** This functions finds what new tracks, GP and karts have been unlocked
|
||||
*/
|
||||
void UnlockManager::findWhatWasUnlocked(int points_before, int points_now,
|
||||
std::vector<std::string>& tracks,
|
||||
std::vector<std::string>& gps)
|
||||
std::vector<std::string>& gps,
|
||||
std::vector<std::string>& karts,
|
||||
std::vector<const ChallengeData*>& unlocked)
|
||||
{
|
||||
ChallengeData* c = NULL;
|
||||
|
||||
for (AllChallengesType::iterator it = m_all_challenges.begin();
|
||||
it != m_all_challenges.end(); it++)
|
||||
{
|
||||
ChallengeData* c = it->second;
|
||||
c = it->second;
|
||||
if (c->getNumTrophies() > points_before &&
|
||||
c->getNumTrophies() <= points_now )
|
||||
{
|
||||
@ -257,4 +274,36 @@ void UnlockManager::findWhatWasUnlocked(int points_before, int points_now,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int n = 0; n < unlocked.size(); n++)
|
||||
{
|
||||
std::vector<ChallengeData::UnlockableFeature> features = unlocked[n]->getFeatures();
|
||||
|
||||
for (unsigned int i = 0; i < features.size(); i++)
|
||||
{
|
||||
if( features[i].m_type == ChallengeData::UNLOCK_KART )
|
||||
karts.push_back(features[i].m_name);
|
||||
}
|
||||
}
|
||||
|
||||
//std::vector<const ChallengeData*>
|
||||
// getRecentlyCompletedChallenges()
|
||||
} // findWhatWasUnlocked
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This functions sets as completed the "challenges" requiring a certain number
|
||||
* of points, to unlock features.
|
||||
* Returns true if the challenge has been completed
|
||||
*/
|
||||
bool UnlockManager::unlockByPoints(int points, ChallengeStatus* unlock_list)
|
||||
{
|
||||
//TODO : add support for other conditions (achievements...) for alternative unlock paths
|
||||
if( unlock_list!=NULL && unlock_list->getData()->getNumTrophies() <= points)
|
||||
{
|
||||
unlock_list->setSolved(RaceManager::DIFFICULTY_BEST);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // unlockByPoints
|
||||
|
||||
/* EOF */
|
||||
|
@ -44,12 +44,16 @@ private:
|
||||
typedef std::map<std::string, ChallengeData*> AllChallengesType;
|
||||
AllChallengesType m_all_challenges;
|
||||
|
||||
/* The challenges who don't have a race, only unlockables */
|
||||
AllChallengesType m_list_challenges;
|
||||
|
||||
void readAllChallengesInDirs(const std::vector<std::string>* all_dirs);
|
||||
|
||||
public:
|
||||
UnlockManager ();
|
||||
~UnlockManager ();
|
||||
void addOrFreeChallenge(ChallengeData *c);
|
||||
void addListChallenge(ChallengeData *c);
|
||||
void addChallenge (const std::string& filename);
|
||||
|
||||
const ChallengeData *getChallengeData(const std::string& id);
|
||||
@ -61,7 +65,10 @@ public:
|
||||
|
||||
void findWhatWasUnlocked(int pointsBefore, int pointsNow,
|
||||
std::vector<std::string>& tracks,
|
||||
std::vector<std::string>& gps);
|
||||
std::vector<std::string>& gps,
|
||||
std::vector<std::string>& karts,
|
||||
std::vector<const ChallengeData*>& unlocked);
|
||||
bool unlockByPoints(int points, ChallengeStatus* unlock_list);
|
||||
|
||||
StoryModeStatus *createStoryModeStatus(const XMLNode *node=NULL);
|
||||
|
||||
|
@ -230,6 +230,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getPoints() const { return m_story_mode_status->getPoints(); }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getNextUnlockPoints() const { return m_story_mode_status->getNextUnlockPoints(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setFirstTime(bool b) { m_story_mode_status->setFirstTime(b); }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isFirstTime() const { return m_story_mode_status->isFirstTime(); }
|
||||
@ -263,7 +265,11 @@ public:
|
||||
unsigned int getNumHardTrophies() const
|
||||
{
|
||||
return m_story_mode_status->getNumHardTrophies();
|
||||
} // getNumHardTropies
|
||||
} // getNumHardTrophies
|
||||
unsigned int getNumBestTrophies() const
|
||||
{
|
||||
return m_story_mode_status->getNumBestTrophies();
|
||||
} // getNumBestTrophies
|
||||
// ------------------------------------------------------------------------
|
||||
AchievementsStatus* getAchievementsStatus()
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id,
|
||||
m_children.push_back(skill_bar);
|
||||
}
|
||||
|
||||
setValues(props);
|
||||
setValues(props, PLAYER_DIFFICULTY_NORMAL);
|
||||
|
||||
move(area.UpperLeftCorner.X, area.UpperLeftCorner.Y,
|
||||
area.getWidth(), area.getHeight());
|
||||
@ -88,7 +88,8 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id,
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void KartStatsWidget::setValues(const KartProperties* props)
|
||||
void KartStatsWidget::setValues(const KartProperties* props,
|
||||
PerPlayerDifficulty d)
|
||||
{
|
||||
// Use kart properties computed for "hard" difficulty to show the user, so
|
||||
// that properties don't change according to the the last used difficulty
|
||||
@ -97,7 +98,7 @@ void KartStatsWidget::setValues(const KartProperties* props)
|
||||
RaceManager::Difficulty previous_difficulty = race_manager->getDifficulty();
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD);
|
||||
KartProperties kp_computed;
|
||||
kp_computed.copyForPlayer(props);
|
||||
kp_computed.copyForPlayer(props, d);
|
||||
|
||||
// Scale the values so they look better
|
||||
// The scaling factor and offset were found by trial and error.
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "guiengine/widgets/skill_level_widget.hpp"
|
||||
|
||||
class KartProperties;
|
||||
enum PerPlayerDifficulty : uint8_t;
|
||||
|
||||
namespace GUIEngine
|
||||
{
|
||||
@ -94,7 +95,7 @@ namespace GUIEngine
|
||||
* inside itself */
|
||||
void setSize(const int x, const int y, const int w, const int h);
|
||||
|
||||
void setValues(const KartProperties* props);
|
||||
void setValues(const KartProperties* props, PerPlayerDifficulty d);
|
||||
|
||||
/** Change the value of the widget, it must be a percent. */
|
||||
void setValue(Stats type, int value);
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "karts/kart_model.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "network/network_player_profile.hpp"
|
||||
#include "states_screens/kart_selection.hpp"
|
||||
#include <IGUIEnvironment.h>
|
||||
|
||||
@ -36,7 +35,6 @@ using namespace GUIEngine;
|
||||
|
||||
PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent,
|
||||
StateManager::ActivePlayer* associated_player,
|
||||
NetworkPlayerProfile* associated_user,
|
||||
core::recti area, const int player_id,
|
||||
std::string kart_group,
|
||||
const int irrlicht_widget_id) : Widget(WTYPE_DIV)
|
||||
@ -49,14 +47,13 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent,
|
||||
m_ready_text = NULL;
|
||||
m_parent_screen = parent;
|
||||
|
||||
m_associated_user = associated_user;
|
||||
m_associated_player = associated_player;
|
||||
x_speed = 1.0f;
|
||||
y_speed = 1.0f;
|
||||
w_speed = 1.0f;
|
||||
h_speed = 1.0f;
|
||||
m_ready = false;
|
||||
m_handicapped = false;
|
||||
m_difficulty = PLAYER_DIFFICULTY_NORMAL;
|
||||
m_not_updated_yet = true;
|
||||
|
||||
m_irrlicht_widget_id = irrlicht_widget_id;
|
||||
@ -116,10 +113,6 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent,
|
||||
m_player_ident_spinner->setBadge(GAMEPAD_BADGE);
|
||||
}
|
||||
}
|
||||
else if (m_associated_user) // online user, FIXME is that useful ?
|
||||
{
|
||||
m_player_ident_spinner->setBadge(OK_BADGE);
|
||||
}
|
||||
|
||||
if (irrlicht_widget_id == -1)
|
||||
{
|
||||
@ -184,7 +177,7 @@ PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent,
|
||||
"kart '%s' nor any other kart.",
|
||||
default_kart.c_str());
|
||||
}
|
||||
m_kartInternalName = props->getIdent();
|
||||
m_kart_internal_name = props->getIdent();
|
||||
|
||||
const KartModel &kart_model = props->getMasterKartModel();
|
||||
|
||||
@ -374,8 +367,6 @@ void PlayerKartWidget::add()
|
||||
irr::core::stringw name; // name of the player
|
||||
if (m_associated_player)
|
||||
name = m_associated_player->getProfile()->getName();
|
||||
if (m_associated_user)
|
||||
name = m_associated_user->getName();
|
||||
core::stringw label = translations->fribidize(name);
|
||||
|
||||
if (m_parent_screen->m_multiplayer)
|
||||
@ -478,12 +469,12 @@ bool PlayerKartWidget::isReady()
|
||||
} // isReady
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** \return Whether this player is handicapped or not */
|
||||
bool PlayerKartWidget::isHandicapped()
|
||||
/** \return Per player difficulty */
|
||||
PerPlayerDifficulty PlayerKartWidget::getDifficulty()
|
||||
{
|
||||
assert(m_magic_number == 0x33445566);
|
||||
return m_handicapped;
|
||||
} // isHandicapped
|
||||
return m_difficulty;
|
||||
} // getDifficulty
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/** Updates the animation (moving/shrinking/etc.) */
|
||||
@ -636,13 +627,19 @@ GUIEngine::EventPropagation PlayerKartWidget::transmitEvent(Widget* w,
|
||||
m_associated_player->setPlayerProfile(profile);
|
||||
if(UserConfigParams::m_per_player_difficulty && spinner_value % 2 != 0)
|
||||
{
|
||||
m_handicapped = true;
|
||||
m_difficulty = PLAYER_DIFFICULTY_HANDICAP;
|
||||
m_model_view->setBadge(ANCHOR_BADGE);
|
||||
m_kart_stats->setValues(
|
||||
kart_properties_manager->getKart(m_kart_internal_name),
|
||||
PLAYER_DIFFICULTY_HANDICAP);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_handicapped = false;
|
||||
m_difficulty = PLAYER_DIFFICULTY_NORMAL;
|
||||
m_model_view->unsetBadge(ANCHOR_BADGE);
|
||||
m_kart_stats->setValues(
|
||||
kart_properties_manager->getKart(m_kart_internal_name),
|
||||
PLAYER_DIFFICULTY_NORMAL);
|
||||
}
|
||||
m_model_view->getModelViewRenderInfo()->setHue(
|
||||
m_associated_player->getConstProfile()->getDefaultKartColor());
|
||||
@ -728,7 +725,7 @@ void PlayerKartWidget::setSize(const int x, const int y, const int w, const int
|
||||
void PlayerKartWidget::setKartInternalName(const std::string& whichKart)
|
||||
{
|
||||
assert(m_magic_number == 0x33445566);
|
||||
m_kartInternalName = whichKart;
|
||||
m_kart_internal_name = whichKart;
|
||||
} // setKartInternalName
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -736,7 +733,7 @@ void PlayerKartWidget::setKartInternalName(const std::string& whichKart)
|
||||
const std::string& PlayerKartWidget::getKartInternalName() const
|
||||
{
|
||||
assert(m_magic_number == 0x33445566);
|
||||
return m_kartInternalName;
|
||||
return m_kart_internal_name;
|
||||
} // getKartInternalName
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -748,3 +745,15 @@ EventPropagation PlayerKartWidget::onSpinnerConfirmed()
|
||||
return EVENT_BLOCK;
|
||||
} // onSpinnerConfirmed
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
void PlayerKartWidget::enableHandicapForNetwork()
|
||||
{
|
||||
m_difficulty = PLAYER_DIFFICULTY_HANDICAP;
|
||||
m_model_view->setBadge(ANCHOR_BADGE);
|
||||
m_kart_stats->setValues(
|
||||
kart_properties_manager->getKart(m_kart_internal_name),
|
||||
PLAYER_DIFFICULTY_HANDICAP);
|
||||
core::stringw label = _("%s (handicapped)",
|
||||
m_player_ident_spinner->getCustomText());
|
||||
m_player_ident_spinner->setCustomText(label);
|
||||
} // enableHandicapForNetwork
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
|
||||
class KartSelectionScreen;
|
||||
class NetworkPlayerProfile;
|
||||
|
||||
namespace GUIEngine
|
||||
{
|
||||
@ -44,7 +43,7 @@ namespace GUIEngine
|
||||
/** Whether this player confirmed their selection */
|
||||
bool m_ready;
|
||||
/** If the player is handicapped. */
|
||||
bool m_handicapped;
|
||||
PerPlayerDifficulty m_difficulty;
|
||||
|
||||
/** widget coordinates */
|
||||
int player_name_x, player_name_y, player_name_w, player_name_h;
|
||||
@ -65,9 +64,6 @@ namespace GUIEngine
|
||||
StateManager::ActivePlayer* m_associated_player;
|
||||
int m_player_id;
|
||||
|
||||
/** Network info about the user. */
|
||||
NetworkPlayerProfile* m_associated_user;
|
||||
|
||||
/** Internal name of the spinner; useful to interpret spinner events,
|
||||
* which contain the name of the activated object */
|
||||
std::string spinnerID;
|
||||
@ -91,13 +87,12 @@ namespace GUIEngine
|
||||
irr::gui::IGUIStaticText* m_ready_text;
|
||||
|
||||
core::stringw deviceName;
|
||||
std::string m_kartInternalName;
|
||||
std::string m_kart_internal_name;
|
||||
|
||||
bool m_not_updated_yet;
|
||||
|
||||
PlayerKartWidget(KartSelectionScreen* parent,
|
||||
StateManager::ActivePlayer* associated_player,
|
||||
NetworkPlayerProfile* associated_user,
|
||||
core::recti area, const int player_id,
|
||||
std::string kart_group,
|
||||
const int irrlicht_idget_id=-1);
|
||||
@ -137,8 +132,8 @@ namespace GUIEngine
|
||||
bool isReady();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** \return Whether this player is handicapped or not */
|
||||
bool isHandicapped();
|
||||
/** \return Per player difficulty */
|
||||
PerPlayerDifficulty getDifficulty();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/** Updates the animation (moving/shrinking/etc.) */
|
||||
@ -169,6 +164,8 @@ namespace GUIEngine
|
||||
|
||||
/** \brief Event callback from ISpinnerConfirmListener */
|
||||
virtual GUIEngine::EventPropagation onSpinnerConfirmed();
|
||||
// -------------------------------------------------------------------------
|
||||
void enableHandicapForNetwork();
|
||||
}; // PlayerKartWidget
|
||||
}
|
||||
|
||||
|
@ -328,7 +328,7 @@ void SpinnerWidget::addLabel(stringw label)
|
||||
void SpinnerWidget::setValue(const int new_value)
|
||||
{
|
||||
m_value = new_value;
|
||||
m_customText = "";
|
||||
m_custom_text = "";
|
||||
|
||||
if (m_graphical)
|
||||
{
|
||||
@ -402,30 +402,30 @@ void SpinnerWidget::setActive(bool active)
|
||||
if (active)
|
||||
{
|
||||
setText(L"");
|
||||
if (m_customText.empty())
|
||||
if (m_custom_text.empty())
|
||||
{
|
||||
setValue(getValue()); // Update the display
|
||||
}
|
||||
else
|
||||
{
|
||||
setCustomText(m_customText);
|
||||
setCustomText(m_custom_text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save it temporary because setValue(which is uses for update in
|
||||
// this case) overwrites it
|
||||
core::stringw customText = m_customText;
|
||||
core::stringw custom_text = m_custom_text;
|
||||
setText(L"-");
|
||||
setValue(getValue()); // Update the display
|
||||
m_customText = customText;
|
||||
m_custom_text = custom_text;
|
||||
}
|
||||
} // setActive
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void SpinnerWidget::setCustomText(const core::stringw& text)
|
||||
{
|
||||
m_customText = text;
|
||||
m_custom_text = text;
|
||||
if (m_children.size() > 0)
|
||||
{
|
||||
m_children[1].m_element->setText(text.c_str());
|
||||
|
@ -79,7 +79,7 @@ namespace GUIEngine
|
||||
/** \brief Keeps track of the custom text in spinner (a text which isn't related to a value)
|
||||
* to remember it and set it back (example : when we deactivate the widget)
|
||||
*/
|
||||
core::stringw m_customText;
|
||||
core::stringw m_custom_text;
|
||||
|
||||
/** \brief implementing method from base class Widget */
|
||||
virtual EventPropagation transmitEvent(Widget* w,
|
||||
@ -197,6 +197,7 @@ namespace GUIEngine
|
||||
|
||||
/** Display custom text in spinner */
|
||||
void setCustomText(const core::stringw& text);
|
||||
const core::stringw& getCustomText() const { return m_custom_text; }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ AbstractKart::AbstractKart(const std::string& ident,
|
||||
ident.c_str());
|
||||
kp = kart_properties_manager->getKart(std::string("tux"));
|
||||
}
|
||||
m_kart_properties->copyForPlayer(kp);
|
||||
m_kart_properties->copyForPlayer(kp, difficulty);
|
||||
m_difficulty = difficulty;
|
||||
m_kart_animation = NULL;
|
||||
assert(m_kart_properties);
|
||||
|
@ -55,19 +55,19 @@
|
||||
* \param init_pos The start coordinates and heading of the kart.
|
||||
*/
|
||||
LocalPlayerController::LocalPlayerController(AbstractKart *kart,
|
||||
const int local_playerID)
|
||||
const int local_player_id,
|
||||
PerPlayerDifficulty d)
|
||||
: PlayerController(kart), m_sky_particles_emitter(NULL)
|
||||
{
|
||||
|
||||
m_player = StateManager::get()->getActivePlayer(local_playerID);
|
||||
m_difficulty = d;
|
||||
m_player = StateManager::get()->getActivePlayer(local_player_id);
|
||||
if(m_player)
|
||||
m_player->setKart(kart);
|
||||
|
||||
// Keep a pointer to the camera to remove the need to search for
|
||||
// the right camera once per frame later.
|
||||
|
||||
Camera *camera = Camera::createCamera(kart, local_playerID);
|
||||
|
||||
Camera *camera = Camera::createCamera(kart, local_player_id);
|
||||
|
||||
m_camera_index = camera->getIndex();
|
||||
m_wee_sound = SFXManager::get()->createSoundSource("wee");
|
||||
m_bzzt_sound = SFXManager::get()->getBuffer("bzzt");
|
||||
@ -390,5 +390,10 @@ core::stringw LocalPlayerController::getName() const
|
||||
{
|
||||
if (NetworkConfig::get()->isNetworking())
|
||||
return PlayerController::getName();
|
||||
return m_player->getProfile()->getName();
|
||||
|
||||
core::stringw name = m_player->getProfile()->getName();
|
||||
if (m_difficulty == PLAYER_DIFFICULTY_HANDICAP)
|
||||
name = _("%s (handicapped)", name);
|
||||
|
||||
return name;
|
||||
} // getName
|
||||
|
@ -48,6 +48,8 @@ private:
|
||||
* camera object is managed in the Camera class, so no need to free it. */
|
||||
int m_camera_index;
|
||||
|
||||
PerPlayerDifficulty m_difficulty;
|
||||
|
||||
SFXBase *m_wee_sound;
|
||||
SFXBuffer *m_bzzt_sound;
|
||||
SFXBuffer *m_ugh_sound;
|
||||
@ -58,7 +60,8 @@ private:
|
||||
virtual void displayPenaltyWarning() OVERRIDE;
|
||||
public:
|
||||
LocalPlayerController(AbstractKart *kart,
|
||||
const int local_playerID);
|
||||
const int local_player_id,
|
||||
PerPlayerDifficulty d);
|
||||
~LocalPlayerController();
|
||||
void update (int ticks) OVERRIDE;
|
||||
bool action (PlayerAction action, int value,
|
||||
|
@ -393,12 +393,17 @@ void PlayerController::rewindTo(BareNetworkString *buffer)
|
||||
// ----------------------------------------------------------------------------
|
||||
core::stringw PlayerController::getName() const
|
||||
{
|
||||
core::stringw name = m_kart->getName();
|
||||
if (NetworkConfig::get()->isNetworking())
|
||||
{
|
||||
auto& players = LobbyProtocol::get<LobbyProtocol>()->getGameSetup()
|
||||
->getPlayers();
|
||||
if (auto player = players.at(m_kart->getWorldKartId()).lock())
|
||||
return player->getName();
|
||||
{
|
||||
name = player->getName();
|
||||
if (player->getPerPlayerDifficulty() == PLAYER_DIFFICULTY_HANDICAP)
|
||||
name = _("%s (handicapped)", name);
|
||||
}
|
||||
}
|
||||
return m_kart->getName();
|
||||
return name;
|
||||
} // getName
|
||||
|
@ -1361,7 +1361,7 @@ void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_nod
|
||||
void SkiddingAI::handleBubblegum(int item_skill, const std::vector<const Item *> &items_to_collect,
|
||||
const std::vector<const Item *> &items_to_avoid)
|
||||
{
|
||||
int shield_radius = m_ai_properties->m_shield_incoming_radius;
|
||||
float shield_radius = m_ai_properties->m_shield_incoming_radius;
|
||||
|
||||
int projectile_types[4]; //[3] basket, [2] cakes, [1] plunger, [0] bowling
|
||||
projectile_types[0] = projectile_manager->getNearbyProjectileCount(m_kart, shield_radius, PowerupManager::POWERUP_BOWLING);
|
||||
@ -1446,7 +1446,7 @@ void SkiddingAI::handleBubblegum(int item_skill, const std::vector<const Item *>
|
||||
}
|
||||
|
||||
//If the kart view is blocked by a plunger, use the shield
|
||||
if(m_kart->getBlockedByPlungerTime()>0)
|
||||
if(m_kart->getBlockedByPlungerTicks()>0)
|
||||
{
|
||||
m_controls->setFire(true);
|
||||
m_controls->setLookBack(false);
|
||||
|
@ -372,7 +372,7 @@ void Kart::reset()
|
||||
m_speed = 0.0f;
|
||||
m_smoothed_speed = 0.0f;
|
||||
m_current_lean = 0.0f;
|
||||
m_falling_ticks = 0;
|
||||
m_falling_time = 0.0f;
|
||||
m_view_blocked_by_plunger = 0;
|
||||
m_has_caught_nolok_bubblegum = false;
|
||||
m_is_jumping = false;
|
||||
@ -1550,7 +1550,6 @@ void Kart::update(int ticks)
|
||||
}
|
||||
|
||||
PROFILER_PUSH_CPU_MARKER("Kart::Update (material)", 0x60, 0x34, 0x7F);
|
||||
handleMaterialGFX(ticks);
|
||||
const Material* material=m_terrain_info->getMaterial();
|
||||
if (!material) // kart falling off the track
|
||||
{
|
||||
@ -1861,9 +1860,11 @@ void Kart::handleMaterialSFX(const Material *material)
|
||||
* you are driving on, or the effect from a surface the kart is
|
||||
* (partially) under. The surface effect is triggered, if either the
|
||||
* kart is falling, or if the surface the kart is driving on has
|
||||
* the 'isBelowSurface' property set.
|
||||
* the 'isBelowSurface' property set. This function is called once
|
||||
* per rendered frame from updateGraphics().
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void Kart::handleMaterialGFX(int ticks)
|
||||
void Kart::handleMaterialGFX(float dt)
|
||||
{
|
||||
const Material *material = getMaterial();
|
||||
|
||||
@ -1900,19 +1901,19 @@ void Kart::handleMaterialGFX(int ticks)
|
||||
bool falling = material && material->hasFallingEffect() && !m_flying;
|
||||
if (falling)
|
||||
{
|
||||
m_falling_ticks -= ticks;
|
||||
if (m_falling_ticks < 0)
|
||||
m_falling_ticks = 0;
|
||||
m_falling_time -= dt;
|
||||
if (m_falling_time < 0)
|
||||
m_falling_time = 0;
|
||||
}
|
||||
else
|
||||
m_falling_ticks = stk_config->time2Ticks(0.35f);
|
||||
m_falling_time = 0.35f;
|
||||
|
||||
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
|
||||
{
|
||||
Camera *camera = Camera::getCamera(i);
|
||||
if(camera->getKart()!=this) continue;
|
||||
|
||||
if (falling && m_falling_ticks <= 0)
|
||||
if (falling && m_falling_time <= 0)
|
||||
{
|
||||
camera->setMode(Camera::CM_FALLING);
|
||||
}
|
||||
@ -2456,8 +2457,6 @@ void Kart::updatePhysics(int ticks)
|
||||
m_max_speed->setMinSpeed(min_speed);
|
||||
m_max_speed->update(ticks);
|
||||
|
||||
|
||||
updateEngineSFX(stk_config->ticks2Time(ticks));
|
||||
#ifdef XX
|
||||
Log::info("Kart","angVel %f %f %f heading %f suspension %f %f %f %f"
|
||||
,m_body->getAngularVelocity().getX()
|
||||
@ -2474,14 +2473,15 @@ void Kart::updatePhysics(int ticks)
|
||||
} // updatephysics
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Adjust the engine sound effect depending on the speed of the kart.
|
||||
/** Adjust the engine sound effect depending on the speed of the kart. This
|
||||
* is called during updateGraphics, i.e. once per rendered frame only.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void Kart::updateEngineSFX(float dt)
|
||||
{
|
||||
// Only update SFX during the last substep (otherwise too many SFX commands
|
||||
// in one frame), and if sfx are enabled
|
||||
if(!m_engine_sound || !SFXManager::get()->sfxAllowed() ||
|
||||
!main_loop->isLastSubstep() )
|
||||
if(!m_engine_sound || !SFXManager::get()->sfxAllowed() )
|
||||
return;
|
||||
|
||||
// when going faster, use higher pitch for engine
|
||||
@ -3039,32 +3039,8 @@ void Kart::updateGraphics(float dt)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XX
|
||||
// cheap wheelie effect
|
||||
if (m_controls.getNitro())
|
||||
{
|
||||
m_node->updateAbsolutePosition();
|
||||
m_kart_model->getWheelNodes()[0]->updateAbsolutePosition();
|
||||
float wheel_y = m_kart_model->getWheelNodes()[0]->getAbsolutePosition().Y;
|
||||
|
||||
core::vector3df rot = m_node->getRotation();
|
||||
|
||||
float ratio = 0.8f; //float(m_zipper_fire->getCreationRate())
|
||||
// /float(m_zipper_fire->getParticlesInfo()->getMaxRate());
|
||||
|
||||
const float a = (13.4f - ratio*13.0f);
|
||||
float dst = -45.0f*sin((a*a)/180.f*M_PI);
|
||||
|
||||
rot.X = dst;
|
||||
m_node->setRotation(rot);
|
||||
|
||||
m_node->updateAbsolutePosition();
|
||||
m_kart_model->getWheelNodes()[0]->updateAbsolutePosition();
|
||||
float wheel_y_after = m_kart_model->getWheelNodes()[0]->getAbsolutePosition().Y;
|
||||
|
||||
m_node->setPosition(m_node->getPosition() + core::vector3df(0,wheel_y_after - wheel_y,0));
|
||||
}
|
||||
#endif
|
||||
handleMaterialGFX(dt);
|
||||
updateEngineSFX(dt);
|
||||
|
||||
} // updateGraphics
|
||||
|
||||
|
@ -207,7 +207,7 @@ protected:
|
||||
float m_finish_time;
|
||||
bool m_finished_race;
|
||||
|
||||
int m_falling_ticks;
|
||||
float m_falling_time;
|
||||
|
||||
/** When a kart has its view blocked by the plunger, this variable will be
|
||||
* > 0 the number it contains is the time left before removing plunger. */
|
||||
@ -245,7 +245,7 @@ protected:
|
||||
|
||||
void updatePhysics(int ticks);
|
||||
void handleMaterialSFX(const Material *material);
|
||||
void handleMaterialGFX(int ticks);
|
||||
void handleMaterialGFX(float dt);
|
||||
void updateFlying();
|
||||
void updateSliding();
|
||||
void updateEnginePowerAndBrakes(int ticks);
|
||||
|
@ -132,7 +132,8 @@ KartProperties::~KartProperties()
|
||||
* \param source The source kart properties from which to copy this objects'
|
||||
* values.
|
||||
*/
|
||||
void KartProperties::copyForPlayer(const KartProperties *source)
|
||||
void KartProperties::copyForPlayer(const KartProperties *source,
|
||||
PerPlayerDifficulty d)
|
||||
{
|
||||
*this = *source;
|
||||
|
||||
@ -146,7 +147,7 @@ void KartProperties::copyForPlayer(const KartProperties *source)
|
||||
|
||||
// Combine the characteristics for this object. We can't copy it because
|
||||
// this object has other pointers (to m_characteristic).
|
||||
combineCharacteristics();
|
||||
combineCharacteristics(d);
|
||||
}
|
||||
} // copyForPlayer
|
||||
|
||||
@ -224,7 +225,7 @@ void KartProperties::load(const std::string &filename, const std::string &node)
|
||||
}
|
||||
getAllData(root);
|
||||
m_characteristic = std::make_shared<XmlCharacteristic>(root);
|
||||
combineCharacteristics();
|
||||
combineCharacteristics(PLAYER_DIFFICULTY_NORMAL);
|
||||
}
|
||||
catch(std::exception& err)
|
||||
{
|
||||
@ -343,7 +344,7 @@ void KartProperties::setHatMeshName(const std::string &hat_name)
|
||||
} // setHatMeshName
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void KartProperties::combineCharacteristics()
|
||||
void KartProperties::combineCharacteristics(PerPlayerDifficulty difficulty)
|
||||
{
|
||||
m_combined_characteristic = std::make_shared<CombinedCharacteristic>();
|
||||
m_combined_characteristic->addCharacteristic(kart_properties_manager->
|
||||
@ -362,6 +363,9 @@ void KartProperties::combineCharacteristics()
|
||||
// Kart type found
|
||||
m_combined_characteristic->addCharacteristic(characteristic);
|
||||
|
||||
m_combined_characteristic->addCharacteristic(kart_properties_manager->
|
||||
getPlayerCharacteristic(getPerPlayerDifficultyAsString(difficulty)));
|
||||
|
||||
m_combined_characteristic->addCharacteristic(m_characteristic.get());
|
||||
m_cached_characteristic = std::make_shared<CachedCharacteristic>
|
||||
(m_combined_characteristic.get());
|
||||
|
@ -207,7 +207,7 @@ private:
|
||||
|
||||
void load (const std::string &filename,
|
||||
const std::string &node);
|
||||
void combineCharacteristics();
|
||||
void combineCharacteristics(PerPlayerDifficulty d);
|
||||
|
||||
public:
|
||||
/** Returns the string representation of a per-player difficulty. */
|
||||
@ -215,7 +215,8 @@ public:
|
||||
|
||||
KartProperties (const std::string &filename="");
|
||||
~KartProperties ();
|
||||
void copyForPlayer (const KartProperties *source);
|
||||
void copyForPlayer (const KartProperties *source,
|
||||
PerPlayerDifficulty d = PLAYER_DIFFICULTY_NORMAL);
|
||||
void copyFrom (const KartProperties *source);
|
||||
void getAllData (const XMLNode * root);
|
||||
void checkAllSet (const std::string &filename);
|
||||
|
@ -1094,7 +1094,7 @@ int handleCmdLine()
|
||||
!server_password.empty());
|
||||
NetworkConfig::get()->addNetworkPlayer(
|
||||
input_manager->getDeviceManager()->getLatestUsedDevice(),
|
||||
PlayerManager::getCurrentPlayer(), false/*handicap*/);
|
||||
PlayerManager::getCurrentPlayer(), PLAYER_DIFFICULTY_NORMAL);
|
||||
NetworkConfig::get()->doneAddingNetworkPlayers();
|
||||
STKHost::create();
|
||||
auto cts = std::make_shared<ConnectToServer>(server);
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/protocols/lobby_protocol.hpp"
|
||||
#include "network/protocol_manager.hpp"
|
||||
#include "network/race_event_manager.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
@ -55,6 +54,7 @@ MainLoop::MainLoop(unsigned parent_pid)
|
||||
m_prev_time = 0;
|
||||
m_throttle_fps = true;
|
||||
m_is_last_substep = false;
|
||||
m_frame_before_loading_world = false;
|
||||
#ifdef WIN32
|
||||
if (parent_pid != 0)
|
||||
{
|
||||
@ -302,23 +302,6 @@ void MainLoop::run()
|
||||
|
||||
while (!m_abort)
|
||||
{
|
||||
bool loading_shutdown = false;
|
||||
auto lb = LobbyProtocol::get<LobbyProtocol>();
|
||||
if (World::getWorld() && lb &&
|
||||
STKHost::existHost() && !STKHost::get()->requestedShutdown())
|
||||
{
|
||||
while (!lb->allPlayersReady())
|
||||
{
|
||||
if (STKHost::existHost() && STKHost::get()->requestedShutdown())
|
||||
{
|
||||
loading_shutdown = true;
|
||||
break;
|
||||
}
|
||||
StkTime::sleep(1);
|
||||
m_curr_time = irr_driver->getDevice()->getTimer()->getRealTime();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (parent != 0 && parent != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -349,8 +332,7 @@ void MainLoop::run()
|
||||
|
||||
// Shutdown next frame if shutdown request is sent while loading the
|
||||
// world
|
||||
if (!loading_shutdown && STKHost::existHost() &&
|
||||
STKHost::get()->requestedShutdown())
|
||||
if (STKHost::existHost() && STKHost::get()->requestedShutdown())
|
||||
{
|
||||
SFXManager::get()->quickSound("anvil");
|
||||
core::stringw msg = _("Server connection timed out.");
|
||||
@ -428,7 +410,21 @@ void MainLoop::run()
|
||||
}
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
if (World::getWorld()) World::getWorld()->updateTime(1);
|
||||
if (m_frame_before_loading_world)
|
||||
{
|
||||
m_frame_before_loading_world = false;
|
||||
break;
|
||||
}
|
||||
if (World::getWorld())
|
||||
{
|
||||
if (World::getWorld()->getPhase() == WorldStatus::SETUP_PHASE)
|
||||
{
|
||||
// Skip the large num steps contributed by loading time
|
||||
World::getWorld()->updateTime(1);
|
||||
break;
|
||||
}
|
||||
World::getWorld()->updateTime(1);
|
||||
}
|
||||
} // for i < num_steps
|
||||
|
||||
m_is_last_substep = false;
|
||||
|
@ -34,6 +34,7 @@ private:
|
||||
/** True if the frame rate should be throttled. */
|
||||
bool m_throttle_fps;
|
||||
|
||||
bool m_frame_before_loading_world;
|
||||
/** True during the last substep of the inner main loop (where world
|
||||
* is updated). Used to reduce amount of updates (e.g. sfx positions
|
||||
* etc). */
|
||||
@ -56,6 +57,8 @@ public:
|
||||
/** Returns if this is the last substep. Used to reduce the amount
|
||||
* of updates (e.g. to sfx position) to once per rendered frame. */
|
||||
bool isLastSubstep() const { return m_is_last_substep; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setFrameBeforeLoadingWorld() { m_frame_before_loading_world = true; }
|
||||
}; // MainLoop
|
||||
|
||||
extern MainLoop* main_loop;
|
||||
|
@ -422,11 +422,14 @@ void CutsceneWorld::enterRaceOverState()
|
||||
// un-set the GP mode so that after unlocking, it doesn't try to continue the GP
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
|
||||
//TODO : this code largely duplicate a similar code present in raceResultGUI.
|
||||
// Try to reduce duplication
|
||||
std::vector<const ChallengeData*> unlocked =
|
||||
PlayerManager::getCurrentPlayer()->getRecentlyCompletedChallenges();
|
||||
|
||||
if (unlocked.size() > 0)
|
||||
{
|
||||
//PlayerManager::getCurrentPlayer()->clearUnlocked();
|
||||
PlayerManager::getCurrentPlayer()->clearUnlocked();
|
||||
|
||||
StateManager::get()->enterGameState();
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
|
||||
@ -441,8 +444,8 @@ void CutsceneWorld::enterRaceOverState()
|
||||
((CutsceneWorld*)World::getWorld())->setParts(parts);
|
||||
|
||||
assert(unlocked.size() > 0);
|
||||
scene->addTrophy(race_manager->getDifficulty());
|
||||
scene->findWhatWasUnlocked(race_manager->getDifficulty());
|
||||
scene->addTrophy(race_manager->getDifficulty(),true);
|
||||
scene->findWhatWasUnlocked(race_manager->getDifficulty(),unlocked);
|
||||
|
||||
StateManager::get()->replaceTopMostScreen(scene, GUIEngine::INGAME_MENU);
|
||||
}
|
||||
@ -476,9 +479,10 @@ void CutsceneWorld::enterRaceOverState()
|
||||
|
||||
std::vector<const ChallengeData*> unlocked =
|
||||
PlayerManager::getCurrentPlayer()->getRecentlyCompletedChallenges();
|
||||
|
||||
if (unlocked.size() > 0)
|
||||
{
|
||||
//PlayerManager::getCurrentPlayer()->clearUnlocked();
|
||||
PlayerManager::getCurrentPlayer()->clearUnlocked();
|
||||
|
||||
StateManager::get()->enterGameState();
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
|
||||
@ -492,8 +496,8 @@ void CutsceneWorld::enterRaceOverState()
|
||||
parts.push_back("featunlocked");
|
||||
((CutsceneWorld*)World::getWorld())->setParts(parts);
|
||||
|
||||
scene->addTrophy(race_manager->getDifficulty());
|
||||
scene->findWhatWasUnlocked(race_manager->getDifficulty());
|
||||
scene->addTrophy(race_manager->getDifficulty(),true);
|
||||
scene->findWhatWasUnlocked(race_manager->getDifficulty(),unlocked);
|
||||
|
||||
StateManager::get()->replaceTopMostScreen(scene, GUIEngine::INGAME_MENU);
|
||||
}
|
||||
@ -594,4 +598,3 @@ void CutsceneWorld::createRaceGUI()
|
||||
m_race_gui = new CutsceneGUI();
|
||||
} // createRaceGUI
|
||||
|
||||
|
||||
|
@ -65,7 +65,15 @@ void OverWorld::enterOverWorld()
|
||||
race_manager->setMinorMode (RaceManager::MINOR_MODE_OVERWORLD);
|
||||
race_manager->setNumKarts( 1 );
|
||||
race_manager->setTrack( "overworld" );
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD);
|
||||
|
||||
if (PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
{
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD);
|
||||
}
|
||||
else
|
||||
{
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_BEST);
|
||||
}
|
||||
|
||||
// Use keyboard 0 by default (FIXME: let player choose?)
|
||||
InputDevice* device = input_manager->getDeviceManager()->getKeyboard(0);
|
||||
@ -255,7 +263,7 @@ void OverWorld::onFirePressed(Controller* who)
|
||||
if (unlocked)
|
||||
{
|
||||
race_manager->setKartLastPositionOnOverworld(kart_xyz);
|
||||
new SelectChallengeDialog(0.8f, 0.8f,
|
||||
new SelectChallengeDialog(0.9f, 0.9f,
|
||||
challenges[n].m_challenge_id);
|
||||
}
|
||||
}
|
||||
|
@ -421,8 +421,8 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index,
|
||||
switch(kart_type)
|
||||
{
|
||||
case RaceManager::KT_PLAYER:
|
||||
controller = new LocalPlayerController(new_kart,
|
||||
local_player_id);
|
||||
controller = new LocalPlayerController(new_kart, local_player_id,
|
||||
difficulty);
|
||||
m_num_players ++;
|
||||
break;
|
||||
case RaceManager::KT_NETWORK_PLAYER:
|
||||
|
@ -367,7 +367,8 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index,
|
||||
{
|
||||
case RaceManager::KT_PLAYER:
|
||||
{
|
||||
controller = new LocalPlayerController(new_kart, local_player_id);
|
||||
controller = new LocalPlayerController(new_kart, local_player_id,
|
||||
difficulty);
|
||||
const PlayerProfile* p = StateManager::get()
|
||||
->getActivePlayer(local_player_id)->getConstProfile();
|
||||
if (p && p->getDefaultKartColor() > 0.0f)
|
||||
|
@ -38,6 +38,7 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
WorldStatus::WorldStatus()
|
||||
{
|
||||
main_loop->setFrameBeforeLoadingWorld();
|
||||
m_clock_mode = CLOCK_CHRONO;
|
||||
|
||||
m_prestart_sound = SFXManager::get()->createSoundSource("pre_start_race");
|
||||
@ -236,7 +237,7 @@ void WorldStatus::updateTime(int ticks)
|
||||
if (!UserConfigParams::m_sfx &&
|
||||
m_auxiliary_ticks < stk_config->time2Ticks(3.0f))
|
||||
return;
|
||||
|
||||
|
||||
if (!m_play_track_intro_sound)
|
||||
{
|
||||
startEngines();
|
||||
@ -246,24 +247,42 @@ void WorldStatus::updateTime(int ticks)
|
||||
|
||||
m_auxiliary_ticks = 0;
|
||||
|
||||
if (m_play_ready_set_go_sounds)
|
||||
m_prestart_sound->play();
|
||||
|
||||
// In a networked game the client needs to wait for a notification
|
||||
// from the server that all clients and the server are ready to
|
||||
// start the game. The server will actually wait for all clients
|
||||
// to confirm that they have started the race before starting
|
||||
// itself. In a normal race, this phase is skipped and the race
|
||||
// starts immediately.
|
||||
m_phase = NetworkConfig::get()->isNetworking() ? WAIT_FOR_SERVER_PHASE
|
||||
: READY_PHASE;
|
||||
if (NetworkConfig::get()->isNetworking())
|
||||
{
|
||||
m_phase = WAIT_FOR_SERVER_PHASE;
|
||||
// In networked races, inform the start game protocol that
|
||||
// the world has been setup
|
||||
auto lobby = LobbyProtocol::get<LobbyProtocol>();
|
||||
assert(lobby);
|
||||
lobby->finishedLoadingWorld();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_play_ready_set_go_sounds)
|
||||
m_prestart_sound->play();
|
||||
m_phase = READY_PHASE;
|
||||
}
|
||||
return; // Don't increase time
|
||||
case WAIT_FOR_SERVER_PHASE:
|
||||
{
|
||||
// Wait for all players to finish loading world
|
||||
auto lobby = LobbyProtocol::get<LobbyProtocol>();
|
||||
assert(lobby);
|
||||
if (!lobby->allPlayersReady())
|
||||
return;
|
||||
// This stage is only reached in case of a networked game.
|
||||
// The server waits for a confirmation from
|
||||
// each client that they have started (to guarantee that the
|
||||
// server is running with a local time behind all clients).
|
||||
if (m_play_ready_set_go_sounds)
|
||||
m_prestart_sound->play();
|
||||
|
||||
if (NetworkConfig::get()->isServer() &&
|
||||
m_server_is_ready.load() == false) return;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
#include "utils/time.hpp"
|
||||
@ -27,12 +28,16 @@
|
||||
#include <string.h>
|
||||
#if defined(WIN32)
|
||||
# include "ws2tcpip.h"
|
||||
# include <iphlpapi.h>
|
||||
# define inet_ntop InetNtop
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
# include <errno.h>
|
||||
# include <ifaddrs.h>
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
class BareNetworkString;
|
||||
class NetworkString;
|
||||
@ -68,6 +69,7 @@ public:
|
||||
TransportAddress* sender, int max_tries = -1);
|
||||
void broadcastPacket(NetworkString *data,
|
||||
bool reliable = true);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns a pointer to the ENet host object. */
|
||||
ENetHost* getENetHost() { return m_host; }
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "network/network_config.hpp"
|
||||
#include "config/stk_config.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
#include "online/xml_request.hpp"
|
||||
#include "states_screens/main_menu_screen.hpp"
|
||||
#include "states_screens/networking_lobby.hpp"
|
||||
@ -78,7 +79,7 @@ void NetworkConfig::setIsServer(bool b)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void NetworkConfig::setServerMode(RaceManager::MinorRaceModeType minor,
|
||||
RaceManager::MajorRaceModeType major)
|
||||
RaceManager::MajorRaceModeType major)
|
||||
{
|
||||
if (major == RaceManager::MAJOR_MODE_GRAND_PRIX)
|
||||
{
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "network/transport_address.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include "irrString.h"
|
||||
#include <tuple>
|
||||
@ -42,7 +43,7 @@ namespace GUIEngine
|
||||
class InputDevice;
|
||||
class PlayerProfile;
|
||||
|
||||
class NetworkConfig
|
||||
class NetworkConfig : public NoCopy
|
||||
{
|
||||
private:
|
||||
/** The singleton instance. */
|
||||
@ -101,7 +102,7 @@ private:
|
||||
std::string m_server_id_file;
|
||||
|
||||
std::vector<std::tuple<InputDevice*, PlayerProfile*,
|
||||
/*is_handicap*/bool> > m_network_players;
|
||||
PerPlayerDifficulty> > m_network_players;
|
||||
|
||||
core::stringw m_motd;
|
||||
|
||||
@ -189,7 +190,8 @@ public:
|
||||
m_password = "";
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
const std::vector<std::tuple<InputDevice*, PlayerProfile*, bool> >&
|
||||
const std::vector<std::tuple<InputDevice*, PlayerProfile*,
|
||||
PerPlayerDifficulty> >&
|
||||
getNetworkPlayers() const { return m_network_players; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isAddingNetworkPlayers() const
|
||||
@ -197,7 +199,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
void doneAddingNetworkPlayers() { m_done_adding_network_players = true; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool addNetworkPlayer(InputDevice* device, PlayerProfile* profile, bool h)
|
||||
bool addNetworkPlayer(InputDevice* device, PlayerProfile* profile,
|
||||
PerPlayerDifficulty d)
|
||||
{
|
||||
for (auto& p : m_network_players)
|
||||
{
|
||||
@ -206,7 +209,7 @@ public:
|
||||
if (std::get<1>(p) == profile)
|
||||
return false;
|
||||
}
|
||||
m_network_players.emplace_back(device, profile, h);
|
||||
m_network_players.emplace_back(device, profile, d);
|
||||
return true;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -141,6 +141,28 @@ public:
|
||||
/** Allows to read a buffer from the beginning again. */
|
||||
void reset() { m_current_offset = 0; }
|
||||
// ------------------------------------------------------------------------
|
||||
BareNetworkString& encodeString16(const irr::core::stringw& value)
|
||||
{
|
||||
uint8_t str_len = (uint8_t)value.size();
|
||||
if (value.size() > 255)
|
||||
str_len = 255;
|
||||
addUInt8(str_len);
|
||||
for (unsigned i = 0; i < str_len; i++)
|
||||
addUInt16((uint16_t)value[i]);
|
||||
return *this;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
int decodeString16(irr::core::stringw* out) const
|
||||
{
|
||||
unsigned str_len = getUInt8();
|
||||
for (unsigned i = 0; i < str_len; i++)
|
||||
{
|
||||
uint16_t c = getUInt16();
|
||||
out->append((wchar_t)c);
|
||||
}
|
||||
return str_len + 1;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
BareNetworkString& encodeString(const std::string &value);
|
||||
BareNetworkString& encodeString(const irr::core::stringw &value);
|
||||
int decodeString(std::string *out) const;
|
||||
|
@ -97,7 +97,7 @@ void ClientLobby::clearPlayers()
|
||||
*/
|
||||
void ClientLobby::setAddress(const TransportAddress &address)
|
||||
{
|
||||
m_server_address.copy(address);
|
||||
m_server_address = address;
|
||||
} // setAddress
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -513,6 +513,9 @@ void ClientLobby::updatePlayerList(Event* event)
|
||||
// icon to be used, see NetworkingLobby::loadedFromFile
|
||||
std::get<3>(pl) = data.getUInt8() == 1 /*if server owner*/ ? 0 :
|
||||
std::get<1>(pl) != 0 /*if online account*/ ? 1 : 2;
|
||||
PerPlayerDifficulty d = (PerPlayerDifficulty)data.getUInt8();
|
||||
if (d == PLAYER_DIFFICULTY_HANDICAP)
|
||||
std::get<2>(pl) = _("%s (handicapped)", std::get<2>(pl));
|
||||
players.push_back(pl);
|
||||
}
|
||||
NetworkingLobby::getInstance()->updatePlayers(players);
|
||||
@ -541,13 +544,12 @@ void ClientLobby::handleChat(Event* event)
|
||||
if (!UserConfigParams::m_lobby_chat)
|
||||
return;
|
||||
SFXManager::get()->quickSound("plopp");
|
||||
std::string message;
|
||||
event->data().decodeString(&message);
|
||||
Log::info("ClientLobby", "%s", message.c_str());
|
||||
core::stringw message;
|
||||
event->data().decodeString16(&message);
|
||||
Log::info("ClientLobby", "%s", StringUtils::wideToUtf8(message).c_str());
|
||||
if (message.size() > 0)
|
||||
{
|
||||
NetworkingLobby::getInstance()->addMoreServerInfo(
|
||||
StringUtils::utf8ToWide(message));
|
||||
NetworkingLobby::getInstance()->addMoreServerInfo(message);
|
||||
}
|
||||
} // handleChat
|
||||
|
||||
|
@ -46,7 +46,7 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION)
|
||||
ConnectToPeer::ConnectToPeer(const TransportAddress &address)
|
||||
: Protocol(PROTOCOL_CONNECTION)
|
||||
{
|
||||
m_peer_address.copy(address);
|
||||
m_peer_address = address;
|
||||
// We don't need to find the peer address, so we can start
|
||||
// with the state when we found the peer address.
|
||||
m_state = WAIT_FOR_CONNECTION;
|
||||
@ -84,7 +84,7 @@ void ConnectToPeer::asynchronousUpdate()
|
||||
assert(get_peer_address);
|
||||
if (get_peer_address->getAddress().isUnset())
|
||||
return;
|
||||
m_peer_address.copy(get_peer_address->getAddress());
|
||||
m_peer_address = get_peer_address->getAddress();
|
||||
m_current_protocol = nullptr;
|
||||
if (m_peer_address.isUnset())
|
||||
{
|
||||
@ -117,7 +117,7 @@ void ConnectToPeer::asynchronousUpdate()
|
||||
// The wan remote should already start its ping message to us now
|
||||
// so we can send packet directly to it.
|
||||
TransportAddress broadcast_address;
|
||||
broadcast_address.copy(m_peer_address);
|
||||
broadcast_address = m_peer_address;
|
||||
|
||||
BareNetworkString aloha(std::string("aloha_stk"));
|
||||
STKHost::get()->sendRawPacket(aloha, broadcast_address);
|
||||
|
@ -46,8 +46,8 @@ ConnectToServer::ConnectToServer(std::shared_ptr<Server> server)
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
m_server = server;
|
||||
m_server_address.copy(m_server->getAddress());
|
||||
m_server = server;
|
||||
m_server_address = m_server->getAddress();
|
||||
}
|
||||
setHandleConnections(true);
|
||||
} // ConnectToServer(server, host)
|
||||
@ -82,7 +82,7 @@ void ConnectToServer::asynchronousUpdate()
|
||||
{
|
||||
if (!m_server)
|
||||
{
|
||||
while (!ServersManager::get()->refresh())
|
||||
while (!ServersManager::get()->refresh(false))
|
||||
StkTime::sleep(1);
|
||||
while (!ServersManager::get()->listUpdated())
|
||||
StkTime::sleep(1);
|
||||
@ -105,7 +105,7 @@ void ConnectToServer::asynchronousUpdate()
|
||||
return a->getCurrentPlayers() < b->getCurrentPlayers();
|
||||
});
|
||||
m_server = servers[0];
|
||||
m_server_address.copy(m_server->getAddress());
|
||||
m_server_address = m_server->getAddress();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -435,7 +435,7 @@ void ConnectToServer::waitingAloha(bool is_wan)
|
||||
if (sender.isPublicAddressLocalhost())
|
||||
sender.setIP(0x7f000001); // 127.0.0.1
|
||||
}
|
||||
m_server_address.copy(sender);
|
||||
m_server_address = sender;
|
||||
m_state = CONNECTING;
|
||||
// Reset timer for next usage
|
||||
m_timer = 0.0;
|
||||
|
@ -169,12 +169,12 @@ void ServerLobby::handleChat(Event* event)
|
||||
return;
|
||||
}
|
||||
core::stringw message;
|
||||
event->data().decodeStringW(&message);
|
||||
event->data().decodeString16(&message);
|
||||
if (message.size() > 0)
|
||||
{
|
||||
NetworkString* chat = getNetworkString();
|
||||
chat->setSynchronous(true);
|
||||
chat->addUInt8(LE_CHAT).encodeString(message);
|
||||
chat->addUInt8(LE_CHAT).encodeString16(message);
|
||||
sendMessageToPeersChangingToken(chat, /*reliable*/true);
|
||||
delete chat;
|
||||
}
|
||||
@ -264,7 +264,7 @@ void ServerLobby::asynchronousUpdate()
|
||||
}
|
||||
else
|
||||
{
|
||||
m_server_address.copy(STKHost::get()->getPublicAddress());
|
||||
m_server_address = STKHost::get()->getPublicAddress();
|
||||
STKHost::get()->startListening();
|
||||
m_state = REGISTER_SELF_ADDRESS;
|
||||
}
|
||||
@ -1045,6 +1045,7 @@ void ServerLobby::updatePlayerList()
|
||||
if (m_server_owner.lock() == profile->getPeer())
|
||||
server_owner = 1;
|
||||
pl->addUInt8(server_owner);
|
||||
pl->addUInt8(profile->getPerPlayerDifficulty());
|
||||
}
|
||||
sendMessageToPeersChangingToken(pl);
|
||||
delete pl;
|
||||
@ -1276,8 +1277,8 @@ void ServerLobby::finishedLoadingWorldClient(Event *event)
|
||||
{
|
||||
std::shared_ptr<STKPeer> peer = event->getPeerSP();
|
||||
m_peers_ready.at(peer) = true;
|
||||
Log::info("ServerLobby", "Peer %d has finished loading world",
|
||||
peer->getHostId());
|
||||
Log::info("ServerLobby", "Peer %d has finished loading world at %lf",
|
||||
peer->getHostId(), StkTime::getRealTime());
|
||||
} // finishedLoadingWorldClient
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -145,7 +145,7 @@ void RewindManager::addEvent(EventRewinder *event_rewinder,
|
||||
* \param buffer Pointer to the event data.
|
||||
*/
|
||||
void RewindManager::addNetworkEvent(EventRewinder *event_rewinder,
|
||||
BareNetworkString *buffer, int ticks)
|
||||
BareNetworkString *buffer, int ticks)
|
||||
{
|
||||
m_rewind_queue.addNetworkEvent(event_rewinder, buffer, ticks);
|
||||
} // addNetworkEvent
|
||||
|
@ -225,6 +225,20 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// Any state of event that is received before the latest confirmed
|
||||
// state can be deleted.
|
||||
if ((*i)->getTicks() < m_latest_confirmed_state_time)
|
||||
{
|
||||
Log::info("RewindQueue",
|
||||
"Deleting %s at %d because it's before confirmed state %d",
|
||||
(*i)->isEvent() ? "event" : "state",
|
||||
(*i)->getTicks(),
|
||||
m_latest_confirmed_state_time);
|
||||
delete *i;
|
||||
i = m_network_events.getData().erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// A server never rewinds (otherwise we would have to handle
|
||||
// duplicated states, which in the best case would then have
|
||||
// a negative effect for every player, when in fact only one
|
||||
@ -276,6 +290,7 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind,
|
||||
if (latest_confirmed_state > m_latest_confirmed_state_time)
|
||||
{
|
||||
cleanupOldRewindInfo(latest_confirmed_state);
|
||||
m_latest_confirmed_state_time = latest_confirmed_state;
|
||||
}
|
||||
|
||||
} // mergeNetworkData
|
||||
|
@ -116,11 +116,11 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players,
|
||||
m_server_owner = 0;
|
||||
m_current_players = current_players;
|
||||
m_max_players = max_players;
|
||||
m_address.copy(address);
|
||||
m_address = address;
|
||||
// In case of LAN server, public and private port are the same.
|
||||
m_private_port = m_address.getPort();
|
||||
m_difficulty = (RaceManager::Difficulty)difficulty;
|
||||
m_server_mode = server_mode;
|
||||
m_difficulty = (RaceManager::Difficulty)difficulty;
|
||||
m_server_mode = server_mode;
|
||||
m_password_protected = password_protected;
|
||||
m_distance = 0.0f;
|
||||
} // server(server_id, ...)
|
||||
|
@ -31,9 +31,16 @@
|
||||
#include "utils/time.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <irrString.h>
|
||||
#include <string>
|
||||
|
||||
#if defined(WIN32)
|
||||
# undef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x600
|
||||
# include <iphlpapi.h>
|
||||
#else
|
||||
# include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#define SERVER_REFRESH_INTERVAL 5.0f
|
||||
|
||||
static ServersManager* g_manager_singleton(NULL);
|
||||
@ -136,11 +143,14 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
addr.host = STKHost::HOST_ANY;
|
||||
addr.port = STKHost::PORT_ANY;
|
||||
Network *broadcast = new Network(1, 1, 0, 0, &addr);
|
||||
|
||||
BareNetworkString s(std::string("stk-server"));
|
||||
TransportAddress broadcast_address(-1,
|
||||
NetworkConfig::get()->getServerDiscoveryPort());
|
||||
broadcast->sendRawPacket(s, broadcast_address);
|
||||
const std::vector<TransportAddress> &all_bcast =
|
||||
ServersManager::get()->getBroadcastAddresses();
|
||||
for (auto &bcast_addr : all_bcast)
|
||||
{
|
||||
Log::info("Server Discovery", "Broadcasting to %s",
|
||||
bcast_addr.toString().c_str());
|
||||
broadcast->sendRawPacket(std::string("stk-server"), bcast_addr);
|
||||
}
|
||||
|
||||
Log::info("ServersManager", "Sent broadcast message.");
|
||||
|
||||
@ -153,7 +163,12 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
const auto& servers = ServersManager::get()->getServers();
|
||||
int cur_server_id = (int)servers.size();
|
||||
assert(cur_server_id == 0);
|
||||
std::vector<std::shared_ptr<Server> > servers_now;
|
||||
// Use a map with the server name as key to automatically remove
|
||||
// duplicated answers from a server (since we potentially do
|
||||
// multiple broadcasts). We can not use the sender ip address,
|
||||
// because e.g. a local client would answer as 127.0.0.1 and
|
||||
// 192.168.**.
|
||||
std::map<irr::core::stringw, std::shared_ptr<Server> > servers_now;
|
||||
while (StkTime::getRealTime() - start_time < DURATION)
|
||||
{
|
||||
TransportAddress sender;
|
||||
@ -178,9 +193,10 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
uint8_t mode = s.getUInt8();
|
||||
sender.setPort(port);
|
||||
uint8_t password = s.getUInt8();
|
||||
servers_now.emplace_back(std::make_shared<Server>
|
||||
servers_now.emplace(name, std::make_shared<Server>
|
||||
(cur_server_id++, name, max_players, players,
|
||||
difficulty, mode, sender, password == 1));
|
||||
difficulty, mode, sender, password == 1) );
|
||||
//all_servers.[name] = servers_now.back();
|
||||
} // if received_data
|
||||
} // while still waiting
|
||||
m_success = true;
|
||||
@ -199,11 +215,25 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
|
||||
} // getLANRefreshRequest
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Takes a mapping of server name to server data (to avoid having the same
|
||||
* server listed more than once since the client will be doing multiple
|
||||
* broadcasts to find a server), and converts this into a list of servers.
|
||||
* \param servers Mapping of server name to Server object.
|
||||
*/
|
||||
void ServersManager::setLanServers(const std::map<irr::core::stringw,
|
||||
std::shared_ptr<Server> >& servers)
|
||||
{
|
||||
m_servers.clear();
|
||||
for (auto i : servers) m_servers.emplace_back(i.second);
|
||||
m_list_updated = true;
|
||||
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Factory function to create either a LAN or a WAN update-of-server
|
||||
* requests. The current list of servers is also cleared.
|
||||
*/
|
||||
bool ServersManager::refresh()
|
||||
bool ServersManager::refresh(bool full_refresh)
|
||||
{
|
||||
if (StkTime::getRealTime() - m_last_load_time.load()
|
||||
< SERVER_REFRESH_INTERVAL)
|
||||
@ -214,10 +244,21 @@ bool ServersManager::refresh()
|
||||
|
||||
cleanUpServers();
|
||||
m_list_updated = false;
|
||||
|
||||
if (NetworkConfig::get()->isWAN())
|
||||
Online::RequestManager::get()->addRequest(getWANRefreshRequest());
|
||||
{
|
||||
Online::RequestManager::get()->addRequest(getWANRefreshRequest());
|
||||
}
|
||||
else
|
||||
Online::RequestManager::get()->addRequest(getLANRefreshRequest());
|
||||
{
|
||||
if (full_refresh)
|
||||
{
|
||||
updateBroadcastAddresses();
|
||||
}
|
||||
|
||||
Online::RequestManager::get()->addRequest(getLANRefreshRequest());
|
||||
}
|
||||
|
||||
return true;
|
||||
} // refresh
|
||||
|
||||
@ -253,3 +294,147 @@ void ServersManager::setWanServers(bool success, const XMLNode* input)
|
||||
m_last_load_time.store((float)StkTime::getRealTime());
|
||||
m_list_updated = true;
|
||||
} // refresh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sets a list of default broadcast addresses which is used in case no valid
|
||||
* broadcast address is found. This list includes default private network
|
||||
* addresses.
|
||||
*/
|
||||
void ServersManager::setDefaultBroadcastAddresses()
|
||||
{
|
||||
// Add some common LAN addresses
|
||||
m_broadcast_address.emplace_back(std::string("192.168.255.255"));
|
||||
m_broadcast_address.emplace_back(std::string("192.168.0.255") );
|
||||
m_broadcast_address.emplace_back(std::string("192.168.1.255") );
|
||||
m_broadcast_address.emplace_back(std::string("172.31.255.255") );
|
||||
m_broadcast_address.emplace_back(std::string("172.16.255.255") );
|
||||
m_broadcast_address.emplace_back(std::string("172.16.0.255") );
|
||||
m_broadcast_address.emplace_back(std::string("10.255.255.255") );
|
||||
m_broadcast_address.emplace_back(std::string("10.0.255.255") );
|
||||
m_broadcast_address.emplace_back(std::string("10.0.0.255") );
|
||||
m_broadcast_address.emplace_back(std::string("255.255.255.255"));
|
||||
m_broadcast_address.emplace_back(std::string("127.0.0.255") );
|
||||
m_broadcast_address.emplace_back(std::string("127.0.0.1") );
|
||||
} // setDefaultBroadcastAddresses
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** This masks various possible broadcast addresses. For example, in a /16
|
||||
* network it would first use *.*.255.255, then *.*.*.255. Also if the
|
||||
* length of the mask is not a multiple of 8, the original value will
|
||||
* be used, before multiple of 8 are create: /22 (*.3f.ff.ff), then
|
||||
* /16 (*.*.ff.ff), /8 (*.*.*.ff). While this is usually an overkill,
|
||||
* it can help in the case that the router does not forward a broadcast
|
||||
* as expected (this problem often happens with 255.255.255.255, which is
|
||||
* why this broadcast address creation code was added).
|
||||
* \param a The transport address for which the broadcast addresses need
|
||||
* to be created.
|
||||
* \param len Number of bits to be or'ed.
|
||||
*/
|
||||
void ServersManager::addAllBroadcastAddresses(const TransportAddress &a, int len)
|
||||
{
|
||||
// Try different broadcast addresses - by masking on
|
||||
// byte boundaries
|
||||
while (len > 0)
|
||||
{
|
||||
unsigned int mask = (1 << len) - 1;
|
||||
TransportAddress bcast(a.getIP() | mask,
|
||||
NetworkConfig::get()->getServerDiscoveryPort());
|
||||
Log::info("Broadcast", "address %s length %d mask %x --> %s",
|
||||
a.toString().c_str(),
|
||||
len, mask,
|
||||
bcast.toString().c_str());
|
||||
m_broadcast_address.push_back(bcast);
|
||||
if (len % 8 != 0)
|
||||
len -= (len % 8);
|
||||
else
|
||||
len = len - 8;
|
||||
} // while len > 0
|
||||
} // addAllBroadcastAddresses
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Updates a list of all possible broadcast addresses on this machine.
|
||||
* It queries all adapters for active IPV4 interfaces, determines their
|
||||
* netmask to create the broadcast addresses. It will also add 'smaller'
|
||||
* broadcast addesses, e.g. in a /16 network, it will add *.*.255.255 and
|
||||
* *.*.*.255, since it was sometimes observed that routers would not let
|
||||
* all broadcast addresses through. Duplicated answers (from the same server
|
||||
* to different addersses) will be filtered out in ServersManager.
|
||||
*/
|
||||
void ServersManager::updateBroadcastAddresses()
|
||||
{
|
||||
m_broadcast_address.clear();
|
||||
|
||||
#ifdef WIN32
|
||||
IP_ADAPTER_ADDRESSES *addresses;
|
||||
int count = 100, return_code;
|
||||
|
||||
int iteration = 0;
|
||||
do
|
||||
{
|
||||
addresses = new IP_ADAPTER_ADDRESSES[count];
|
||||
ULONG buf_len = sizeof(IP_ADAPTER_ADDRESSES)*count;
|
||||
long flags = 0;
|
||||
return_code = GetAdaptersAddresses(AF_INET, flags, NULL, addresses,
|
||||
&buf_len);
|
||||
iteration++;
|
||||
} while (return_code == ERROR_BUFFER_OVERFLOW && iteration<10);
|
||||
|
||||
if (return_code == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
Log::warn("NetworkConfig", "Can not get broadcast addresses.");
|
||||
setDefaultBroadcastAddresses();
|
||||
return;
|
||||
}
|
||||
|
||||
for (IP_ADAPTER_ADDRESSES *p = addresses; p; p = p->Next)
|
||||
{
|
||||
// Check all operational IP4 adapters
|
||||
if (p->OperStatus == IfOperStatusUp &&
|
||||
p->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET)
|
||||
{
|
||||
const sockaddr_in *sa = (sockaddr_in*)p->FirstUnicastAddress->Address.lpSockaddr;
|
||||
// Use sa->sin_addr.S_un.S_addr and htonl?
|
||||
TransportAddress ta(sa->sin_addr.S_un.S_un_b.s_b1,
|
||||
sa->sin_addr.S_un.S_un_b.s_b2,
|
||||
sa->sin_addr.S_un.S_un_b.s_b3,
|
||||
sa->sin_addr.S_un.S_un_b.s_b4);
|
||||
int len = 32 - p->FirstUnicastAddress->OnLinkPrefixLength;
|
||||
addAllBroadcastAddresses(ta, len);
|
||||
}
|
||||
}
|
||||
#else
|
||||
struct ifaddrs *addresses, *p;
|
||||
|
||||
getifaddrs(&addresses);
|
||||
for (p = addresses; p; p = p->ifa_next)
|
||||
{
|
||||
if (p->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) p->ifa_addr;
|
||||
TransportAddress ta(htonl(sa->sin_addr.s_addr), 0);
|
||||
uint32_t u = ((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr;
|
||||
// Convert mask to #bits: SWAT algorithm
|
||||
u = u - ((u >> 1) & 0x55555555);
|
||||
u = (u & 0x33333333) + ((u >> 2) & 0x33333333);
|
||||
u = (((u + (u >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||
|
||||
printf("Interface: %s\tAddress: %s\tmask: %x\n", p->ifa_name,
|
||||
ta.toString().c_str(), u);
|
||||
addAllBroadcastAddresses(ta, u);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // updateBroadcastAddresses
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns a list of all possible broadcast addresses on this machine.
|
||||
*/
|
||||
const std::vector<TransportAddress>& ServersManager::getBroadcastAddresses()
|
||||
{
|
||||
if (m_broadcast_address.empty())
|
||||
{
|
||||
updateBroadcastAddresses();
|
||||
}
|
||||
|
||||
return m_broadcast_address;
|
||||
} // getBroadcastAddresses
|
||||
|
@ -19,13 +19,17 @@
|
||||
#ifndef HEADER_SERVERS_MANAGER_HPP
|
||||
#define HEADER_SERVERS_MANAGER_HPP
|
||||
|
||||
#include <irrString.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Online { class XMLRequest; }
|
||||
class Server;
|
||||
class TransportAddress;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
@ -37,6 +41,9 @@ class ServersManager
|
||||
private:
|
||||
/** List of servers */
|
||||
std::vector<std::shared_ptr<Server> > m_servers;
|
||||
|
||||
/** List of broadcast addresses to use. */
|
||||
std::vector<TransportAddress> m_broadcast_address;
|
||||
|
||||
std::atomic<float> m_last_load_time;
|
||||
|
||||
@ -48,16 +55,16 @@ private:
|
||||
// ------------------------------------------------------------------------
|
||||
void setWanServers(bool success, const XMLNode* input);
|
||||
// ------------------------------------------------------------------------
|
||||
void setLanServers(std::vector<std::shared_ptr<Server> >& servers)
|
||||
{
|
||||
m_servers = std::move(servers);
|
||||
m_list_updated = true;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
Online::XMLRequest* getWANRefreshRequest() const;
|
||||
// ------------------------------------------------------------------------
|
||||
Online::XMLRequest* getLANRefreshRequest() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void setLanServers(const std::map<irr::core::stringw,
|
||||
std::shared_ptr<Server> >& servers);
|
||||
|
||||
void setDefaultBroadcastAddresses();
|
||||
void addAllBroadcastAddresses(const TransportAddress &a, int len);
|
||||
void updateBroadcastAddresses();
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// Singleton
|
||||
@ -67,11 +74,13 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
void cleanUpServers() { m_servers.clear(); }
|
||||
// ------------------------------------------------------------------------
|
||||
bool refresh();
|
||||
bool refresh(bool full_refresh);
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<std::shared_ptr<Server> >& getServers() { return m_servers; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool listUpdated() const { return m_list_updated; }
|
||||
// ------------------------------------------------------------------------
|
||||
const std::vector<TransportAddress>& getBroadcastAddresses();
|
||||
|
||||
}; // class ServersManager
|
||||
#endif // HEADER_SERVERS_MANAGER_HPP
|
||||
|
@ -577,12 +577,12 @@ void STKHost::setPublicAddress()
|
||||
// alternate NAT traversal methods.
|
||||
if (!xor_addr.isUnset())
|
||||
{
|
||||
m_public_address.copy(xor_addr);
|
||||
m_public_address = xor_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::warn("STKHost", "Only non xor-mapped address returned.");
|
||||
m_public_address.copy(non_xor_addr);
|
||||
m_public_address = non_xor_addr;
|
||||
}
|
||||
// Succeed, save ping
|
||||
UserConfigParams::m_stun_list[server_name] =
|
||||
|
@ -22,7 +22,6 @@
|
||||
#ifndef HEADER_TRANSPORT_ADDRESS_HPP
|
||||
#define HEADER_TRANSPORT_ADDRESS_HPP
|
||||
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/types.hpp"
|
||||
|
||||
@ -35,7 +34,7 @@
|
||||
* \brief Describes a transport-layer address.
|
||||
* For IP networks, a transport address is the couple ip:port.
|
||||
*/
|
||||
class TransportAddress : public NoCopy
|
||||
class TransportAddress
|
||||
{
|
||||
private:
|
||||
uint32_t m_ip; //!< The IPv4 address
|
||||
@ -49,6 +48,14 @@ public:
|
||||
m_port = port;
|
||||
} // TransportAddress
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
TransportAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4,
|
||||
uint16_t port=0)
|
||||
{
|
||||
m_ip = (b1 << 24) + (b2 << 16) + (b3 << 8) + b4;
|
||||
m_port = port;
|
||||
} // TransportAddress(uint8_t,...)
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Construct an transport address from an ENetAddress. */
|
||||
TransportAddress(const ENetAddress &a)
|
||||
@ -92,15 +99,6 @@ public:
|
||||
~TransportAddress() {}
|
||||
// ------------------------------------------------------------------------
|
||||
static void unitTesting();
|
||||
private:
|
||||
friend class NetworkConfig;
|
||||
/** The copy constructor is private, so that the friend class
|
||||
* NetworkConfig can access it to create a copy (getMyAddress), but
|
||||
* no other class can. */
|
||||
TransportAddress(const TransportAddress &other)
|
||||
{
|
||||
copy(other);
|
||||
} // TransportAddress(const TransportAddress&)
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool isPublicAddressLocalhost() const;
|
||||
@ -108,15 +106,6 @@ public:
|
||||
bool isLAN() const;
|
||||
// ------------------------------------------------------------------------
|
||||
bool isUnset() const { return m_ip == 0 || m_port == 0; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** A copy function (to replace the copy constructor which is disabled
|
||||
* using NoCopy): it copies the data from the argument into this object.*/
|
||||
void copy(const TransportAddress &other)
|
||||
{
|
||||
m_ip = other.m_ip;
|
||||
m_port = other.m_port;
|
||||
} // copy
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Resets ip and port to 0. */
|
||||
void clear()
|
||||
|
@ -46,7 +46,6 @@
|
||||
#include "network/protocol_manager.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/protocols/lobby_protocol.hpp"
|
||||
#include "network/race_event_manager.hpp"
|
||||
#include "replay/replay_play.hpp"
|
||||
#include "scriptengine/property_animator.hpp"
|
||||
@ -561,15 +560,6 @@ void RaceManager::startNextRace()
|
||||
m_kart_status[i].m_last_score = m_kart_status[i].m_score;
|
||||
m_kart_status[i].m_last_time = 0;
|
||||
}
|
||||
|
||||
// In networked races, inform the start game protocol that
|
||||
// the world has been setup
|
||||
if(NetworkConfig::get()->isNetworking())
|
||||
{
|
||||
auto lobby = LobbyProtocol::get<LobbyProtocol>();
|
||||
assert(lobby);
|
||||
lobby->finishedLoadingWorld();
|
||||
}
|
||||
} // startNextRace
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -63,6 +63,7 @@ void CustomVideoSettingsDialog::beforeAddingWidgets()
|
||||
particles_effects->addLabel(_("Important only"));
|
||||
particles_effects->addLabel(_("Enabled"));
|
||||
particles_effects->setValue(UserConfigParams::m_particles_effects);
|
||||
particles_effects->setMin(1);
|
||||
|
||||
SpinnerWidget* geometry_level = getWidget<SpinnerWidget>("geometry_detail");
|
||||
//I18N: Geometry level disabled : lowest level, no details
|
||||
|
@ -43,9 +43,9 @@ core::stringw getLabel(RaceManager::Difficulty difficulty, const ChallengeData*
|
||||
{
|
||||
core::stringw label;
|
||||
|
||||
if (c->getPosition(difficulty) != -1)
|
||||
if (c->getMaxPosition(difficulty) != -1)
|
||||
{
|
||||
int r = c->getPosition(difficulty);
|
||||
int r = c->getMaxPosition(difficulty);
|
||||
if (c->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) r--;
|
||||
|
||||
if (label.size() > 0) label.append(L"\n");
|
||||
@ -80,7 +80,10 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
|
||||
std::string challenge_id) :
|
||||
ModalDialog(percentWidth, percentHeight)
|
||||
{
|
||||
loadFromFile("select_challenge.stkgui");
|
||||
if (PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
loadFromFile("select_challenge_nobest.stkgui");
|
||||
else
|
||||
loadFromFile("select_challenge.stkgui");
|
||||
m_challenge_id = challenge_id;
|
||||
World::getWorld()->schedulePause(WorldStatus::IN_GAME_MENU_PHASE);
|
||||
|
||||
@ -95,6 +98,14 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
|
||||
case 2:
|
||||
getWidget("expert")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
if(PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
getWidget("expert")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
else
|
||||
getWidget("supertux")->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const ChallengeStatus* c = PlayerManager::getCurrentPlayer()
|
||||
@ -121,6 +132,14 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
|
||||
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
|
||||
}
|
||||
|
||||
if (c->isSolved(RaceManager::DIFFICULTY_BEST)
|
||||
&& !PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
{
|
||||
IconButtonWidget* btn = getWidget<IconButtonWidget>("supertux");
|
||||
btn->setImage(file_manager->getAsset(FileManager::GUI,"cup_platinum.png"),
|
||||
IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
|
||||
}
|
||||
|
||||
|
||||
LabelWidget* novice_label = getWidget<LabelWidget>("novice_label");
|
||||
LabelWidget* medium_label = getWidget<LabelWidget>("intermediate_label");
|
||||
@ -130,6 +149,12 @@ SelectChallengeDialog::SelectChallengeDialog(const float percentWidth,
|
||||
medium_label->setText( getLabel(RaceManager::DIFFICULTY_MEDIUM, c->getData()), false );
|
||||
expert_label->setText( getLabel(RaceManager::DIFFICULTY_HARD, c->getData()), false );
|
||||
|
||||
if (!PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
{
|
||||
LabelWidget* supertux_label = getWidget<LabelWidget>("supertux_label");
|
||||
supertux_label->setText( getLabel(RaceManager::DIFFICULTY_BEST, c->getData()), false );
|
||||
}
|
||||
|
||||
if (c->getData()->isGrandPrix())
|
||||
{
|
||||
const GrandPrixData* gp = grand_prix_manager->getGrandPrix(c->getData()->getGPId());
|
||||
@ -167,7 +192,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
|
||||
{
|
||||
std::string eventSource = eventSourceParam;
|
||||
if (eventSource == "novice" || eventSource == "intermediate" ||
|
||||
eventSource == "expert")
|
||||
eventSource == "expert" || eventSource == "supertux")
|
||||
{
|
||||
const ChallengeData* challenge = unlock_manager->getChallengeData(m_challenge_id);
|
||||
|
||||
@ -229,6 +254,11 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
|
||||
challenge->setRace(RaceManager::DIFFICULTY_HARD);
|
||||
UserConfigParams::m_difficulty = 2;
|
||||
}
|
||||
else if (eventSource == "supertux")
|
||||
{
|
||||
challenge->setRace(RaceManager::DIFFICULTY_BEST);
|
||||
UserConfigParams::m_difficulty = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("SelectChallenge", "Unknown widget <%s>\n",
|
||||
@ -250,4 +280,3 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -111,11 +111,14 @@ GUIEngine::EventPropagation
|
||||
const unsigned pid = m_profiles->getValue();
|
||||
assert(pid < PlayerManager::get()->getNumPlayers());
|
||||
PlayerProfile* p = m_available_players[pid];
|
||||
const bool handicap = m_handicap->getState();
|
||||
if (NetworkConfig::get()->addNetworkPlayer(m_device, p, handicap))
|
||||
const PerPlayerDifficulty d = m_handicap->getState() ?
|
||||
PLAYER_DIFFICULTY_HANDICAP : PLAYER_DIFFICULTY_NORMAL;
|
||||
if (NetworkConfig::get()->addNetworkPlayer(m_device, p, d))
|
||||
{
|
||||
NetworkingLobby::getInstance()
|
||||
->addSplitscreenPlayer(p->getName());
|
||||
core::stringw name = p->getName();
|
||||
if (d == PLAYER_DIFFICULTY_HANDICAP)
|
||||
name = _("%s (handicapped)", name);
|
||||
NetworkingLobby::getInstance()->addSplitscreenPlayer(name);
|
||||
m_self_destroy = true;
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(std::string model,
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(KartProperties* kart,
|
||||
FeatureUnlockedCutScene::UnlockedThing::UnlockedThing(const KartProperties* kart,
|
||||
irr::core::stringw msg)
|
||||
{
|
||||
m_unlocked_kart = kart;
|
||||
@ -202,7 +202,7 @@ void FeatureUnlockedCutScene::onCutsceneEnd()
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty difficulty)
|
||||
void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty difficulty,std::vector<const ChallengeData*>& unlocked)
|
||||
{
|
||||
PlayerProfile *player = PlayerManager::getCurrentPlayer();
|
||||
int points_before = player->getPoints();
|
||||
@ -210,9 +210,10 @@ void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty diffic
|
||||
|
||||
std::vector<std::string> tracks;
|
||||
std::vector<std::string> gps;
|
||||
std::vector<std::string> karts;
|
||||
|
||||
player->computeActive();
|
||||
unlock_manager->findWhatWasUnlocked(points_before, points_now, tracks, gps);
|
||||
unlock_manager->findWhatWasUnlocked(points_before, points_now, tracks, gps, karts, unlocked);
|
||||
|
||||
for (unsigned int i = 0; i < tracks.size(); i++)
|
||||
{
|
||||
@ -222,26 +223,41 @@ void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty diffic
|
||||
{
|
||||
addUnlockedGP(grand_prix_manager->getGrandPrix(gps[i]));
|
||||
}
|
||||
for (unsigned int i = 0; i < karts.size(); i++)
|
||||
{
|
||||
addUnlockedKart(kart_properties_manager->getKart(karts[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty)
|
||||
void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty, bool is_grandprix)
|
||||
{
|
||||
core::stringw msg;
|
||||
|
||||
int gp_factor = is_grandprix ? GP_FACTOR : 1;
|
||||
RaceManager::Difficulty max_unlocked_difficulty = RaceManager::DIFFICULTY_BEST;
|
||||
|
||||
if (PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
max_unlocked_difficulty = RaceManager::DIFFICULTY_HARD;
|
||||
|
||||
switch (difficulty)
|
||||
{
|
||||
case RaceManager::DIFFICULTY_EASY:
|
||||
msg = _("You completed the easy challenge! Points earned on this level: %i/%i",
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY], CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD]);
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_EASY]*gp_factor, CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor);
|
||||
break;
|
||||
case RaceManager::DIFFICULTY_MEDIUM:
|
||||
msg = _("You completed the intermediate challenge! Points earned on this level: %i/%i",
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_MEDIUM], CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD]);
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_MEDIUM]*gp_factor, CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor);
|
||||
break;
|
||||
case RaceManager::DIFFICULTY_HARD:
|
||||
msg = _("You completed the difficult challenge! Points earned on this level: %i/%i",
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD], CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD]);
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_HARD]*gp_factor, CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor);
|
||||
break;
|
||||
case RaceManager::DIFFICULTY_BEST:
|
||||
msg = _("You completed the SuperTux challenge! Points earned on this level: %i/%i",
|
||||
CHALLENGE_POINTS[RaceManager::DIFFICULTY_BEST]*gp_factor, CHALLENGE_POINTS[max_unlocked_difficulty]*gp_factor);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@ -259,6 +275,9 @@ void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty)
|
||||
case RaceManager::DIFFICULTY_HARD:
|
||||
model = file_manager->getAsset(FileManager::MODEL,"trophy_gold.spm");
|
||||
break;
|
||||
case RaceManager::DIFFICULTY_BEST:
|
||||
model = file_manager->getAsset(FileManager::MODEL,"trophy_platinum.spm");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
return;
|
||||
@ -269,12 +288,15 @@ void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// unused for now, maybe will be useful later?
|
||||
|
||||
void FeatureUnlockedCutScene::addUnlockedKart(KartProperties* unlocked_kart,
|
||||
irr::core::stringw msg)
|
||||
void FeatureUnlockedCutScene::addUnlockedKart(const KartProperties* unlocked_kart)
|
||||
{
|
||||
assert(unlocked_kart != NULL);
|
||||
if (unlocked_kart == NULL)
|
||||
{
|
||||
Log::error("FeatureUnlockedCutScene::addUnlockedKart", "Unlocked kart does not exist");
|
||||
return;
|
||||
}
|
||||
irr::core::stringw msg = _("You unlocked %s!", unlocked_kart->getName());
|
||||
m_unlocked_stuff.push_back( new UnlockedThing(unlocked_kart, msg) );
|
||||
} // addUnlockedKart
|
||||
|
||||
@ -426,8 +448,6 @@ void FeatureUnlockedCutScene::init()
|
||||
Log::error("FeatureUnlockedCutScene::init", "Malformed unlocked goody");
|
||||
}
|
||||
}
|
||||
|
||||
PlayerManager::getCurrentPlayer()->clearUnlocked();
|
||||
} // init
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -54,7 +54,7 @@ class FeatureUnlockedCutScene : public GUIEngine::CutsceneScreen, public GUIEngi
|
||||
struct UnlockedThing
|
||||
{
|
||||
/** Will be non-null if this unlocked thing is a kart */
|
||||
KartProperties* m_unlocked_kart;
|
||||
const KartProperties* m_unlocked_kart;
|
||||
|
||||
std::string m_unlock_model;
|
||||
|
||||
@ -80,7 +80,7 @@ class FeatureUnlockedCutScene : public GUIEngine::CutsceneScreen, public GUIEngi
|
||||
|
||||
UnlockedThing(std::string model, irr::core::stringw msg);
|
||||
|
||||
UnlockedThing(KartProperties* kart, irr::core::stringw msg);
|
||||
UnlockedThing(const KartProperties* kart, irr::core::stringw msg);
|
||||
|
||||
/**
|
||||
* Creates a 'picture' reward.
|
||||
@ -141,11 +141,11 @@ public:
|
||||
void eventCallback(GUIEngine::Widget* widget, const std::string& name,
|
||||
const int playerID) OVERRIDE;
|
||||
|
||||
void findWhatWasUnlocked(RaceManager::Difficulty difficulty);
|
||||
void findWhatWasUnlocked(RaceManager::Difficulty difficulty,std::vector<const ChallengeData*>& unlocked);
|
||||
|
||||
/** Call before showing up the screen to make a kart come out of the chest.
|
||||
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
|
||||
void addUnlockedKart(KartProperties* unlocked_kart, irr::core::stringw msg);
|
||||
void addUnlockedKart(const KartProperties* unlocked_kart);
|
||||
|
||||
/** Call before showing up the screen to make a picture come out of the chest
|
||||
'addUnlockedThings' will invoke this, so you generally don't need to call this directly. */
|
||||
@ -166,7 +166,7 @@ public:
|
||||
void addUnlockedThings(const std::vector<const ChallengeData*> unlocked);
|
||||
*/
|
||||
|
||||
void addTrophy(RaceManager::Difficulty difficulty);
|
||||
void addTrophy(RaceManager::Difficulty difficulty, bool is_grandprix);
|
||||
|
||||
/** override from base class to handle escape press */
|
||||
virtual bool onEscapePressed() OVERRIDE;
|
||||
@ -175,4 +175,3 @@ public:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -189,14 +189,14 @@ void KartHoverListener::onSelectionChanged(DynamicRibbonWidget* theWidget,
|
||||
{
|
||||
// discard events sent when putting back to the right kart
|
||||
if (selectionID ==
|
||||
m_parent->m_kart_widgets[player_id].m_kartInternalName) return;
|
||||
m_parent->m_kart_widgets[player_id].m_kart_internal_name) return;
|
||||
|
||||
DynamicRibbonWidget* w =
|
||||
m_parent->getWidget<DynamicRibbonWidget>("karts");
|
||||
assert(w != NULL);
|
||||
|
||||
w->setSelection(m_parent->m_kart_widgets[player_id]
|
||||
.m_kartInternalName, player_id, true);
|
||||
.m_kart_internal_name, player_id, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -516,7 +516,7 @@ bool KartSelectionScreen::joinPlayer(InputDevice* device, PlayerProfile* p)
|
||||
|
||||
// ---- Create player/kart widget
|
||||
PlayerKartWidget* newPlayerWidget =
|
||||
new PlayerKartWidget(this, aplayer, NULL, kartsArea, m_kart_widgets.size(),
|
||||
new PlayerKartWidget(this, aplayer, kartsArea, m_kart_widgets.size(),
|
||||
selected_kart_group);
|
||||
|
||||
manualAddWidget(newPlayerWidget);
|
||||
@ -832,7 +832,7 @@ void KartSelectionScreen::updateKartStats(uint8_t widget_id,
|
||||
|
||||
if (kp != NULL)
|
||||
{
|
||||
w->setValues(kp);
|
||||
w->setValues(kp, m_kart_widgets[widget_id].getDifficulty());
|
||||
w->update(0);
|
||||
}
|
||||
}
|
||||
@ -1206,7 +1206,7 @@ void KartSelectionScreen::allPlayersDone()
|
||||
const int kart_count = m_kart_widgets.size();
|
||||
for (int n = 0; n < kart_count; n++)
|
||||
{
|
||||
std::string selected_kart = m_kart_widgets[n].m_kartInternalName;
|
||||
std::string selected_kart = m_kart_widgets[n].m_kart_internal_name;
|
||||
|
||||
if (selected_kart == RANDOM_KART_ID)
|
||||
{
|
||||
@ -1238,7 +1238,7 @@ void KartSelectionScreen::allPlayersDone()
|
||||
for (int i=0; i<item_count; i++)
|
||||
{
|
||||
if (items[i].m_code_name ==
|
||||
m_kart_widgets[n].m_kartInternalName)
|
||||
m_kart_widgets[n].m_kart_internal_name)
|
||||
{
|
||||
items[i].m_code_name = ID_DONT_USE;
|
||||
break;
|
||||
@ -1249,9 +1249,8 @@ void KartSelectionScreen::allPlayersDone()
|
||||
race_manager->setPlayerKart(n, selected_kart);
|
||||
|
||||
// Set per player difficulty if needed
|
||||
if (m_multiplayer && UserConfigParams::m_per_player_difficulty &&
|
||||
m_kart_widgets[n].isHandicapped())
|
||||
race_manager->setPlayerDifficulty(n, PLAYER_DIFFICULTY_HANDICAP);
|
||||
if (m_multiplayer && UserConfigParams::m_per_player_difficulty)
|
||||
race_manager->setPlayerDifficulty(n, m_kart_widgets[n].getDifficulty());
|
||||
}
|
||||
|
||||
// ---- Switch to assign mode
|
||||
|
@ -318,18 +318,11 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
parts.push_back("featunlocked");
|
||||
((CutsceneWorld*)World::getWorld())->setParts(parts);
|
||||
|
||||
scene->addTrophy(RaceManager::DIFFICULTY_EASY);
|
||||
scene->addTrophy(RaceManager::DIFFICULTY_EASY, false);
|
||||
|
||||
if (selection == "test_unlocked")
|
||||
{
|
||||
// the passed kart will not be modified, that's why I allow myself
|
||||
// to use const_cast
|
||||
scene->addUnlockedKart(
|
||||
const_cast<KartProperties*>(
|
||||
kart_properties_manager->getKart("tux")
|
||||
),
|
||||
L"You unlocked <actual text would go here...>"
|
||||
);
|
||||
scene->addUnlockedKart(kart_properties_manager->getKart("tux"));
|
||||
scene->addUnlockedTrack(track_manager->getTrack("lighthouse"));
|
||||
scene->push();
|
||||
}
|
||||
|
@ -43,6 +43,11 @@ void NetworkKartSelectionScreen::init()
|
||||
for (auto& p : NetworkConfig::get()->getNetworkPlayers())
|
||||
{
|
||||
joinPlayer(std::get<0>(p), std::get<1>(p));
|
||||
if (std::get<2>(p) == PLAYER_DIFFICULTY_HANDICAP)
|
||||
{
|
||||
m_kart_widgets.get(m_kart_widgets.size() -1)
|
||||
->enableHandicapForNetwork();
|
||||
}
|
||||
w->updateItemDisplay();
|
||||
if (!w->setSelection(UserConfigParams::m_default_kart, 0, true))
|
||||
{
|
||||
@ -80,7 +85,7 @@ void NetworkKartSelectionScreen::allPlayersDone()
|
||||
{
|
||||
// If server recieve an invalid name, it will auto correct to a random
|
||||
// kart
|
||||
kart.encodeString(m_kart_widgets[n].m_kartInternalName);
|
||||
kart.encodeString(m_kart_widgets[n].m_kart_internal_name);
|
||||
}
|
||||
STKHost::get()->sendToServer(&kart, true);
|
||||
|
||||
|
@ -258,8 +258,7 @@ void NetworkingLobby::sendChat(irr::core::stringw text)
|
||||
name = PlayerManager::getCurrentOnlineUserName();
|
||||
else
|
||||
name = player->getName();
|
||||
// Max 80 words
|
||||
chat.encodeString((name + L": " + text).subString(0, 80));
|
||||
chat.encodeString16(name + L": " + text);
|
||||
|
||||
STKHost::get()->sendToServer(&chat, true);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ void OnlineScreen::init()
|
||||
{
|
||||
NetworkConfig::get()->addNetworkPlayer(
|
||||
input_manager->getDeviceManager()->getLatestUsedDevice(),
|
||||
PlayerManager::getCurrentPlayer(), false/*handicap*/);
|
||||
PlayerManager::getCurrentPlayer(), PLAYER_DIFFICULTY_NORMAL);
|
||||
NetworkConfig::get()->doneAddingNetworkPlayers();
|
||||
}
|
||||
} // init
|
||||
@ -178,7 +178,7 @@ void OnlineScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
NetworkConfig::get()->cleanNetworkPlayers();
|
||||
NetworkConfig::get()->addNetworkPlayer(
|
||||
input_manager->getDeviceManager()->getLatestUsedDevice(),
|
||||
PlayerManager::getCurrentPlayer(), false/*handicap*/);
|
||||
PlayerManager::getCurrentPlayer(), PLAYER_DIFFICULTY_NORMAL);
|
||||
NetworkConfig::get()->doneAddingNetworkPlayers();
|
||||
}
|
||||
else
|
||||
@ -271,8 +271,8 @@ void OnlineScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
return false;
|
||||
}
|
||||
|
||||
m_entered_server_address.copy(
|
||||
STKHost::get()->getServerPeerForClient()->getAddress());
|
||||
m_entered_server_address =
|
||||
STKHost::get()->getServerPeerForClient()->getAddress();
|
||||
auto cl = LobbyProtocol::create<ClientLobby>();
|
||||
cl->setAddress(m_entered_server_address);
|
||||
cl->requestStart();
|
||||
|
@ -50,7 +50,7 @@ void OptionsScreenVideo::initPresets()
|
||||
({
|
||||
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */,
|
||||
false /* animatedCharacters */, 0 /* particles */, 0 /* image_quality */,
|
||||
false /* animatedCharacters */, 1 /* particles */, 0 /* image_quality */,
|
||||
false /* depth of field */, true /* degraded IBL */
|
||||
});
|
||||
|
||||
@ -58,7 +58,7 @@ void OptionsScreenVideo::initPresets()
|
||||
({
|
||||
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */,
|
||||
true /* animatedCharacters */, 0 /* particles */, 0 /* image_quality */,
|
||||
true /* animatedCharacters */, 2 /* particles */, 0 /* image_quality */,
|
||||
false /* depth of field */, true /* degraded IBL */
|
||||
});
|
||||
|
||||
@ -66,7 +66,7 @@ void OptionsScreenVideo::initPresets()
|
||||
({
|
||||
true /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */,
|
||||
true /* animatedCharacters */, 1 /* particles */, 1 /* image_quality */,
|
||||
true /* animatedCharacters */, 2 /* particles */, 1 /* image_quality */,
|
||||
false /* depth of field */, true /* degraded IBL */
|
||||
});
|
||||
|
||||
@ -74,7 +74,7 @@ void OptionsScreenVideo::initPresets()
|
||||
({
|
||||
true /* light */, 0 /* shadow */, false /* bloom */, true /* motionblur */,
|
||||
true /* lightshaft */, true /* glow */, true /* mlaa */, false /* ssao */,
|
||||
true /* animatedCharacters */, 1 /* particles */, 1 /* image_quality */,
|
||||
true /* animatedCharacters */, 2 /* particles */, 1 /* image_quality */,
|
||||
false /* depth of field */, false /* degraded IBL */
|
||||
});
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user