Merge pull request #10 from supertuxkart/master

Update fork
This commit is contained in:
Alayan-stk-2 2018-05-01 23:56:10 +02:00 committed by GitHub
commit 179e825e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 1354 additions and 551 deletions

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"/>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View 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>

View 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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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"/>

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -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>

View 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>

View File

@ -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" />

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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; }

View File

@ -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);

View File

@ -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; }
// ------------------------------------------------------------------------

View File

@ -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 */

View File

@ -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);

View File

@ -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()
{

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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
}

View File

@ -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());

View File

@ -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; }
};
}

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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)

View File

@ -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;

View File

@ -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>

View File

@ -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; }

View File

@ -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)
{

View File

@ -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;
}
// ------------------------------------------------------------------------

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
//-----------------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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, ...)

View File

@ -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

View File

@ -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

View File

@ -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] =

View File

@ -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()

View File

@ -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
//-----------------------------------------------------------------------------

View File

@ -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

View File

@ -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
}
// ----------------------------------------------------------------------------

View File

@ -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;
}

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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