Merge remote-tracking branch 'origin/master' into network-item-debugging

This commit is contained in:
hiker 2018-10-03 10:54:14 +10:00
commit 35deb3970b
122 changed files with 8106 additions and 2118 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="abyss" laps="3"/> <track id="abyss" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="0"/> <requirements trophies="0"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="candela_city" laps="3"/> <track id="candela_city" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="75"/> <requirements trophies="75"/>

View File

@ -1,24 +1,31 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="cocoa_temple" laps="3"/> <track id="cocoa_temple" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="timetrial"/>
<requirements trophies="5"/> <requirements trophies="0"/>
<best> <best>
<karts number="8"/> <karts number="1"
<requirements position="1" time="125"/> replay_file="challenge_cocoa_temple_supertux.replay"/>
<requirements position="1"/>
</best> </best>
<hard> <hard>
<karts number="7"/> <karts number="1"
<requirements position="1" time="160"/> replay_file="challenge_cocoa_temple_expert.replay"/>
<requirements position="1"/>
</hard> </hard>
<medium> <medium>
<karts number="6"/> <karts number="1"
replay_file="challenge_cocoa_temple_intermediate.replay"/>
<requirements position="1"/> <requirements position="1"/>
</medium> </medium>
<easy> <easy>
<karts number="5"/> <karts number="1"
replay_file="challenge_cocoa_temple_novice.replay"/>
<karts number="1"/>
<requirements position="1"/> <requirements position="1"/>
</easy> </easy>
<unlock kart="suzanne"/>
</challenge> </challenge>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="cornfield_crossing" laps="3"/> <track id="cornfield_crossing" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="0"/> <requirements trophies="0"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="fortmagma" laps="3"/> <track id="fortmagma" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="190"/> <requirements trophies="190"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="gran_paradiso_island" laps="3"/> <track id="gran_paradiso_island" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="80"/> <requirements trophies="80"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="greenvalley" laps="3"/> <track id="greenvalley" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="90"/> <requirements trophies="90"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="hacienda" laps="3"/> <track id="hacienda" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="25"/> <requirements trophies="25"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="lighthouse" laps="4"/> <track id="lighthouse" laps="4" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="125"/> <requirements trophies="125"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="mansion" laps="3"/> <track id="mansion" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="100"/> <requirements trophies="100"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="mines" laps="3"/> <track id="mines" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="140"/> <requirements trophies="140"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="minigolf" laps="4"/> <track id="minigolf" laps="4" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="130"/> <requirements trophies="130"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="olivermath" laps="5"/> <track id="olivermath" laps="5" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="0"/> <requirements trophies="0"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="sandtrack" laps="3"/> <track id="sandtrack" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="0"/> <requirements trophies="0"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="scotland" laps="3"/> <track id="scotland" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="0"/> <requirements trophies="0"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="snowmountain" laps="3"/> <track id="snowmountain" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="115"/> <requirements trophies="115"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="snowtuxpeak" laps="3"/> <track id="snowtuxpeak" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="45"/> <requirements trophies="45"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="stk_enterprise" laps="3"/> <track id="stk_enterprise" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="105"/> <requirements trophies="105"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="volcano_island" laps="2"/> <track id="volcano_island" laps="2" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="15"/> <requirements trophies="15"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="xr591" laps="2"/> <track id="xr591" laps="2" reverse="false"/>
<mode major="single" minor="quickrace"/> <mode major="single" minor="quickrace"/>
<requirements trophies="135"/> <requirements trophies="135"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<challenge version="3"> <challenge version="3">
<unlock_list list="false"/> <unlock_list list="false"/>
<track id="zengarden" laps="4"/> <track id="zengarden" laps="4" reverse="false"/>
<mode major="single" minor="timetrial"/> <mode major="single" minor="timetrial"/>
<requirements trophies="35"/> <requirements trophies="35"/>

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<stkgui> <stkgui>
<div y="2%" width="100%" height="96%" layout="vertical-row"> <div x="1%" y="2%" width="98%" height="96%" layout="vertical-row">
<div width="100%" height="40%" layout="vertical-row"> <div width="100%" height="40%" layout="vertical-row">
<div width="100%" height="25%" layout="vertical-row" > <div width="100%" height="25%" layout="vertical-row" >
<label id="name" width="100%" text_align="center"/> <label id="name" width="100%" text_align="center"/>
</div> </div>
<!-- This is filled in programmatically --> <!-- This is filled in programmatically -->
<box width="98%" height="75%" align="center" layout="vertical-row" padding="1"> <box width="100%" height="75%" align="center" layout="vertical-row" padding="1">
<list id="current_replay_info" x="0" y="0" width="100%" height="100%"/> <list id="current_replay_info" x="0" y="0" width="100%" height="100%"/>
</box> </box>
</div> </div>
@ -18,8 +18,8 @@
</div> </div>
<div width="64%" height="100%" layout="vertical-row"> <div width="64%" height="100%" layout="vertical-row">
<div width="95%" align="center" layout="vertical-row" height="fit"> <div width="95%" align="center" layout="vertical-row" height="50%">
<div width="100%" height="40" layout="horizontal-row" > <div width="100%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="record-race" I18N="Ghost replay info action" text_align="left"/> <checkbox width="fit" id="record-race" I18N="Ghost replay info action" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" id="record-race-text" height="100%" text_align="left" I18N="Ghost replay info action" text="Record the race for ghost replay"/> <label proportion="1" id="record-race-text" height="100%" text_align="left" I18N="Ghost replay info action" text="Record the race for ghost replay"/>
@ -36,7 +36,7 @@
</div> </div>
</div> </div>
<div width="95%" height="50%" align="center"> <div width="95%" height="40%" align="center">
<buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center"> <buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center">
<icon-button id="start" width="128" height="128" <icon-button id="start" width="128" height="128"
icon="gui/icons/green_check.png" icon="gui/icons/green_check.png"

View File

@ -5,12 +5,14 @@
<model id="model" width="100%" layout="horizontal-row" height="100%"> <model id="model" width="100%" layout="horizontal-row" height="100%">
</model> </model>
</div> </div>
<label text="0 to use the original color, otherwise pick one from slider." <div width="20%" height="fit" text-align="left" layout="horizontal-row" >
width="100%" text_align="center" word_wrap="true" <checkbox id="toggle-slider" />
I18N="In the kart color slider dialog"/> <spacer width="40"/>
<label id="toggle-text"/>
</div>
<spacer height="30" width="10"/> <spacer height="30" width="10"/>
<div height="fit" width="100%" layout="horizontal-row"> <div height="fit" width="100%" layout="horizontal-row">
<gauge id="color-slider" min_value="0" max_value="100" proportion="1"/> <gauge id="color-slider" min_value="1" max_value="100" proportion="1"/>
</div> </div>
<spacer height="30" width="10"/> <spacer height="30" width="10"/>
<button id="close" text="Apply" align="center"/> <button id="close" text="Apply" align="center"/>

View File

@ -44,6 +44,8 @@ options_language.png by Alayan, based on http://www.languageicon.org/, released
blue_flag.png, heart.png and red_flag.png by Benau, released under CC-BY-SA 4 blue_flag.png, heart.png and red_flag.png by Benau, released under CC-BY-SA 4
lap_flag.png, modified by Alayan, original by Alina Oleynik from The Noun Project, under CC-BY 3.0
==== ====
Glass Skin by Auria, under CC-BY-SA 3+ Glass Skin by Auria, under CC-BY-SA 3+

BIN
data/gui/icons/lap_flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -13,50 +13,55 @@
<list id="replay_list" x="0" y="0" width="100%" height="100%"/> <list id="replay_list" x="0" y="0" width="100%" height="100%"/>
</box> </box>
<tabs id="race_mode" height="6%" max_height="110" x="2%" width="98%" align="center"> <tabs id="race_mode" height="6%" max_height="110" x="1%" width="98%" align="center">
<icon-button id="tab_time_trial" width="128" height="128" icon="gui/icons/mode_tt.png" <icon-button id="tab_time_trial" width="128" height="128" icon="gui/icons/mode_tt.png"
I18N="In the ghost replay selection screen" text="Time trial"/> I18N="In the ghost replay selection screen" text="Time trial"/>
<icon-button id="tab_egg_hunt" width="128" height="128" icon="gui/icons/mode_easter.png" <icon-button id="tab_egg_hunt" width="128" height="128" icon="gui/icons/mode_easter.png"
I18N="In the ghost replay selection screen" text="Egg hunt"/> I18N="In the ghost replay selection screen" text="Egg hunt"/>
</tabs> </tabs>
<spacer width="100%" height="1.5%" /> <spacer width="100%" height="2%" />
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="1" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="best_times_toggle" text_align="left"/> <checkbox width="fit" id="best_times_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show the best times"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show the best times"/>
</div> </div>
<div proportion="1" height="fit" layout="horizontal-row" > <div width="40%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="compare_toggle" text_align="left"/> <checkbox width="fit" id="compare_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" id="compare-toggle-text" text_align="left" I18N="In the ghost replay selection screen" text="Compare replay"/> <label height="100%" id="compare-toggle-text" text_align="left" I18N="In the ghost replay selection screen" text="Compare replay"/>
</div> </div>
</div> </div>
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="2" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_difficulty_toggle" text_align="left"/> <checkbox width="fit" id="replay_difficulty_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current difficulty"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current difficulty"/>
</div> </div>
<div proportion="1" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_multiplayer_toggle" text_align="left"/>
<spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Hide multiplayer replays"/>
</div>
</div> </div>
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="1" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_version_toggle" text_align="left"/> <checkbox width="fit" id="replay_version_toggle" text_align="left"/>
<spacer width="1%" height="fit" /> <spacer width="2%" height="fit" />
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current version"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current version"/>
</div> </div>
</div> </div>
<button x="1%" id="record-ghost" I18N="In the ghost replay selection screen" text="Record a ghost replay"/> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_multiplayer_toggle" text_align="left"/>
<spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Hide multiplayer replays"/>
</div>
<div width="40%" height="fit" layout="horizontal-row" >
<spacer proportion="1" height="5"/>
<button width="fit" id="record-ghost" I18N="In the ghost replay selection screen" text="Record a ghost replay"/>
</div>
</div>
</div> </div>
</stkgui> </stkgui>

View File

@ -17,7 +17,7 @@
<checkbox width="fit" id="private_server" text_align="left"/> <checkbox width="fit" id="private_server" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" height="100%" text_align="left" <label proportion="1" height="100%" text_align="left"
I18N="In the server selection screen" text="Show only private server(s)"/> I18N="In the server selection screen" text="Show private server(s)"/>
<checkbox width="fit" id="game_started" text_align="left"/> <checkbox width="fit" id="game_started" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" height="100%" text_align="left" <label proportion="1" height="100%" text_align="left"

View File

@ -30,14 +30,26 @@
<!-- ************ SKIN CHOICE ************ --> <!-- ************ SKIN CHOICE ************ -->
<div layout="horizontal-row" width="100%" height="fit"> <div layout="horizontal-row" width="100%" height="fit">
<label I18N="In the ui settings" text="Skin" align="center"/> <label proportion="1" I18N="In the ui settings" text="Skin" align="center"/>
<spacer width="2%" height="20"/> <spacer width="2%" height="20"/>
<spinner id="skinchoice" width="30%"/> <div layout="horizontal-row" proportion="5" height="100%">
<spinner id="skinchoice" width="60%"/>
</div>
</div> </div>
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<label proportion="1" I18N="In the ui settings" text="Minimap" align="center"/>
<spacer width="2%" height="20"/>
<div layout="horizontal-row" proportion="5" height="100%">
<spinner id="minimap" width="60%"/>
</div>
</div>
<spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="fit">
<checkbox id="showfps"/> <checkbox id="showfps"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Display FPS" word_wrap="true"/> <label height="100%" I18N="In the ui settings" text="Display FPS" word_wrap="true"/>
@ -45,7 +57,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="split_screen_horizontally"/> <checkbox id="split_screen_horizontally"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Multiplayer splits screen horizontally" word_wrap="true"/> <label height="100%" I18N="In the ui settings" text="Multiplayer splits screen horizontally" word_wrap="true"/>
@ -53,7 +65,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="perPlayerDifficulty"/> <checkbox id="perPlayerDifficulty"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Enable per-player handicaps" word_wrap="true"/> <label height="100%" I18N="In the ui settings" text="Enable per-player handicaps" word_wrap="true"/>
@ -61,7 +73,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="enable-internet"/> <checkbox id="enable-internet"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Connect to the Internet" word_wrap="true"/> <label height="100%" I18N="In the ui settings" text="Connect to the Internet" word_wrap="true"/>
@ -69,7 +81,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="enable-hw-report"/> <checkbox id="enable-hw-report"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" id="label-hw-report" I18N="In the ui settings" <label height="100%" id="label-hw-report" I18N="In the ui settings"
@ -78,7 +90,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="show-login"/> <checkbox id="show-login"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Always show login screen" word_wrap="true"/> <label height="100%" I18N="In the ui settings" text="Always show login screen" word_wrap="true"/>
@ -86,7 +98,7 @@
<spacer width="5" height="2%"/> <spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="6%"> <div layout="horizontal-row" width="100%" height="fit">
<checkbox id="enable-lobby-chat"/> <checkbox id="enable-lobby-chat"/>
<spacer width="1%" height="100%" /> <spacer width="1%" height="100%" />
<label height="100%" id="label-lobby-chat" I18N="In the ui settings" text="Enable chatting in networking lobby" word_wrap="true"/> <label height="100%" id="label-lobby-chat" I18N="In the ui settings" text="Enable chatting in networking lobby" word_wrap="true"/>

View File

@ -7,89 +7,117 @@
<spacer width="1" height="2%"/> <spacer width="1" height="2%"/>
<box width="100%" height="40%" padding="10" layout="horizontal-row"> <div width="100%" height="73%" layout="horizontal-row">
<!-- Left pane --> <!-- Left pane -->
<div proportion="1" height="100%" layout="vertical-row"> <div width="34%" height="100%" layout="vertical-row">
<icon-button proportion="1" width="100%" height="100%" id="screenshot" custom_ratio="1.33333"/> <icon-button width="100%" height="55%" id="screenshot" custom_ratio="1.33333"/>
</div>
<!-- Right pane -->
<div proportion="1" height="100%" layout="vertical-row">
<label id="highscores" width="100%" text_align="center" text="= Highscores ="/>
<spacer width="1" height="2%"/> <spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row"> <!-- Misc. info box -->
<icon id="iconscore1" icon="gui/icons/random_kart.png" width="font" height="font"/> <box width="100%" height="43%" padding="10" layout="vertical-row">
<spacer width="2%" height="1"/> <spacer width="1" height="20%"/>
<label id="highscore1" proportion="1" text="(Empty)"/> <label id="author" width="100%" text_align="center" word_wrap="true"/>
</div> <spacer width="1" height="20%"/>
<label id="max-arena-players" width="100%" text_align="center" word_wrap="true"/>
<spacer width="1" height="2%"/> <spacer width="1" height="20%"/>
</box>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore2" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore2" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore3" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore3" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<label id="author" width="100%" text_align="center" word_wrap="true"/>
<spacer width="1" height="10%"/>
<label id="max-arena-players" width="100%" text_align="center" word_wrap="true"/>
</div> </div>
</box> <spacer width="2%" height="1"/>
<spacer width="1" height="1%"/>
<box width="100%" height="33%" layout="vertical-row"> <div width="64%" height="100%" layout="vertical-row">
<div width="100%" height="fit" layout="horizontal-row" > <!-- Right pane -->
<label id="lap-text" proportion="1" I18N="In the track info screen" text="Number of laps" text_align="right"/> <box width="100%" height="55%" padding="10" layout="vertical-row">
<spacer width="40"/> <label id="highscores" width="100%" text_align="center" text="= Highscores ="/>
<div proportion="1" height="fit" layout="horizontal-row">
<spinner id="lap-spinner" width="50%" min_value="1" max_value="20" align="center" <spacer width="1" height="2%"/>
wrap_around="true" />
</div> <div width="95%" height="fit" layout="horizontal-row">
</div> <icon id="iconscore1" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="1" height="1%"/> <spacer width="2%" height="1"/>
<div width="100%" height="fit" layout="horizontal-row" > <label id="highscore1" proportion="1" text="(Empty)"/>
<label id="ai-text" proportion="1" I18N="In the track info screen" text="Number of AI karts" text_align="right"/>
<spacer width="40"/>
<div proportion="1" height="fit" layout="horizontal-row">
<spinner id="ai-spinner" width="50%" min_value="1" max_value="20" align="center"
wrap_around="true" />
</div>
</div>
<spacer width="1" height="1%"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label id="option-text" proportion="1" I18N="In the track info screen" text_align="right"/>
<spacer width="40"/>
<div proportion="1" height="fit" layout="horizontal-row">
<div width="50%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="option" align="center"/>
</div> </div>
</div>
</div> <spacer width="1" height="2%"/>
<spacer width="1" height="1%"/>
<div width="100%" height="fit" layout="horizontal-row" > <div width="95%" height="fit" layout="horizontal-row">
<label id="record-race-text" proportion="1" I18N="In the track info screen" text="Record the race for ghost replay" text_align="right"/> <icon id="iconscore2" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="40"/> <spacer width="2%" height="1"/>
<div proportion="1" height="fit" layout="horizontal-row"> <label id="highscore2" proportion="1" text="(Empty)"/>
<div width="50%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="record" align="center"/>
</div> </div>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore3" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore3" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore4" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore4" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="2%"/>
<div width="95%" height="fit" layout="horizontal-row">
<icon id="iconscore5" icon="gui/icons/random_kart.png" width="font" height="font"/>
<spacer width="2%" height="1"/>
<label id="highscore5" proportion="1" text="(Empty)"/>
</div>
</box><!-- Highscores box -->
<spacer width="1" height="2%"/>
<!-- Race options box -->
<box width="100%" height="43%" layout="vertical-row">
<div width="100%" height="fit" layout="horizontal-row" >
<div proportion="1" height="fit" layout="horizontal-row">
<spinner id="lap-spinner" width="100%" min_value="1" max_value="20" align="center"
wrap_around="true" />
</div>
<spacer width="3%"/>
<label id="lap-text" proportion="4" I18N="In the track info screen" text="Number of laps" text_align="left"/>
</div>
<spacer width="1" height="2%"/>
<div width="100%" height="fit" layout="horizontal-row" >
<div proportion="1" height="fit" layout="horizontal-row">
<spinner id="ai-spinner" width="100%" min_value="1" max_value="20" align="center"
wrap_around="true" />
</div>
<spacer width="3%"/>
<label id="ai-text" proportion="4" I18N="In the track info screen" text="Number of AI karts" text_align="left"/>
</div>
<spacer width="1" height="2%"/>
<div width="100%" height="fit" layout="horizontal-row" >
<div proportion="1" height="fit" layout="horizontal-row">
<div width="100%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="option" align="center"/>
</div>
</div>
<spacer width="3%"/>
<label id="option-text" proportion="4" I18N="In the track info screen" text_align="left"/>
</div>
<spacer width="1" height="2%"/>
<div width="100%" height="fit" layout="horizontal-row" >
<div proportion="1" height="fit" layout="horizontal-row">
<div width="100%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="record" align="center"/>
</div>
</div>
<spacer width="3%"/>
<label id="record-race-text" proportion="4" I18N="In the track info screen" text="Record the race for ghost replay" text_align="left"/>
</div>
</box><!-- Race options box -->
</div> </div>
</box> </div>
<spacer width="1" height="1%"/> <spacer width="1" height="1%"/>
<buttonbar id="buttons" height="17%" width="100%" align="center"> <buttonbar id="buttons" height="17%" width="100%" align="center">
<icon-button id="start" width="64" height="64" icon="gui/icons/green_check.png" <icon-button id="start" width="64" height="64" icon="gui/icons/green_check.png"
I18N="In the track info screen" text="Start Race"/> I18N="In the track info screen" text="Start Race"/>

View File

@ -67,7 +67,7 @@
The actual turn radius is piece-wise linearly interpolated. This The actual turn radius is piece-wise linearly interpolated. This
allows for tighter turning at lower speeds, and also avoids that allows for tighter turning at lower speeds, and also avoids that
the kart becomes too hard to control at high speed (speeds of the kart becomes too hard to control at high speed (speeds of
higher than 23 can only be reached with powerups). higher than 25 can only be reached with powerups).
time-full-steer: This is the amount of change in steering depending time-full-steer: This is the amount of change in steering depending
on current steering. So if the steering is between 0 and 0.5, on current steering. So if the steering is between 0 and 0.5,
the time-for-steering-change is 0.15. If the current steering is the time-for-steering-change is 0.15. If the current steering is

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
<!-- Minimum and maxium server versions that be be read by this binary. <!-- Minimum and maxium server versions that be be read by this binary.
Older versions will be ignored. --> Older versions will be ignored. -->
<server-version min="1" max="1"/> <server-version min="2" max="2"/>
<!-- Maximum number of karts to be used at the same time. This limit <!-- Maximum number of karts to be used at the same time. This limit
can easily be increased, but some tracks might not have valid start can easily be increased, but some tracks might not have valid start

View File

@ -133,6 +133,20 @@ namespace scene
} }
} }
virtual void recursiveUpdateAbsolutePosition()
{
if (IsVisible)
{
// update absolute position
updateAbsolutePosition();
// perform the post render process on all children
ISceneNodeList::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
(*it)->recursiveUpdateAbsolutePosition();
}
}
//! Renders the node. //! Renders the node.
virtual void render() = 0; virtual void render() = 0;

View File

@ -249,6 +249,10 @@ void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
LastTimeMs = timeMs; LastTimeMs = timeMs;
IAnimatedMeshSceneNode::OnAnimate(timeMs); IAnimatedMeshSceneNode::OnAnimate(timeMs);
// For up-to-date current frame bone-child attachment
for (u32 n=0;n<JointChildSceneNodes.size();++n)
JointChildSceneNodes[n]->recursiveUpdateAbsolutePosition();
} }

View File

@ -121,6 +121,9 @@ void CMountPointReader::buildDirectory()
io::path full = list->getFullFileName(i); io::path full = list->getFullFileName(i);
full = full.subString(Path.size(), full.size() - Path.size()); full = full.subString(Path.size(), full.size() - Path.size());
if (full == "")
continue;
if (!list->isDirectory(i)) if (!list->isDirectory(i))
{ {
addItem(full, list->getFileOffset(i), list->getFileSize(i), false, RealFileNames.size()); addItem(full, list->getFileOffset(i), list->getFileSize(i), false, RealFileNames.size());

View File

@ -39,6 +39,7 @@ ChallengeData::ChallengeData(const std::string& filename)
m_mode = CM_SINGLE_RACE; m_mode = CM_SINGLE_RACE;
m_minor = RaceManager::MINOR_MODE_NORMAL_RACE; m_minor = RaceManager::MINOR_MODE_NORMAL_RACE;
m_num_laps = -1; m_num_laps = -1;
m_reverse = false;
m_track_id = ""; m_track_id = "";
m_gp_id = ""; m_gp_id = "";
m_version = 0; m_version = 0;
@ -182,6 +183,12 @@ ChallengeData::ChallengeData(const std::string& filename)
{ {
error("laps"); error("laps");
} }
if (!track_node->get("reverse", &m_reverse))
{
Log::warn("Challenge Data",
"No reverse mode specified for challenge %s, defaulting to normal",
filename.c_str());
}
} }
else if (gp_node != NULL) else if (gp_node != NULL)
{ {
@ -283,6 +290,10 @@ const irr::core::stringw ChallengeData::getChallengeDescription() const
// Follow the leader mode: // Follow the leader mode:
description = _("Follow the leader"); description = _("Follow the leader");
} }
if (m_reverse == true)
{
description += _("Reverse");
}
} }
return description; return description;
} // getChallengeDescription } // getChallengeDescription
@ -397,6 +408,7 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const
race_manager->setMinorMode(m_minor); race_manager->setMinorMode(m_minor);
race_manager->setTrack(m_track_id); race_manager->setTrack(m_track_id);
race_manager->setNumLaps(m_num_laps); race_manager->setNumLaps(m_num_laps);
race_manager->setReverseTrack(m_reverse);
race_manager->setNumKarts(m_default_num_karts[d]); race_manager->setNumKarts(m_default_num_karts[d]);
race_manager->setNumPlayers(1); race_manager->setNumPlayers(1);
race_manager->setCoinTarget(m_energy[d]); race_manager->setCoinTarget(m_energy[d]);
@ -423,7 +435,7 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const
if (m_is_ghost_replay) if (m_is_ghost_replay)
{ {
const bool result = ReplayPlay::get()->addReplayFile(file_manager const bool result = ReplayPlay::get()->addReplayFile(file_manager
->getAsset(FileManager::CHALLENGE, m_replay_files[d]), ->getAsset(FileManager::REPLAY, m_replay_files[d]),
true/*custom_replay*/); true/*custom_replay*/);
if (!result) if (!result)
Log::fatal("ChallengeData", "Can't open replay for challenge!"); Log::fatal("ChallengeData", "Can't open replay for challenge!");
@ -460,7 +472,7 @@ bool ChallengeData::isChallengeFulfilled() const
if (kart->isEliminated() ) return false; if (kart->isEliminated() ) return false;
if (track_name != m_track_id ) return false; if (track_name != m_track_id ) return false;
if ((int)world->getNumKarts() < m_default_num_karts[d] ) return false; if ((int)world->getNumKarts() < m_default_num_karts[d] ) return false;
if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false; if (m_energy[d] > 0 && kart->getEnergy() < m_energy[d] ) return false;
if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false; if (m_position[d] > 0 && kart->getPosition() > m_position[d]) return false;

View File

@ -94,6 +94,7 @@ private:
RaceManager::MinorRaceModeType m_minor; RaceManager::MinorRaceModeType m_minor;
int m_num_laps; int m_num_laps;
bool m_reverse;
int m_position[RaceManager::DIFFICULTY_COUNT]; int m_position[RaceManager::DIFFICULTY_COUNT];
int m_default_num_karts[RaceManager::DIFFICULTY_COUNT]; int m_default_num_karts[RaceManager::DIFFICULTY_COUNT];
std::string m_ai_kart_ident[RaceManager::DIFFICULTY_COUNT]; std::string m_ai_kart_ident[RaceManager::DIFFICULTY_COUNT];
@ -184,6 +185,9 @@ public:
return m_num_laps; return m_num_laps;
} // getNumLaps } // getNumLaps
// ------------------------------------------------------------------------
/** Return reverse mode. */
bool getReverse() const { return m_reverse; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Get number of required trophies to start this challenge */ /** Get number of required trophies to start this challenge */
int getNumTrophies() const { return m_num_trophies; } int getNumTrophies() const { return m_num_trophies; }

View File

@ -41,6 +41,7 @@ static std::vector<UserConfigParam*> all_params;
#include "utils/string_utils.hpp" #include "utils/string_utils.hpp"
#include "utils/translation.hpp" #include "utils/translation.hpp"
#include <algorithm>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <stdlib.h> #include <stdlib.h>
@ -53,9 +54,12 @@ const int UserConfig::m_current_config_version = 8;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
UserConfigParam::~UserConfigParam() UserConfigParam::~UserConfigParam()
{ {
// Now we have server config param so we cannot do this anymore, if (m_can_be_deleted)
// esp all params are kept until the closing of stk anyway {
//all_params.remove(this); auto it = std::find(all_params.begin(), all_params.end(), this);
if (it != all_params.end())
all_params.erase(it);
}
} // ~UserConfigParam } // ~UserConfigParam
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -66,6 +66,7 @@ class UserConfigParam
{ {
friend class GroupUserConfigParam; friend class GroupUserConfigParam;
protected: protected:
bool m_can_be_deleted = true;
std::string m_param_name; std::string m_param_name;
std::string m_comment; std::string m_comment;
public: public:
@ -761,7 +762,7 @@ namespace UserConfigParams
&m_network_group, "Use random port for server connection " &m_network_group, "Use random port for server connection "
"(check stk_config.xml for default value)")); "(check stk_config.xml for default value)"));
PARAM_PREFIX BoolUserConfigParam m_lobby_chat PARAM_PREFIX BoolUserConfigParam m_lobby_chat
PARAM_DEFAULT(BoolUserConfigParam(false, "lobby-chat", PARAM_DEFAULT(BoolUserConfigParam(true, "lobby-chat",
&m_network_group, "Enable chatting in networking lobby, if off than " &m_network_group, "Enable chatting in networking lobby, if off than "
"no chat message will be displayed from any players.")); "no chat message will be displayed from any players."));
PARAM_PREFIX IntUserConfigParam m_max_players PARAM_PREFIX IntUserConfigParam m_max_players
@ -902,6 +903,10 @@ namespace UserConfigParams
PARAM_DEFAULT( StringUserConfigParam("Peach.stkskin", "skin_file", PARAM_DEFAULT( StringUserConfigParam("Peach.stkskin", "skin_file",
"Name of the skin to use") ); "Name of the skin to use") );
PARAM_PREFIX IntUserConfigParam m_minimap_display
PARAM_DEFAULT(IntUserConfigParam(0, "minimap_display",
"Minimap: 0 bottom-left, 1 middle-right, 2 hidden"));
// ---- Handicap // ---- Handicap
PARAM_PREFIX GroupUserConfigParam m_handicap PARAM_PREFIX GroupUserConfigParam m_handicap
PARAM_DEFAULT( GroupUserConfigParam("Handicap", PARAM_DEFAULT( GroupUserConfigParam("Handicap",

View File

@ -23,6 +23,7 @@
#include <sstream> #include <sstream>
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/material.hpp" #include "graphics/material.hpp"
#include "graphics/particle_kind_manager.hpp" #include "graphics/particle_kind_manager.hpp"
#include "graphics/sp/sp_texture_manager.hpp" #include "graphics/sp/sp_texture_manager.hpp"
@ -57,7 +58,8 @@ MaterialManager::MaterialManager()
MaterialManager::~MaterialManager() MaterialManager::~MaterialManager()
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
SP::SPTextureManager::get()->stopThreads(); if (CVS->isGLSL())
SP::SPTextureManager::get()->stopThreads();
#endif #endif
for(unsigned int i=0; i<m_materials.size(); i++) for(unsigned int i=0; i<m_materials.size(); i++)

View File

@ -974,21 +974,39 @@ namespace GUIEngine
if (ScreenKeyboard::isActive()) ScreenKeyboard::dismiss(); if (ScreenKeyboard::isActive()) ScreenKeyboard::dismiss();
if (ModalDialog::isADialogActive()) ModalDialog::dismiss(); if (ModalDialog::isADialogActive()) ModalDialog::dismiss();
//delete g_font; if (g_font)
g_font->drop(); {
g_font = NULL; //delete g_font;
//delete g_title_font; g_font->drop();
g_title_font->drop(); g_font = NULL;
g_title_font = NULL; }
//delete g_small_font; if (g_title_font)
g_small_font->drop(); {
g_small_font = NULL; //delete g_title_font;
g_large_font->drop(); g_title_font->drop();
g_large_font = NULL; g_title_font = NULL;
g_digit_font->drop(); }
g_digit_font = NULL; if (g_small_font)
g_outline_font->drop(); {
g_outline_font = NULL; //delete g_small_font;
g_small_font->drop();
g_small_font = NULL;
}
if (g_large_font)
{
g_large_font->drop();
g_large_font = NULL;
}
if (g_digit_font)
{
g_digit_font->drop();
g_digit_font = NULL;
}
if (g_outline_font)
{
g_outline_font->drop();
g_outline_font = NULL;
}
// nothing else to delete for now AFAIK, irrlicht will automatically // nothing else to delete for now AFAIK, irrlicht will automatically
// kill everything along the device // kill everything along the device

View File

@ -34,8 +34,6 @@
#include "input/input_manager.hpp" #include "input/input_manager.hpp"
#include "modes/demo_world.hpp" #include "modes/demo_world.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_config.hpp"
#include "network/stk_host.hpp"
#include "states_screens/state_manager.hpp" #include "states_screens/state_manager.hpp"
#include "utils/debug.hpp" #include "utils/debug.hpp"
#include "utils/profiler.hpp" #include "utils/profiler.hpp"
@ -186,21 +184,6 @@ bool EventHandler::OnEvent (const SEvent &event)
SFXManager::get()->resumeAll(); SFXManager::get()->resumeAll();
} }
} }
else if (cmd == APP_CMD_TERM_WINDOW)
{
if (STKHost::existHost() && NetworkConfig::get()->isWAN())
{
STKHost::get()->requestShutdownDelayed(10000);
}
}
else if (cmd == APP_CMD_INIT_WINDOW)
{
if (STKHost::existHost() && NetworkConfig::get()->isWAN() &&
!STKHost::get()->requestedShutdown())
{
STKHost::get()->cancelShutdown();
}
}
else if (cmd == APP_CMD_LOW_MEMORY) else if (cmd == APP_CMD_LOW_MEMORY)
{ {
Log::warn("EventHandler", "Low memory event received"); Log::warn("EventHandler", "Low memory event received");

View File

@ -272,7 +272,9 @@ PlayerKartWidget::~PlayerKartWidget()
if (m_kart_name->getIrrlichtElement() != NULL) if (m_kart_name->getIrrlichtElement() != NULL)
m_kart_name->getIrrlichtElement()->remove(); m_kart_name->getIrrlichtElement()->remove();
getCurrentScreen()->manualRemoveWidget(this);
if (getCurrentScreen() != NULL)
getCurrentScreen()->manualRemoveWidget(this);
#ifdef DEBUG #ifdef DEBUG
m_magic_number = 0xDEADBEEF; m_magic_number = 0xDEADBEEF;

View File

@ -388,25 +388,26 @@ void MultitouchDevice::updateAxisY(float value)
*/ */
void MultitouchDevice::handleControls(MultitouchButton* button) void MultitouchDevice::handleControls(MultitouchButton* button)
{ {
if (m_controller == NULL) if (!isGameRunning())
return; return;
if (button->type == MultitouchButtonType::BUTTON_STEERING) if (button->type == MultitouchButtonType::BUTTON_ESCAPE)
{
updateAxisX(button->axis_x);
updateAxisY(button->axis_y);
}
else if (button->type == MultitouchButtonType::BUTTON_UP_DOWN)
{
updateAxisY(button->axis_y);
}
else if (button->type == MultitouchButtonType::BUTTON_ESCAPE)
{ {
StateManager::get()->escapePressed(); StateManager::get()->escapePressed();
} }
else
if (m_controller != NULL && !race_manager->isWatchingReplay())
{ {
if (button->action != PA_BEFORE_FIRST) if (button->type == MultitouchButtonType::BUTTON_STEERING)
{
updateAxisX(button->axis_x);
updateAxisY(button->axis_y);
}
else if (button->type == MultitouchButtonType::BUTTON_UP_DOWN)
{
updateAxisY(button->axis_y);
}
else if (button->action != PA_BEFORE_FIRST)
{ {
int value = button->pressed ? Input::MAX_VALUE : 0; int value = button->pressed ? Input::MAX_VALUE : 0;
m_controller->action(button->action, value); m_controller->action(button->action, value);
@ -416,6 +417,15 @@ void MultitouchDevice::handleControls(MultitouchButton* button)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool MultitouchDevice::isGameRunning()
{
return StateManager::get()->getGameState() == GUIEngine::GAME &&
!GUIEngine::ModalDialog::isADialogActive() &&
!GUIEngine::ScreenKeyboard::isActive();
}
// ----------------------------------------------------------------------------
void MultitouchDevice::updateController() void MultitouchDevice::updateController()
{ {
if (m_player == NULL) if (m_player == NULL)
@ -427,10 +437,7 @@ void MultitouchDevice::updateController()
// Handle multitouch events only when race is running. It avoids to process // Handle multitouch events only when race is running. It avoids to process
// it when pause dialog is active during the race. And there is no reason // it when pause dialog is active during the race. And there is no reason
// to use it for GUI navigation. // to use it for GUI navigation.
if (StateManager::get()->getGameState() != GUIEngine::GAME || if (!isGameRunning())
GUIEngine::ModalDialog::isADialogActive() ||
GUIEngine::ScreenKeyboard::isActive() ||
race_manager->isWatchingReplay())
{ {
m_controller = NULL; m_controller = NULL;
return; return;

View File

@ -94,6 +94,7 @@ private:
float getSteeringFactor(float value); float getSteeringFactor(float value);
void handleControls(MultitouchButton* button); void handleControls(MultitouchButton* button);
bool isGameRunning();
public: public:
/** The array that contains data for all multitouch input events */ /** The array that contains data for all multitouch input events */

View File

@ -631,3 +631,26 @@ std::string AssetsAndroid::getDataPath()
return ""; return "";
} }
//-----------------------------------------------------------------------------
/** Get a path for internal lib directory
* \return Path for internal lib directory or empty string when failed
*/
std::string AssetsAndroid::getLibPath()
{
#ifdef ANDROID
AndroidApplicationInfo application_info =
CIrrDeviceAndroid::getApplicationInfo(global_android_app->activity);
std::string lib_path = application_info.native_lib_dir;
if (access(lib_path.c_str(), R_OK) != 0)
{
lib_path = "";
}
return lib_path;
#endif
return "";
}

View File

@ -43,6 +43,7 @@ public:
void init(); void init();
static std::string getDataPath(); static std::string getDataPath();
static std::string getLibPath();
}; };

View File

@ -110,7 +110,8 @@ Attachment::~Attachment()
*/ */
void Attachment::set(AttachmentType type, int ticks, void Attachment::set(AttachmentType type, int ticks,
AbstractKart *current_kart, AbstractKart *current_kart,
bool disable_swatter_animation) bool disable_swatter_animation,
bool set_by_rewind_parachute)
{ {
// Don't override currently player swatter removing bomb animation // Don't override currently player swatter removing bomb animation
Swatter* s = dynamic_cast<Swatter*>(m_plugin); Swatter* s = dynamic_cast<Swatter*>(m_plugin);
@ -179,7 +180,8 @@ void Attachment::set(AttachmentType type, int ticks,
// A parachute can be attached as result of the usage of an item. In this // A parachute can be attached as result of the usage of an item. In this
// case we have to save the current kart speed so that it can be detached // case we have to save the current kart speed so that it can be detached
// by slowing down. // by slowing down.
if(m_type==ATTACH_PARACHUTE) // if set by rewind the parachute ticks is already correct
if (m_type == ATTACH_PARACHUTE && !set_by_rewind_parachute)
{ {
const KartProperties *kp = m_kart->getKartProperties(); const KartProperties *kp = m_kart->getKartProperties();
float speed_mult; float speed_mult;
@ -306,19 +308,16 @@ void Attachment::rewindTo(BareNetworkString *buffer)
// Attaching an object can be expensive (loading new models, ...) // Attaching an object can be expensive (loading new models, ...)
// so avoid doing this if there is no change in attachment type // so avoid doing this if there is no change in attachment type
// Don't use set to reset a model on local player if it's already cleared if (m_type == new_type)
// (or m_initial_speed is redone / model is re-shown again when rewinding)
if (m_type == new_type || m_type == ATTACH_NOTHING)
{ {
setTicksLeft(ticks_left); setTicksLeft(ticks_left);
if (m_type != new_type && new_type != ATTACH_SWATTER)
m_type = new_type;
return; return;
} }
set(new_type, ticks_left, m_previous_owner, set(new_type, ticks_left, m_previous_owner,
new_type == ATTACH_SWATTER && !is_removing_bomb new_type == ATTACH_SWATTER && !is_removing_bomb
/*disable_swatter_animation*/); /*disable_swatter_animation*/,
new_type == ATTACH_PARACHUTE);
} // rewindTo } // rewindTo
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -353,6 +352,12 @@ void Attachment::hitBanana(ItemState *item_state)
return; return;
} }
// Make it consistent with attachment rewind when eating banana with bomb
// see if (m_type == ATTACH_BOMB && m_kart->getKartAnimation() != NULL)
// in 515
if (m_kart->getKartAnimation())
return;
AttachmentType new_attachment = ATTACH_NOTHING; AttachmentType new_attachment = ATTACH_NOTHING;
const KartProperties *kp = m_kart->getKartProperties(); const KartProperties *kp = m_kart->getKartProperties();
// Use this as a basic random number to make sync with server easier. // Use this as a basic random number to make sync with server easier.
@ -529,13 +534,19 @@ void Attachment::update(int ticks)
m_node->setVisible((division & 0x1) == 0); m_node->setVisible((division & 0x1) == 0);
} }
if(m_plugin) if (m_plugin)
{ {
bool discard = m_plugin->updateAndTestFinished(ticks); int discard_ticks = m_plugin->updateAndTestFinished(ticks);
if(discard) if (discard_ticks != -1)
{ {
clear(); // also removes the plugin // Save it for rewinding
return; m_ticks_left =
discard_ticks - World::getWorld()->getTicksSinceStart();
if (m_ticks_left <= 0)
{
clear(); // also removes the plugin
return;
}
} }
} }

View File

@ -116,7 +116,8 @@ public:
void handleCollisionWithKart(AbstractKart *other); void handleCollisionWithKart(AbstractKart *other);
void set (AttachmentType type, int ticks, void set (AttachmentType type, int ticks,
AbstractKart *previous_kart=NULL, AbstractKart *previous_kart=NULL,
bool disable_swatter_animation = false); bool disable_swatter_animation = false,
bool set_by_rewind_parachute = false);
void rewindTo(BareNetworkString *buffer); void rewindTo(BareNetworkString *buffer);
void saveState(BareNetworkString *buffer) const; void saveState(BareNetworkString *buffer) const;

View File

@ -53,8 +53,9 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Updates a plugin. This is called once each time frame. If the /** Updates a plugin. This is called once each time frame. If the
* function returns true, the attachment is discarded. */ * function returns a non-negative number, the attachment is discarded
virtual bool updateAndTestFinished(int ticks) = 0; * when world ticks >= that number. */
virtual int updateAndTestFinished(int ticks) = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Called when the animation of the Attachment's node is done. */ /** Called when the animation of the Attachment's node is done. */

View File

@ -21,6 +21,7 @@
#include "items/item.hpp" #include "items/item.hpp"
#include "utils/vec3.hpp" #include "utils/vec3.hpp"
#include "utils/types.hpp"
#include <assert.h> #include <assert.h>

View File

@ -138,6 +138,10 @@ public:
virtual void collectedItem (ItemState *item, AbstractKart *kart); virtual void collectedItem (ItemState *item, AbstractKart *kart);
virtual void switchItems (); virtual void switchItems ();
bool randomItemsForArena(const AlignedArray<btTransform>& pos); bool randomItemsForArena(const AlignedArray<btTransform>& pos);
// ------------------------------------------------------------------------
/** Returns true if the items are switched atm. */
bool areItemsSwitched() { return (m_switch_ticks > 0); }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Only used in the NetworkItemManager. */ /** Only used in the NetworkItemManager. */
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer, virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,

View File

@ -256,7 +256,7 @@ BareNetworkString* Plunger::saveState(std::vector<std::string>* ru)
BareNetworkString* buffer = Flyable::saveState(ru); BareNetworkString* buffer = Flyable::saveState(ru);
buffer->addUInt16(m_keep_alive).addUInt8(m_moved_to_infinity ? 1 : 0); buffer->addUInt16(m_keep_alive).addUInt8(m_moved_to_infinity ? 1 : 0);
if (m_rubber_band) if (m_rubber_band)
buffer->addUInt8(m_rubber_band->getRubberBandTo()); buffer->addUInt8(m_rubber_band->get8BitState());
else else
buffer->addUInt8(255); buffer->addUInt8(255);
return buffer; return buffer;
@ -268,7 +268,20 @@ void Plunger::restoreState(BareNetworkString *buffer, int count)
Flyable::restoreState(buffer, count); Flyable::restoreState(buffer, count);
m_keep_alive = buffer->getUInt16(); m_keep_alive = buffer->getUInt16();
m_moved_to_infinity = buffer->getUInt8() == 1; m_moved_to_infinity = buffer->getUInt8() == 1;
int8_t rbt = buffer->getUInt8(); uint8_t bit_state = buffer->getUInt8();
if (rbt != -1 && m_rubber_band) if (bit_state == 255 && m_rubber_band)
m_rubber_band->setRubberBandTo((RubberBand::RubberBandTo)rbt); {
delete m_rubber_band;
m_rubber_band = NULL;
if (!m_reverse_mode)
m_reverse_mode = true;
}
else if (bit_state != 255 && !m_rubber_band)
{
m_rubber_band = new RubberBand(this, m_owner);
if (m_reverse_mode)
m_reverse_mode = false;
}
if (bit_state != 255)
m_rubber_band->set8BitState(bit_state);
} // restoreState } // restoreState

View File

@ -561,6 +561,11 @@ void Powerup::hitBonusBox(const ItemState &item_state)
new_powerup = powerup_manager->getRandomPowerup(position, &n, new_powerup = powerup_manager->getRandomPowerup(position, &n,
random_number); random_number);
// FIXME Disable switch and bubblegum for now in network
if (NetworkConfig::get()->isNetworking() &&
(new_powerup == PowerupManager::POWERUP_BUBBLEGUM ||
new_powerup == PowerupManager::POWERUP_SWITCH))
new_powerup = PowerupManager::POWERUP_BOWLING;
// Always add a new powerup in ITEM_MODE_NEW (or if the kart // Always add a new powerup in ITEM_MODE_NEW (or if the kart
// doesn't have a powerup atm). // doesn't have a powerup atm).

View File

@ -45,6 +45,7 @@
RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart)
: m_plunger(plunger), m_owner(kart) : m_plunger(plunger), m_owner(kart)
{ {
m_hit_kart = NULL;
m_attached_state = RB_TO_PLUNGER; m_attached_state = RB_TO_PLUNGER;
updatePosition(); updatePosition();
@ -276,6 +277,7 @@ void RubberBand::hit(AbstractKart *kart_hit, const Vec3 *track_xyz)
// ================= // =================
m_hit_position = *track_xyz; m_hit_position = *track_xyz;
m_attached_state = RB_TO_TRACK; m_attached_state = RB_TO_TRACK;
m_hit_kart = NULL;
} // hit } // hit
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -289,3 +291,24 @@ void RubberBand::remove()
} }
#endif #endif
} // remove } // remove
// ----------------------------------------------------------------------------
uint8_t RubberBand::get8BitState() const
{
uint8_t state = (uint8_t)(m_attached_state & 3);
state |= m_attached_state == RB_TO_KART && m_hit_kart ?
(m_hit_kart->getWorldKartId() << 3) : 0;
return state;
} // get8BitState
// ----------------------------------------------------------------------------
void RubberBand::set8BitState(uint8_t bit_state)
{
m_hit_kart = NULL;
m_attached_state = (RubberBandTo)(bit_state & 3);
if (m_attached_state == RB_TO_KART)
{
unsigned kart = bit_state >> 3;
m_hit_kart = World::getWorld()->getKart(kart);
}
} // set8BitState

View File

@ -73,8 +73,8 @@ public:
void updateGraphics(float dt); void updateGraphics(float dt);
void update(int ticks); void update(int ticks);
void hit(AbstractKart *kart_hit, const Vec3 *track_xyz=NULL); void hit(AbstractKart *kart_hit, const Vec3 *track_xyz=NULL);
RubberBandTo getRubberBandTo() const { return m_attached_state; } uint8_t get8BitState() const;
void setRubberBandTo(RubberBandTo rbt) { m_attached_state = rbt; } void set8BitState(uint8_t bit_state);
void remove(); void remove();
}; // RubberBand }; // RubberBand
#endif #endif

View File

@ -161,20 +161,20 @@ void Swatter::updateGrahpics(float dt)
/** Updates an armed swatter: it checks for any karts that are close enough /** Updates an armed swatter: it checks for any karts that are close enough
* and not invulnerable, it swats the kart. * and not invulnerable, it swats the kart.
* \param dt Time step size. * \param dt Time step size.
* \return True if the attachment should be discarded. * \return World ticks to discard the swatter.
*/ */
bool Swatter::updateAndTestFinished(int ticks) int Swatter::updateAndTestFinished(int ticks)
{ {
const int ticks_start = World::getWorld()->getTicksSinceStart(); const int ticks_start = World::getWorld()->getTicksSinceStart();
if (m_removed_bomb_ticks != std::numeric_limits<int>::max()) if (m_removed_bomb_ticks != std::numeric_limits<int>::max())
{ {
if (ticks_start >= m_removed_bomb_ticks) if (ticks_start >= m_removed_bomb_ticks)
return true; return m_removed_bomb_ticks;
return false; return -1;
} // if removing bomb } // if removing bomb
if (RewindManager::get()->isRewinding()) if (RewindManager::get()->isRewinding())
return false; return -1;
if (!m_discard_now) if (!m_discard_now)
{ {
@ -186,7 +186,7 @@ bool Swatter::updateAndTestFinished(int ticks)
// to make sure all clients know the existence of swatter each other // to make sure all clients know the existence of swatter each other
if (ticks_start - m_swatter_start_ticks < 60 || if (ticks_start - m_swatter_start_ticks < 60 ||
m_swatter_end_ticks - ticks_start < 60) m_swatter_end_ticks - ticks_start < 60)
return false; return -1;
chooseTarget(); chooseTarget();
pointToTarget(); pointToTarget();
@ -258,15 +258,15 @@ bool Swatter::updateAndTestFinished(int ticks)
if (m_discard_now) if (m_discard_now)
{ {
return ticks_start > m_end_swat_ticks; return m_end_swat_ticks;
} }
else if (ticks_start > m_end_swat_ticks) else if (ticks_start > m_end_swat_ticks)
{ {
m_animation_phase = SWATTER_AIMING; m_animation_phase = SWATTER_AIMING;
m_end_swat_ticks = std::numeric_limits<int>::max(); m_end_swat_ticks = std::numeric_limits<int>::max();
return false; return -1;
} }
return false; return -1;
} // updateAndTestFinished } // updateAndTestFinished
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -87,7 +87,7 @@ public:
scene::ISceneNode* bomb_scene_node, int ticks); scene::ISceneNode* bomb_scene_node, int ticks);
virtual ~Swatter(); virtual ~Swatter();
void updateGrahpics(float dt) OVERRIDE; void updateGrahpics(float dt) OVERRIDE;
bool updateAndTestFinished(int ticks) OVERRIDE; int updateAndTestFinished(int ticks) OVERRIDE;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns if the swatter is currently aiming, i.e. can be used to /** Returns if the swatter is currently aiming, i.e. can be used to

View File

@ -261,6 +261,8 @@ public:
/** Returns true if the kart has a plunger attached to its face. */ /** Returns true if the kart has a plunger attached to its face. */
virtual int getBlockedByPlungerTicks() const = 0; virtual int getBlockedByPlungerTicks() const = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual float getGraphicalViewBlockedByPlunger() const = 0;
// ------------------------------------------------------------------------
/** Sets that the view is blocked by a plunger. The duration depends on /** Sets that the view is blocked by a plunger. The duration depends on
* the difficulty, see KartPorperties getPlungerInFaceTime. */ * the difficulty, see KartPorperties getPlungerInFaceTime. */
virtual void blockViewWithPlunger() = 0; virtual void blockViewWithPlunger() = 0;
@ -274,6 +276,9 @@ public:
* \param slowdown Reduction of max speed. */ * \param slowdown Reduction of max speed. */
virtual void setSquash(float time, float slowdown) = 0; virtual void setSquash(float time, float slowdown) = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Makes the kart unsquashed again. */
virtual void unsetSquash() = 0;
// ------------------------------------------------------------------------
/** Returns the speed of the kart in meters/second. This is not declared /** Returns the speed of the kart in meters/second. This is not declared
* pure abstract, since this function is not needed for certain classes, * pure abstract, since this function is not needed for certain classes,
* like Ghost. */ * like Ghost. */

View File

@ -143,9 +143,19 @@ void AbstractKartAnimation::addNetworkAnimationChecker(bool reset_powerup)
{ {
// Prevent access to deleted kart animation object // Prevent access to deleted kart animation object
std::weak_ptr<int> cct = m_check_created_ticks; std::weak_ptr<int> cct = m_check_created_ticks;
Vec3 original_position;
AbstractKart* k = m_kart;
if (k)
original_position = k->getXYZ();
RewindManager::get()->addRewindInfoEventFunction(new RewindManager::get()->addRewindInfoEventFunction(new
RewindInfoEventFunction(m_created_ticks, RewindInfoEventFunction(m_created_ticks,
[](){}, /*undo_function*/[cct, k, original_position]()
{
auto cct_sp = cct.lock();
if (!cct_sp || !k)
return;
k->setXYZ(original_position);
},
/*replay_function*/[p]() /*replay_function*/[p]()
{ {
if (p) if (p)

View File

@ -30,6 +30,14 @@
class AbstractKart; class AbstractKart;
enum KartAnimationType : uint8_t
{
KAT_RESCUE = 0,
KAT_EXPLOSION_DIRECT_HIT = 1,
KAT_EXPLOSION = 2,
KAT_CANNON = 3
};
/** The base class for all kart animation, like rescue, explosion, or cannon. /** The base class for all kart animation, like rescue, explosion, or cannon.
* Kart animations are done by removing the physics body from the physics * Kart animations are done by removing the physics body from the physics
* world, and instead modifying the rotation and position of the kart * world, and instead modifying the rotation and position of the kart
@ -94,10 +102,12 @@ public:
m_end_transform = t; m_end_transform = t;
m_end_ticks = ticks; m_end_ticks = ticks;
} }
// ---------------------------------------------------------------------------- // ------------------------------------------------------------------------
void checkNetworkAnimationCreationSucceed(const btTransform& fallback_trans); void checkNetworkAnimationCreationSucceed(const btTransform& fb_trans);
// ---------------------------------------------------------------------------- // ------------------------------------------------------------------------
int getEndTicks() const { return m_end_ticks; } int getEndTicks() const { return m_end_ticks; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const = 0;
}; // AbstractKartAnimation }; // AbstractKartAnimation
#endif #endif

View File

@ -81,6 +81,8 @@ public:
virtual ~CannonAnimation(); virtual ~CannonAnimation();
virtual void update(int ticks); virtual void update(int ticks);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual bool usePredefinedEndTransform() const { return false; } virtual bool usePredefinedEndTransform() const { return false; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const { return KAT_CANNON; }
}; // CannonAnimation }; // CannonAnimation
#endif #endif

View File

@ -101,6 +101,8 @@ void NetworkAIController::convertAIToPlayerActions()
0 : 32768); 0 : 32768);
all_actions.emplace_back(PA_RESCUE, all_actions.emplace_back(PA_RESCUE,
m_ai_controls->getRescue() ? 32768 : 0); m_ai_controls->getRescue() ? 32768 : 0);
all_actions.emplace_back(PA_LOOK_BACK,
m_ai_controls->getLookBack() ? 32768 : 0);
for (const auto& a : all_actions) for (const auto& a : all_actions)
{ {

View File

@ -316,6 +316,9 @@ void SkiddingAI::update(int ticks)
int num_ai = m_world->getNumKarts() - race_manager->getNumPlayers(); int num_ai = m_world->getNumKarts() - race_manager->getNumPlayers();
int position_among_ai = m_kart->getPosition() - m_num_players_ahead; int position_among_ai = m_kart->getPosition() - m_num_players_ahead;
// Karts with boosted AI get a better speed cap value
if (m_kart->getBoostAI())
position_among_ai = 1;
float speed_cap = m_ai_properties->getSpeedCap(m_distance_to_player, float speed_cap = m_ai_properties->getSpeedCap(m_distance_to_player,
position_among_ai, position_among_ai,
@ -1063,7 +1066,6 @@ void SkiddingAI::evaluateItems(const ItemState *item, Vec3 kart_aim_direction,
* Level 2 to 5 AI : strategy detailed before each item * Level 2 to 5 AI : strategy detailed before each item
* Each successive level is overall stronger (5 the strongest, 2 the weakest of * Each successive level is overall stronger (5 the strongest, 2 the weakest of
* non-random strategies), but two levels may share a strategy for a given item. * non-random strategies), but two levels may share a strategy for a given item.
* (level 5 is not yet used ; meant for SuperTux GP preferred karts or boss races)
* \param dt Time step size. * \param dt Time step size.
* STATE: shield on -> avoid usage of offensive items (with certain tolerance) * STATE: shield on -> avoid usage of offensive items (with certain tolerance)
* STATE: swatter on -> avoid usage of shield * STATE: swatter on -> avoid usage of shield
@ -1361,7 +1363,7 @@ void SkiddingAI::handleBubblegum(int item_skill,
//if it is a bomb, wait : we may pass it to another kart before the timer runs out //if it is a bomb, wait : we may pass it to another kart before the timer runs out
if (item_skill == 5 && type == Attachment::ATTACH_BOMB) if (item_skill == 5 && type == Attachment::ATTACH_BOMB)
{ {
if (m_kart->getAttachment()->getTicksLeft() > stk_config->time2Ticks(3)) if (m_kart->getAttachment()->getTicksLeft() < stk_config->time2Ticks(2))
{ {
m_controls->setFire(true); m_controls->setFire(true);
m_controls->setLookBack(false); m_controls->setLookBack(false);
@ -1425,7 +1427,8 @@ void SkiddingAI::handleBubblegum(int item_skill,
if (abs_angle < 0.2f) straight_behind = true; if (abs_angle < 0.2f) straight_behind = true;
} }
if(m_distance_behind < 8.0f && straight_behind ) if(m_distance_behind < 8.0f && straight_behind &&
(!ItemManager::get()->areItemsSwitched() || item_skill < 4))
{ {
m_controls->setFire(true); m_controls->setFire(true);
m_controls->setLookBack(true); m_controls->setLookBack(true);

View File

@ -72,7 +72,7 @@ ExplosionAnimation *ExplosionAnimation::create(AbstractKart *kart)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ExplosionAnimation::ExplosionAnimation(AbstractKart *kart, ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
const Vec3 &explosion_position, const Vec3 &explosion_position,
bool direct_hit) bool direct_hit, bool from_state)
: AbstractKartAnimation(kart, "ExplosionAnimation") : AbstractKartAnimation(kart, "ExplosionAnimation")
{ {
m_direct_hit = direct_hit; m_direct_hit = direct_hit;
@ -148,7 +148,8 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
m_kart->getAttachment()->clear(); m_kart->getAttachment()->clear();
// Clear powerups when direct hit in CTF // Clear powerups when direct hit in CTF
addNetworkAnimationChecker(m_reset_ticks != -1); if (!from_state)
addNetworkAnimationChecker(m_reset_ticks != -1);
} // ExplosionAnimation } // ExplosionAnimation
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -74,9 +74,9 @@ protected:
Vec3 m_reset_xyz, m_reset_normal; Vec3 m_reset_xyz, m_reset_normal;
ExplosionAnimation(AbstractKart *kart); ExplosionAnimation(AbstractKart *kart);
ExplosionAnimation(AbstractKart *kart, const Vec3 &pos,
bool direct_hit);
public: public:
ExplosionAnimation(AbstractKart *kart, const Vec3 &pos,
bool direct_hit, bool from_state = false);
static ExplosionAnimation *create(AbstractKart *kart, const Vec3 &pos, static ExplosionAnimation *create(AbstractKart *kart, const Vec3 &pos,
bool direct_hit); bool direct_hit);
static ExplosionAnimation *create(AbstractKart *kart); static ExplosionAnimation *create(AbstractKart *kart);
@ -85,5 +85,8 @@ public:
virtual void update(int ticks); virtual void update(int ticks);
bool hasResetAlready() const bool hasResetAlready() const
{ return m_reset_ticks != -1 && m_timer < m_reset_ticks; } { return m_reset_ticks != -1 && m_timer < m_reset_ticks; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const
{ return m_direct_hit ? KAT_EXPLOSION_DIRECT_HIT : KAT_EXPLOSION; }
}; // ExplosionAnimation }; // ExplosionAnimation
#endif #endif

View File

@ -118,18 +118,9 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_max_speed = new MaxSpeed(this); m_max_speed = new MaxSpeed(this);
m_terrain_info = new TerrainInfo(); m_terrain_info = new TerrainInfo();
m_powerup = new Powerup(this); m_powerup = new Powerup(this);
m_last_used_powerup = PowerupManager::POWERUP_NOTHING;
m_vehicle = NULL; m_vehicle = NULL;
m_initial_position = position; m_initial_position = position;
m_race_position = position;
m_collected_energy = 0;
m_finished_race = false;
m_race_result = false; m_race_result = false;
m_finish_time = 0.0f;
m_bubblegum_ticks = 0;
m_bubblegum_torque = 0.0f;
m_invulnerable_ticks = 0;
m_squash_time = std::numeric_limits<float>::max();
m_shadow = NULL; m_shadow = NULL;
m_wheel_box = NULL; m_wheel_box = NULL;
@ -138,16 +129,13 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_skidmarks = NULL; m_skidmarks = NULL;
m_controller = NULL; m_controller = NULL;
m_saved_controller = NULL; m_saved_controller = NULL;
m_flying = false;
m_stars_effect = NULL; m_stars_effect = NULL;
m_is_jumping = false;
m_min_nitro_ticks = 0;
m_energy_to_min_ratio = 0;
m_consumption_per_tick = stk_config->ticks2Time(1) * m_consumption_per_tick = stk_config->ticks2Time(1) *
m_kart_properties->getNitroConsumption(); m_kart_properties->getNitroConsumption();
m_fire_clicked = 0; m_fire_clicked = 0;
m_boosted_ai = false; m_boosted_ai = false;
m_type = RaceManager::KT_AI; m_type = RaceManager::KT_AI;
m_flying = false;
m_xyz_history_size = stk_config->time2Ticks(XYZ_HISTORY_TIME); m_xyz_history_size = stk_config->time2Ticks(XYZ_HISTORY_TIME);
@ -157,17 +145,12 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_previous_xyz.push_back(initial_position); m_previous_xyz.push_back(initial_position);
m_previous_xyz_times.push_back(0.0f); m_previous_xyz_times.push_back(0.0f);
} }
m_time_previous_counter = 0.0f;
m_view_blocked_by_plunger = 0;
m_has_caught_nolok_bubblegum = false;
// Initialize custom sound vector (TODO: add back when properly done) // Initialize custom sound vector (TODO: add back when properly done)
// m_custom_sounds.resize(SFXManager::NUM_CUSTOMS); // m_custom_sounds.resize(SFXManager::NUM_CUSTOMS);
// Set position and heading: // Set position and heading:
m_reset_transform = init_transform; m_reset_transform = init_transform;
m_speed = 0.0f;
m_last_factor_engine_sound = 0.0f; m_last_factor_engine_sound = 0.0f;
m_kart_model->setKart(this); m_kart_model->setKart(this);
@ -202,7 +185,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_terrain_sound = NULL; m_terrain_sound = NULL;
m_last_sound_material = NULL; m_last_sound_material = NULL;
m_previous_terrain_sound = NULL; m_previous_terrain_sound = NULL;
m_graphical_view_blocked_by_plunger = 0.0f;
} // Kart } // Kart
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -316,7 +299,7 @@ Kart::~Kart()
*/ */
void Kart::reset() void Kart::reset()
{ {
if (m_flying) if (m_flying && !isGhostKart())
{ {
m_flying = false; m_flying = false;
stopFlying(); stopFlying();
@ -366,6 +349,9 @@ void Kart::reset()
m_collision_particles->setCreationRateAbsolute(0.0f); m_collision_particles->setCreationRateAbsolute(0.0f);
#endif #endif
unsetSquash();
m_last_used_powerup = PowerupManager::POWERUP_NOTHING;
m_race_position = m_initial_position; m_race_position = m_initial_position;
m_finished_race = false; m_finished_race = false;
m_eliminated = false; m_eliminated = false;
@ -373,8 +359,9 @@ void Kart::reset()
m_bubblegum_ticks = 0; m_bubblegum_ticks = 0;
m_bubblegum_torque = 0.0f; m_bubblegum_torque = 0.0f;
m_invulnerable_ticks = 0; m_invulnerable_ticks = 0;
m_min_nitro_ticks = 0;
m_energy_to_min_ratio = 0;
m_squash_time = std::numeric_limits<float>::max(); m_squash_time = std::numeric_limits<float>::max();
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
m_collected_energy = 0; m_collected_energy = 0;
m_bounce_back_ticks = 0; m_bounce_back_ticks = 0;
m_brake_ticks = 0; m_brake_ticks = 0;
@ -383,9 +370,14 @@ void Kart::reset()
m_current_lean = 0.0f; m_current_lean = 0.0f;
m_falling_time = 0.0f; m_falling_time = 0.0f;
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
m_has_caught_nolok_bubblegum = false; m_has_caught_nolok_bubblegum = false;
m_is_jumping = false; m_is_jumping = false;
m_flying = false;
m_startup_boost = 0.0f; m_startup_boost = 0.0f;
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
for (int i=0;i<m_xyz_history_size;i++) for (int i=0;i<m_xyz_history_size;i++)
{ {
m_previous_xyz[i] = getXYZ(); m_previous_xyz[i] = getXYZ();
@ -599,8 +591,15 @@ void Kart::blockViewWithPlunger()
{ {
// Avoid that a plunger extends the plunger time // Avoid that a plunger extends the plunger time
if(m_view_blocked_by_plunger<=0 && !isShielded()) if(m_view_blocked_by_plunger<=0 && !isShielded())
{
m_view_blocked_by_plunger = m_view_blocked_by_plunger =
stk_config->time2Ticks(m_kart_properties->getPlungerInFaceTime()); stk_config->time2Ticks(m_kart_properties->getPlungerInFaceTime());
if (m_graphical_view_blocked_by_plunger == 0.0f)
{
m_graphical_view_blocked_by_plunger =
m_kart_properties->getPlungerInFaceTime();
}
}
if(isShielded()) if(isShielded())
{ {
decreaseShieldTime(); decreaseShieldTime();
@ -884,22 +883,27 @@ float Kart::getSpeedForTurnRadius(float radius) const
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle // Convert the turn radius into turn angle
for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() / turn_angle_at_speed.setY(i, sin( 1.0 / turn_angle_at_speed.getY(i)));
turn_angle_at_speed.getY(i)));
float angle = sin(m_kart_properties->getWheelBase() / radius); float angle = sin(1.0 / radius);
return turn_angle_at_speed.getReverse(angle); return turn_angle_at_speed.getReverse(angle);
} // getSpeedForTurnRadius } // getSpeedForTurnRadius
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the maximum steering angle (depending on speed). */ /** Returns the maximum steering angle (depending on speed).
This is proportional to kart length because physics reverse this effect,
the results of this function should not be used to determine the
real raw steer angle. */
float Kart::getMaxSteerAngle(float speed) const float Kart::getMaxSteerAngle(float speed) const
{ {
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle // Convert the turn radius into turn angle
// We multiply by wheel base to keep turn radius identical
// across karts of different lengths sharing the same
// turn radius properties
for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() / turn_angle_at_speed.setY(i, sin( 1.0 / turn_angle_at_speed.getY(i))
turn_angle_at_speed.getY(i))); * m_kart_properties->getWheelBase());
return turn_angle_at_speed.get(speed); return turn_angle_at_speed.get(speed);
} // getMaxSteerAngle } // getMaxSteerAngle
@ -955,10 +959,16 @@ void Kart::finishedRace(float time, bool from_server)
World::getWorld()->getTicksSinceStart(), World::getWorld()->getTicksSinceStart(),
/*undo_function*/[old_controller, this]() /*undo_function*/[old_controller, this]()
{ {
if (m_network_finish_check_ticks == -1)
return;
m_controller = old_controller; m_controller = old_controller;
}, },
/*replay_function*/[ec, old_controller, this]() /*replay_function*/[ec, old_controller, this]()
{ {
if (m_network_finish_check_ticks == -1)
return;
m_saved_controller = old_controller; m_saved_controller = old_controller;
ec->reset(); ec->reset();
m_controller = ec; m_controller = ec;
@ -1068,9 +1078,8 @@ void Kart::setRaceResult()
} }
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL) else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
{ {
// the top kart wins
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld()); FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
m_race_result = ffa->getKartAtPosition(1) == this; m_race_result = ffa->getKartFFAResult(getWorldKartId());
} }
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG) else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
{ {
@ -1323,13 +1332,13 @@ void Kart::eliminate()
*/ */
void Kart::update(int ticks) void Kart::update(int ticks)
{ {
if (m_network_finish_check_ticks != 0 && if (m_network_finish_check_ticks > 0 &&
World::getWorld()->getTicksSinceStart() > World::getWorld()->getTicksSinceStart() >
m_network_finish_check_ticks && m_network_finish_check_ticks &&
!m_finished_race && m_saved_controller != NULL) !m_finished_race && m_saved_controller != NULL)
{ {
Log::warn("Kart", "Missing finish race from server."); Log::warn("Kart", "Missing finish race from server.");
m_network_finish_check_ticks = 0; m_network_finish_check_ticks = -1;
delete m_controller; delete m_controller;
m_controller = m_saved_controller; m_controller = m_saved_controller;
m_saved_controller = NULL; m_saved_controller = NULL;
@ -1432,7 +1441,10 @@ void Kart::update(int ticks)
if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= ticks; if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= ticks;
//unblock the view if kart just became shielded //unblock the view if kart just became shielded
if(isShielded()) if(isShielded())
{
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
}
// Decrease remaining invulnerability time // Decrease remaining invulnerability time
if(m_invulnerable_ticks>0) if(m_invulnerable_ticks>0)
{ {
@ -1672,6 +1684,7 @@ void Kart::update(int ticks)
if (emergency) if (emergency)
{ {
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
if (m_flying) if (m_flying)
{ {
stopFlying(); stopFlying();
@ -1831,6 +1844,31 @@ void Kart::setSquash(float time, float slowdown)
#endif #endif
} // setSquash } // setSquash
void Kart::unsetSquash()
{
#ifndef SERVER_ONLY
if (isGhostKart()) return;
m_squash_time = std::numeric_limits<float>::max();
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
if (m_vehicle && m_vehicle->getNumWheels() > 0)
{
scene::ISceneNode** wheels = m_kart_model->getWheelNodes();
scene::ISceneNode* node = m_kart_model->getAnimatedNode() ?
m_kart_model->getAnimatedNode() : m_node;
for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); i++)
{
if (wheels[i])
{
wheels[i]->setParent(node);
}
}
}
#endif
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Returns if the kart is currently being squashed /** Returns if the kart is currently being squashed
*/ */
@ -3020,22 +3058,13 @@ void Kart::updateGraphics(float dt)
// If squasing time ends, reset the model // If squasing time ends, reset the model
if (m_squash_time <= 0.0f || !isSquashed()) if (m_squash_time <= 0.0f || !isSquashed())
{ {
m_squash_time = std::numeric_limits<float>::max(); unsetSquash();
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
scene::ISceneNode* node =
m_kart_model->getAnimatedNode() ?
m_kart_model->getAnimatedNode() : m_node;
if (m_vehicle->getNumWheels() > 0)
{
scene::ISceneNode **wheels = m_kart_model->getWheelNodes();
for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); ++i)
{
if (wheels[i])
wheels[i]->setParent(node);
}
}
} }
} // if squashed } // if squashed
if (m_graphical_view_blocked_by_plunger > 0.0f)
m_graphical_view_blocked_by_plunger -= dt;
if (m_graphical_view_blocked_by_plunger < 0.0f)
m_graphical_view_blocked_by_plunger = 0.0f;
#endif #endif
for (int i = 0; i < EMITTER_COUNT; i++) for (int i = 0; i < EMITTER_COUNT; i++)

View File

@ -223,6 +223,9 @@ protected:
/** When a kart has its view blocked by the plunger, this variable will be /** 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. */ * > 0 the number it contains is the time left before removing plunger. */
int16_t m_view_blocked_by_plunger; int16_t m_view_blocked_by_plunger;
float m_graphical_view_blocked_by_plunger;
/** The current speed (i.e. length of velocity vector) of this kart. */ /** The current speed (i.e. length of velocity vector) of this kart. */
float m_speed; float m_speed;
@ -313,6 +316,7 @@ public:
virtual void handleZipper (const Material *m=NULL, virtual void handleZipper (const Material *m=NULL,
bool play_sound=false) OVERRIDE; bool play_sound=false) OVERRIDE;
virtual void setSquash (float time, float slowdown) OVERRIDE; virtual void setSquash (float time, float slowdown) OVERRIDE;
virtual void unsetSquash () OVERRIDE;
virtual void crashed (AbstractKart *k, bool update_attachments) OVERRIDE; virtual void crashed (AbstractKart *k, bool update_attachments) OVERRIDE;
virtual void crashed (const Material *m, const Vec3 &normal) OVERRIDE; virtual void crashed (const Material *m, const Vec3 &normal) OVERRIDE;
@ -393,6 +397,9 @@ public:
virtual int getBlockedByPlungerTicks() const OVERRIDE virtual int getBlockedByPlungerTicks() const OVERRIDE
{ return m_view_blocked_by_plunger; } { return m_view_blocked_by_plunger; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual float getGraphicalViewBlockedByPlunger() const OVERRIDE
{ return m_graphical_view_blocked_by_plunger; }
// ------------------------------------------------------------------------
/** Sets that the view is blocked by a plunger. The duration depends on /** Sets that the view is blocked by a plunger. The duration depends on
* the difficulty, see KartPorperties getPlungerInFaceTime. */ * the difficulty, see KartPorperties getPlungerInFaceTime. */
virtual void blockViewWithPlunger() OVERRIDE; virtual void blockViewWithPlunger() OVERRIDE;

View File

@ -303,17 +303,12 @@ void KartProperties::load(const std::string &filename, const std::string &node)
m_gravity_center_shift.setZ(0); m_gravity_center_shift.setZ(0);
} }
// In older STK versions the physical wheels where moved 'wheel_radius' // The longer the kart,the bigger its turn radius if using an identical
// into the physical body (i.e. 'hypothetical' wheel shape would not // wheel base, exactly proportionally to its length.
// poke out of the physical shape). In order to make the karts a bit more // The wheel base is used to compensate this
// stable, the physical wheel position (i.e. location of raycast) were // We divide by 1.425 to have a default turn radius which conforms
// moved to be on the corner of the shape. In order to retain the same // closely (+-0,1%) with the specifications in kart_characteristics.xml
// steering behaviour, the wheel base (which in turn determines the m_wheel_base = fabsf(m_kart_model->getLength()/1.425f);
// turn angle at certain speeds) is shortened by 2*wheel_radius
// Wheel radius was always 0.25, and is now not used anymore, but in order
// to keep existing steering behaviour, the same formula is still
// used.
m_wheel_base = fabsf(m_kart_model->getLength() - 2*0.25f);
m_shadow_material = material_manager->getMaterialSPM(m_shadow_file, "", m_shadow_material = material_manager->getMaterialSPM(m_shadow_file, "",
"alphablend"); "alphablend");

View File

@ -21,7 +21,8 @@
#include "items/attachment.hpp" #include "items/attachment.hpp"
#include "items/powerup.hpp" #include "items/powerup.hpp"
#include "karts/abstract_kart.hpp" #include "karts/abstract_kart.hpp"
#include "karts/abstract_kart_animation.hpp" #include "karts/explosion_animation.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/controller/player_controller.hpp" #include "karts/controller/player_controller.hpp"
#include "karts/kart_properties.hpp" #include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp" #include "karts/max_speed.hpp"
@ -52,6 +53,7 @@ KartRewinder::KartRewinder(const std::string& ident,
*/ */
void KartRewinder::reset() void KartRewinder::reset()
{ {
m_last_animation_end_ticks = -1;
m_transfrom_from_network = m_transfrom_from_network =
btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
Kart::reset(); Kart::reset();
@ -138,7 +140,9 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
buffer->add(trans.getOrigin()); buffer->add(trans.getOrigin());
btQuaternion quat = trans.getRotation(); btQuaternion quat = trans.getRotation();
buffer->add(quat); buffer->add(quat);
buffer->addUInt32(ka->getEndTicks()); unsigned et = ka->getEndTicks() & 134217727;
et |= ka->getAnimationType() << 27;
buffer->addUInt32(et);
} }
else else
{ {
@ -207,10 +211,38 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
if (has_animation) if (has_animation)
{ {
int end_ticks = buffer->getUInt32(); unsigned et = buffer->getUInt32();
AbstractKartAnimation* ka = getKartAnimation(); int end_ticks = et & 134217727;
if (ka) KartAnimationType kat = (KartAnimationType)(et >> 27);
ka->setEndTransformTicks(m_transfrom_from_network, end_ticks); if (!getKartAnimation() && end_ticks != m_last_animation_end_ticks)
{
Log::info("KartRewinder", "Creating animation %d from state", kat);
switch (kat)
{
case KAT_RESCUE:
new RescueAnimation(this, false/*is_auto_rescue*/,
true/*from_state*/);
break;
case KAT_EXPLOSION_DIRECT_HIT:
new ExplosionAnimation(this, getSmoothedXYZ(),
true/*direct_hit*/, true/*from_state*/);
break;
case KAT_EXPLOSION:
new ExplosionAnimation(this, getSmoothedXYZ(),
false/*direct_hit*/, true/*from_state*/);
break;
default:
Log::warn("KartRewinder", "Unknown animation %d from state",
kat);
break;
}
}
if (getKartAnimation())
{
getKartAnimation()->setEndTransformTicks(m_transfrom_from_network,
end_ticks);
}
m_last_animation_end_ticks = end_ticks;
} }
Vec3 lv = buffer->getVec3(); Vec3 lv = buffer->getVec3();

View File

@ -33,6 +33,7 @@ private:
btTransform m_transfrom_from_network; btTransform m_transfrom_from_network;
float m_prev_steering, m_steering_smoothing_dt, m_steering_smoothing_time; float m_prev_steering, m_steering_smoothing_dt, m_steering_smoothing_time;
int m_last_animation_end_ticks;
public: public:
KartRewinder(const std::string& ident, unsigned int world_kart_id, KartRewinder(const std::string& ident, unsigned int world_kart_id,
int position, const btTransform& init_transform, int position, const btTransform& init_transform,

View File

@ -40,7 +40,8 @@
* and initialised the timer. * and initialised the timer.
* \param kart Pointer to the kart which is animated. * \param kart Pointer to the kart which is animated.
*/ */
RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue) RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue,
bool from_state)
: AbstractKartAnimation(kart, "RescueAnimation") : AbstractKartAnimation(kart, "RescueAnimation")
{ {
btTransform prev_trans = kart->getTrans(); btTransform prev_trans = kart->getTrans();
@ -89,8 +90,11 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
} }
// Clear powerups when rescue in CTF // Clear powerups when rescue in CTF
addNetworkAnimationChecker(race_manager->getMajorMode() == if (!from_state)
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG); {
addNetworkAnimationChecker(race_manager->getMajorMode() ==
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG);
}
} // RescueAnimation } // RescueAnimation
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -49,9 +49,13 @@ protected:
Referee *m_referee; Referee *m_referee;
public: public:
RescueAnimation(AbstractKart *kart, bool is_auto_rescue=false); RescueAnimation(AbstractKart *kart,
bool is_auto_rescue = false,
bool from_state = false);
float maximumHeight(); float maximumHeight();
virtual ~RescueAnimation(); virtual ~RescueAnimation();
virtual void update(int ticks); virtual void update(int ticks);
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const { return KAT_RESCUE; }
}; // RescueAnimation }; // RescueAnimation
#endif #endif

View File

@ -421,6 +421,7 @@ void Skidding::update(int ticks, bool is_on_ground,
m_predicted_curve->setHeading(m_kart->getHeading()); m_predicted_curve->setHeading(m_kart->getHeading());
float angle = m_kart->getMaxSteerAngle(SPEED) float angle = m_kart->getMaxSteerAngle(SPEED)
* fabsf(getSteeringFraction()); * fabsf(getSteeringFraction());
//FIXME : what is this for ?
float r = kp->getWheelBase() float r = kp->getWheelBase()
/ asin(angle)*1.0f; / asin(angle)*1.0f;

View File

@ -1841,7 +1841,7 @@ void main_abort()
{ {
if (main_loop) if (main_loop)
{ {
main_loop->abort(); main_loop->requestAbort();
} }
} }
#ifdef ANDROID #ifdef ANDROID
@ -1936,8 +1936,11 @@ int main(int argc, char *argv[] )
} }
else if (CommandLine::has("--lan-server", &s)) else if (CommandLine::has("--lan-server", &s))
{ {
ProfileWorld::disableGraphics(); if (no_graphics)
UserConfigParams::m_enable_sound = false; {
ProfileWorld::disableGraphics();
UserConfigParams::m_enable_sound = false;
}
NetworkConfig::get()->setIsServer(true); NetworkConfig::get()->setIsServer(true);
ServerConfig::m_server_name = s; ServerConfig::m_server_name = s;
ServerConfig::m_wan_server = false; ServerConfig::m_wan_server = false;
@ -2279,8 +2282,10 @@ int main(int argc, char *argv[] )
{ {
Log::closeOutputFiles(); Log::closeOutputFiles();
#endif #endif
#ifndef ANDROID
fclose(stderr); fclose(stderr);
fclose(stdout); fclose(stdout);
#endif
#ifndef WIN32 #ifndef WIN32
} }
#endif #endif
@ -2336,6 +2341,9 @@ static void cleanSuperTuxKart()
if(unlock_manager) delete unlock_manager; if(unlock_manager) delete unlock_manager;
Online::ProfileManager::destroy(); Online::ProfileManager::destroy();
GUIEngine::DialogQueue::deallocate(); GUIEngine::DialogQueue::deallocate();
GUIEngine::clear();
GUIEngine::cleanUp();
GUIEngine::clearScreenCache();
if(font_manager) delete font_manager; if(font_manager) delete font_manager;
// Now finish shutting down objects which a separate thread. The // Now finish shutting down objects which a separate thread. The
@ -2349,7 +2357,8 @@ static void cleanSuperTuxKart()
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (!ProfileWorld::isNoGraphics()) if (!ProfileWorld::isNoGraphics())
{ {
if(!NewsManager::get()->waitForReadyToDeleted(2.0f)) if (UserConfigParams::m_internet_status == Online::RequestManager::
IPERM_ALLOWED && !NewsManager::get()->waitForReadyToDeleted(2.0f))
{ {
Log::info("Thread", "News manager not stopping, exiting anyway."); Log::info("Thread", "News manager not stopping, exiting anyway.");
} }

View File

@ -65,7 +65,8 @@ LRESULT CALLBACK separateProcessProc(_In_ HWND hwnd, _In_ UINT uMsg,
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
MainLoop::MainLoop(unsigned parent_pid) MainLoop::MainLoop(unsigned parent_pid)
: m_abort(false), m_ticks_adjustment(0), m_parent_pid(parent_pid) : m_abort(false), m_request_abort(false), m_ticks_adjustment(0),
m_parent_pid(parent_pid)
{ {
m_curr_time = 0; m_curr_time = 0;
m_prev_time = 0; m_prev_time = 0;
@ -307,16 +308,22 @@ void MainLoop::run()
TranslateMessage(&msg); TranslateMessage(&msg);
DispatchMessage(&msg); DispatchMessage(&msg);
if (msg.message == WM_QUIT) if (msg.message == WM_QUIT)
m_abort = true; {
m_request_abort = true;
}
} }
// If parent is killed, abort the child main loop too // If parent is killed, abort the child main loop too
if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT) if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT)
m_abort = true; {
m_request_abort = true;
}
} }
#else #else
// POSIX equivalent // POSIX equivalent
if (m_parent_pid != 0 && getppid() != (int)m_parent_pid) if (m_parent_pid != 0 && getppid() != (int)m_parent_pid)
m_abort = true; {
m_request_abort = true;
}
#endif #endif
PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7);
@ -327,17 +334,28 @@ void MainLoop::run()
// Shutdown next frame if shutdown request is sent while loading the // Shutdown next frame if shutdown request is sent while loading the
// world // world
if (STKHost::existHost() && STKHost::get()->requestedShutdown()) if ((STKHost::existHost() && STKHost::get()->requestedShutdown()) ||
m_request_abort)
{ {
SFXManager::get()->quickSound("anvil"); bool exist_host = STKHost::existHost();
core::stringw msg = _("Server connection timed out."); core::stringw msg = _("Server connection timed out.");
if (!STKHost::get()->getErrorMessage().empty())
if (!m_request_abort)
{ {
msg = STKHost::get()->getErrorMessage(); if (!ProfileWorld::isNoGraphics())
{
SFXManager::get()->quickSound("anvil");
if (!STKHost::get()->getErrorMessage().empty())
{
msg = STKHost::get()->getErrorMessage();
}
}
}
if (exist_host == true)
{
STKHost::get()->shutdown();
} }
STKHost::get()->shutdown();
// In case the user opened a race pause dialog
GUIEngine::ModalDialog::dismiss();
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (CVS->isGLSL()) if (CVS->isGLSL())
@ -351,18 +369,31 @@ void MainLoop::run()
} }
#endif #endif
// In case the user opened a race pause dialog
GUIEngine::ModalDialog::dismiss();
if (World::getWorld()) if (World::getWorld())
{ {
race_manager->clearNetworkGrandPrixResult(); race_manager->clearNetworkGrandPrixResult();
race_manager->exitRace(); race_manager->exitRace();
} }
if (!ProfileWorld::isNoGraphics())
if (exist_host == true)
{ {
StateManager::get()->resetAndSetStack( if (!ProfileWorld::isNoGraphics())
NetworkConfig::get()->getResetScreens().data()); {
MessageQueue::add(MessageQueue::MT_ERROR, msg); StateManager::get()->resetAndSetStack(
NetworkConfig::get()->getResetScreens().data());
MessageQueue::add(MessageQueue::MT_ERROR, msg);
}
NetworkConfig::get()->unsetNetworking();
}
if (m_request_abort)
{
m_abort = true;
} }
NetworkConfig::get()->unsetNetworking();
} }
if (!m_abort) if (!m_abort)
@ -392,84 +423,96 @@ void MainLoop::run()
PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F);
Online::RequestManager::get()->update(frame_duration); Online::RequestManager::get()->update(frame_duration);
PROFILER_POP_CPU_MARKER(); PROFILER_POP_CPU_MARKER();
}
m_ticks_adjustment.lock(); m_ticks_adjustment.lock();
if (m_ticks_adjustment.getData() != 0) if (m_ticks_adjustment.getData() != 0)
{
if (m_ticks_adjustment.getData() > 0)
{ {
num_steps += m_ticks_adjustment.getData(); if (m_ticks_adjustment.getData() > 0)
m_ticks_adjustment.getData() = 0;
}
else if (m_ticks_adjustment.getData() < 0)
{
int new_steps = num_steps + m_ticks_adjustment.getData();
if (new_steps < 0)
{ {
num_steps = 0; num_steps += m_ticks_adjustment.getData();
m_ticks_adjustment.getData() = new_steps;
}
else
{
num_steps = new_steps;
m_ticks_adjustment.getData() = 0; m_ticks_adjustment.getData() = 0;
} }
} else if (m_ticks_adjustment.getData() < 0)
}
m_ticks_adjustment.unlock();
for(int i=0; i<num_steps; i++)
{
if (World::getWorld() && history->replayHistory())
history->updateReplay(World::getWorld()->getTicksSinceStart());
PROFILER_PUSH_CPU_MARKER("Protocol manager update",
0x7F, 0x00, 0x7F);
if (auto pm = ProtocolManager::lock())
{
pm->update(1);
}
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
if (World::getWorld()) updateRace(1);
PROFILER_POP_CPU_MARKER();
// We need to check again because update_race may have requested
// the main loop to abort; and it's not a good idea to continue
// since the GUI engine is no more to be called then.
if (m_abort) break;
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 int new_steps = num_steps + m_ticks_adjustment.getData();
World::getWorld()->updateTime(1); if (new_steps < 0)
{
num_steps = 0;
m_ticks_adjustment.getData() = new_steps;
}
else
{
num_steps = new_steps;
m_ticks_adjustment.getData() = 0;
}
}
}
m_ticks_adjustment.unlock();
for (int i = 0; i < num_steps; i++)
{
if (World::getWorld() && history->replayHistory())
{
history->updateReplay(
World::getWorld()->getTicksSinceStart());
}
PROFILER_PUSH_CPU_MARKER("Protocol manager update",
0x7F, 0x00, 0x7F);
if (auto pm = ProtocolManager::lock())
{
pm->update(1);
}
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
if (World::getWorld())
{
updateRace(1);
}
PROFILER_POP_CPU_MARKER();
// We need to check again because update_race may have requested
// the main loop to abort; and it's not a good idea to continue
// since the GUI engine is no more to be called then.
if (m_abort || m_request_abort)
break;
if (m_frame_before_loading_world)
{
m_frame_before_loading_world = false;
break; break;
} }
World::getWorld()->updateTime(1);
}
} // for i < num_steps
// Handle controller the last to avoid slow PC sending actions too late if (World::getWorld())
if (!m_abort) {
{ 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
// Handle controller the last to avoid slow PC sending actions too
// late
if (!ProfileWorld::isNoGraphics()) if (!ProfileWorld::isNoGraphics())
{ {
// User aborted (e.g. closed window) // User aborted (e.g. closed window)
bool abort = !irr_driver->getDevice()->run(); bool abort = !irr_driver->getDevice()->run();
if (abort) if (abort)
m_abort = true; {
m_request_abort = true;
}
} }
if (auto gp = GameProtocol::lock()) if (auto gp = GameProtocol::lock())
{
gp->sendActions(); gp->sendActions();
}
} }
PROFILER_POP_CPU_MARKER(); // MainLoop pop PROFILER_POP_CPU_MARKER(); // MainLoop pop
PROFILER_SYNC_FRAME(); PROFILER_SYNC_FRAME();
@ -482,12 +525,4 @@ void MainLoop::run()
} // run } // run
//-----------------------------------------------------------------------------
/** Set the abort flag, causing the mainloop to be left.
*/
void MainLoop::abort()
{
m_abort = true;
} // abort
/* EOF */ /* EOF */

View File

@ -32,6 +32,8 @@ private:
/** True if the main loop should exit. */ /** True if the main loop should exit. */
std::atomic_bool m_abort; std::atomic_bool m_abort;
std::atomic_bool m_request_abort;
/** True if the frame rate should be throttled. */ /** True if the frame rate should be throttled. */
bool m_throttle_fps; bool m_throttle_fps;
@ -48,7 +50,9 @@ public:
MainLoop(unsigned parent_pid); MainLoop(unsigned parent_pid);
~MainLoop(); ~MainLoop();
void run(); void run();
void abort(); /** Set the abort flag, causing the mainloop to be left. */
void abort() { m_abort = true; }
void requestAbort() { m_request_abort = true; }
void setThrottleFPS(bool throttle) { m_throttle_fps = throttle; } void setThrottleFPS(bool throttle) { m_throttle_fps = throttle; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns true if STK is to be stoppe. */ /** Returns true if STK is to be stoppe. */

View File

@ -196,3 +196,14 @@ video::SColor FreeForAll::getColor(unsigned int kart_id) const
{ {
return GUIEngine::getSkin()->getColor("font::normal"); return GUIEngine::getSkin()->getColor("font::normal");
} // getColor } // getColor
// ----------------------------------------------------------------------------
bool FreeForAll::getKartFFAResult(int kart_id) const
{
// the kart(s) which has the top score wins
AbstractKart* k = getKartAtPosition(1);
if (!k)
return false;
int top_score = getKartScore(k->getWorldKartId());
return getKartScore(kart_id) == top_score;
} // getKartFFAResult

View File

@ -66,6 +66,8 @@ public:
void setKartScoreFromServer(NetworkString& ns); void setKartScoreFromServer(NetworkString& ns);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
int getKartScore(int kart_id) const { return m_scores.at(kart_id); } int getKartScore(int kart_id) const { return m_scores.at(kart_id); }
// ------------------------------------------------------------------------
bool getKartFFAResult(int kart_id) const;
}; // FreeForAll }; // FreeForAll

View File

@ -598,7 +598,8 @@ void LinearWorld::getKartsDisplayInfo(
// Don't compare times when crossing the start line first // Don't compare times when crossing the start line first
if(laps_of_leader>0 && if(laps_of_leader>0 &&
(getTimeTicks() - getTicksAtLapForKart(kart->getWorldKartId())<5|| (getTimeTicks() - getTicksAtLapForKart(kart->getWorldKartId()) <
stk_config->time2Ticks(8) ||
rank_info.lap != laps_of_leader) && rank_info.lap != laps_of_leader) &&
raceHasLaps()) raceHasLaps())
{ // Display for 5 seconds { // Display for 5 seconds

View File

@ -229,8 +229,8 @@ void World::init()
global_player_id, race_manager->getKartType(i), global_player_id, race_manager->getKartType(i),
race_manager->getPlayerDifficulty(i)); race_manager->getPlayerDifficulty(i));
} }
new_kart->setBoostAI(race_manager->hasBoostedAI(i));
m_karts.push_back(new_kart); m_karts.push_back(new_kart);
} // for i } // for i
// Load other custom models if needed // Load other custom models if needed
@ -587,6 +587,9 @@ void World::onGo()
*/ */
void World::terminateRace() void World::terminateRace()
{ {
// In case the user opened paused dialog in network
GUIEngine::ModalDialog::dismiss();
m_schedule_pause = false; m_schedule_pause = false;
m_schedule_unpause = false; m_schedule_unpause = false;

View File

@ -93,6 +93,7 @@
*/ */
ServerLobby::ServerLobby() : LobbyProtocol(NULL) ServerLobby::ServerLobby() : LobbyProtocol(NULL)
{ {
m_last_success_poll_time.store(StkTime::getRealTimeMs() + 30000);
m_waiting_players_counts.store(0); m_waiting_players_counts.store(0);
m_server_owner_id.store(-1); m_server_owner_id.store(-1);
m_registered_for_once_only = false; m_registered_for_once_only = false;
@ -110,7 +111,7 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL)
m_result_ns = getNetworkString(); m_result_ns = getNetworkString();
m_result_ns->setSynchronous(true); m_result_ns->setSynchronous(true);
m_waiting_for_reset = false; m_waiting_for_reset = false;
m_server_id_online = 0; m_server_id_online.store(0);
} // ServerLobby } // ServerLobby
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -356,7 +357,7 @@ void ServerLobby::createServerIdFile()
if (!sid.empty() && !m_has_created_server_id_file) if (!sid.empty() && !m_has_created_server_id_file)
{ {
std::fstream fs; std::fstream fs;
sid += StringUtils::toString(m_server_id_online) + "_" + sid += StringUtils::toString(m_server_id_online.load()) + "_" +
StringUtils::toString(STKHost::get()->getPrivatePort()); StringUtils::toString(STKHost::get()->getPrivatePort());
fs.open(sid, std::ios::out); fs.open(sid, std::ios::out);
fs.close(); fs.close();
@ -384,6 +385,14 @@ void ServerLobby::asynchronousUpdate()
handlePendingConnection(); handlePendingConnection();
} }
if (NetworkConfig::get()->isWAN() &&
allowJoinedPlayersWaiting() && m_server_recovering.expired() &&
StkTime::getRealTimeMs() > m_last_success_poll_time.load() + 30000)
{
Log::warn("ServerLobby", "Trying auto server recovery.");
registerServer(false/*now*/);
}
switch (m_state.load()) switch (m_state.load())
{ {
case SET_PUBLIC_ADDRESS: case SET_PUBLIC_ADDRESS:
@ -420,7 +429,7 @@ void ServerLobby::asynchronousUpdate()
// Register this server with the STK server. This will block // Register this server with the STK server. This will block
// this thread, because there is no need for the protocol manager // this thread, because there is no need for the protocol manager
// to react to any requests before the server is registered. // to react to any requests before the server is registered.
if (registerServer()) if (registerServer(true/*now*/))
{ {
if (allowJoinedPlayersWaiting()) if (allowJoinedPlayersWaiting())
m_registered_for_once_only = true; m_registered_for_once_only = true;
@ -690,12 +699,56 @@ void ServerLobby::update(int ticks)
* ProtocolManager thread). The information about this client is added * ProtocolManager thread). The information about this client is added
* to the table 'server'. * to the table 'server'.
*/ */
bool ServerLobby::registerServer() bool ServerLobby::registerServer(bool now)
{ {
while (!m_server_unregistered.expired()) while (now && !m_server_unregistered.expired())
StkTime::sleep(1); StkTime::sleep(1);
Online::XMLRequest *request = new Online::XMLRequest(); // ========================================================================
class RegisterServerRequest : public Online::XMLRequest
{
private:
std::weak_ptr<ServerLobby> m_server_lobby;
protected:
virtual void afterOperation()
{
Online::XMLRequest::afterOperation();
const XMLNode* result = getXMLData();
std::string rec_success;
auto sl = m_server_lobby.lock();
if (!sl)
return;
if (result->get("success", &rec_success) &&
rec_success == "yes")
{
const XMLNode* server = result->getNode("server");
assert(server);
const XMLNode* server_info = server->getNode("server-info");
assert(server_info);
unsigned server_id_online = 0;
server_info->get("id", &server_id_online);
assert(server_id_online != 0);
Log::info("ServerLobby",
"Server %d is now online.", server_id_online);
sl->m_server_id_online.store(server_id_online);
sl->m_last_success_poll_time.store(StkTime::getRealTimeMs());
return;
}
Log::error("ServerLobby", "%s",
StringUtils::wideToUtf8(getInfo()).c_str());
// For auto server recovery wait 3 seconds for next try
// This sleep only the request manager thread
if (manageMemory())
StkTime::sleep(3000);
}
public:
RegisterServerRequest(bool now, std::shared_ptr<ServerLobby> sl)
: XMLRequest(!now/*manage memory*/), m_server_lobby(sl) {}
}; // RegisterServerRequest
RegisterServerRequest *request = new RegisterServerRequest(now,
std::dynamic_pointer_cast<ServerLobby>(shared_from_this()));
NetworkConfig::get()->setServerDetails(request, "create"); NetworkConfig::get()->setServerDetails(request, "create");
request->addParameter("address", m_server_address.getIP() ); request->addParameter("address", m_server_address.getIP() );
request->addParameter("port", m_server_address.getPort() ); request->addParameter("port", m_server_address.getPort() );
@ -715,29 +768,19 @@ bool ServerLobby::registerServer()
Log::info("ServerLobby", "Public server address %s", Log::info("ServerLobby", "Public server address %s",
m_server_address.toString().c_str()); m_server_address.toString().c_str());
request->executeNow(); if (now)
const XMLNode* result = request->getXMLData();
std::string rec_success;
if (result->get("success", &rec_success) && rec_success == "yes")
{ {
const XMLNode* server = result->getNode("server"); request->executeNow();
assert(server);
const XMLNode* server_info = server->getNode("server-info");
assert(server_info);
server_info->get("id", &m_server_id_online);
assert(m_server_id_online != 0);
Log::info("ServerLobby",
"Server %d is now online.", m_server_id_online);
delete request; delete request;
return true; if (m_server_id_online.load() == 0)
return false;
} }
else
irr::core::stringc error(request->getInfo().c_str()); {
Log::error("ServerLobby", "%s", error.c_str()); request->queue();
delete request; m_server_recovering = request->observeExistence();
return false; }
return true;
} // registerServer } // registerServer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -811,7 +854,7 @@ void ServerLobby::startSelection(const Event *event)
if (!allowJoinedPlayersWaiting()) if (!allowJoinedPlayersWaiting())
{ {
ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONNECTION); ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONNECTION);
if (NetworkConfig::get()->isWAN() ) if (NetworkConfig::get()->isWAN())
{ {
unregisterServer(false/*now*/); unregisterServer(false/*now*/);
} }
@ -903,7 +946,9 @@ void ServerLobby::checkIncomingConnectionRequests()
// First poll every 5 seconds. Return if no polling needs to be done. // First poll every 5 seconds. Return if no polling needs to be done.
const uint64_t POLL_INTERVAL = 5000; const uint64_t POLL_INTERVAL = 5000;
static uint64_t last_poll_time = 0; static uint64_t last_poll_time = 0;
if (StkTime::getRealTimeMs() < last_poll_time + POLL_INTERVAL) if (StkTime::getRealTimeMs() < last_poll_time + POLL_INTERVAL ||
StkTime::getRealTimeMs() > m_last_success_poll_time.load() + 30000 ||
m_server_id_online.load() == 0)
return; return;
// Keep the port open, it can be sent to anywhere as we will send to the // Keep the port open, it can be sent to anywhere as we will send to the
@ -927,12 +972,13 @@ void ServerLobby::checkIncomingConnectionRequests()
virtual void afterOperation() virtual void afterOperation()
{ {
Online::XMLRequest::afterOperation(); Online::XMLRequest::afterOperation();
const XMLNode *result = getXMLData(); const XMLNode* result = getXMLData();
std::string success; std::string success;
if (!result->get("success", &success) || success != "yes") if (!result->get("success", &success) || success != "yes")
{ {
Log::error("ServerLobby", "Cannot retrieve the list."); Log::error("ServerLobby", "Poll server request failed: %s",
StringUtils::wideToUtf8(getInfo()).c_str());
return; return;
} }
@ -942,6 +988,7 @@ void ServerLobby::checkIncomingConnectionRequests()
auto sl = m_server_lobby.lock(); auto sl = m_server_lobby.lock();
if (!sl) if (!sl)
return; return;
sl->m_last_success_poll_time.store(StkTime::getRealTimeMs());
if (sl->m_state.load() != WAITING_FOR_START_GAME && if (sl->m_state.load() != WAITING_FOR_START_GAME &&
!sl->allowJoinedPlayersWaiting()) !sl->allowJoinedPlayersWaiting())
{ {

View File

@ -95,6 +95,8 @@ private:
/** It indicates if this server is unregistered with the stk server. */ /** It indicates if this server is unregistered with the stk server. */
std::weak_ptr<bool> m_server_unregistered; std::weak_ptr<bool> m_server_unregistered;
std::weak_ptr<bool> m_server_recovering;
/** Timeout counter for various state. */ /** Timeout counter for various state. */
std::atomic<int64_t> m_timeout; std::atomic<int64_t> m_timeout;
@ -148,9 +150,11 @@ private:
std::atomic<uint32_t> m_waiting_players_counts; std::atomic<uint32_t> m_waiting_players_counts;
std::atomic<uint64_t> m_last_success_poll_time;
uint64_t m_server_started_at, m_server_delay; uint64_t m_server_started_at, m_server_delay;
unsigned m_server_id_online; std::atomic<uint32_t> m_server_id_online;
bool m_registered_for_once_only; bool m_registered_for_once_only;
@ -164,7 +168,7 @@ private:
// Track(s) votes // Track(s) votes
void playerVote(Event *event); void playerVote(Event *event);
void playerFinishedResult(Event *event); void playerFinishedResult(Event *event);
bool registerServer(); bool registerServer(bool now);
void finishedLoadingWorldClient(Event *event); void finishedLoadingWorldClient(Event *event);
void kickHost(Event* event); void kickHost(Event* event);
void changeTeam(Event* event); void changeTeam(Event* event);

View File

@ -51,6 +51,7 @@ FloatServerConfigParam::FloatServerConfigParam(float default_value,
const char* comment) const char* comment)
: FloatUserConfigParam(param_name, comment) : FloatUserConfigParam(param_name, comment)
{ {
m_can_be_deleted = false;
m_value = default_value; m_value = default_value;
m_default_value = default_value; m_default_value = default_value;
g_server_params.push_back(this); g_server_params.push_back(this);
@ -62,6 +63,7 @@ IntServerConfigParam::IntServerConfigParam(int default_value,
const char* comment) const char* comment)
: IntUserConfigParam(param_name, comment) : IntUserConfigParam(param_name, comment)
{ {
m_can_be_deleted = false;
m_value = default_value; m_value = default_value;
m_default_value = default_value; m_default_value = default_value;
g_server_params.push_back(this); g_server_params.push_back(this);
@ -73,6 +75,7 @@ BoolServerConfigParam::BoolServerConfigParam(bool default_value,
const char* comment) const char* comment)
: BoolUserConfigParam(param_name, comment) : BoolUserConfigParam(param_name, comment)
{ {
m_can_be_deleted = false;
m_value = default_value; m_value = default_value;
m_default_value = default_value; m_default_value = default_value;
g_server_params.push_back(this); g_server_params.push_back(this);
@ -84,6 +87,7 @@ StringServerConfigParam::StringServerConfigParam(std::string default_value,
const char* comment) const char* comment)
: StringUserConfigParam(param_name, comment) : StringUserConfigParam(param_name, comment)
{ {
m_can_be_deleted = false;
m_value = default_value; m_value = default_value;
m_default_value = default_value; m_default_value = default_value;
g_server_params.push_back(this); g_server_params.push_back(this);
@ -132,7 +136,7 @@ void loadServerConfigXML(const XMLNode* root)
return; return;
} }
int config_file_version = -1; /*int config_file_version = -1;
if (root->get("version", &config_file_version) < 1 || if (root->get("version", &config_file_version) < 1 ||
config_file_version < stk_config->m_min_server_version || config_file_version < stk_config->m_min_server_version ||
config_file_version > stk_config->m_max_server_version) config_file_version > stk_config->m_max_server_version)
@ -142,7 +146,7 @@ void loadServerConfigXML(const XMLNode* root)
delete root; delete root;
writeServerConfigToDisk(); writeServerConfigToDisk();
return; return;
} }*/
for (unsigned i = 0; i < g_server_params.size(); i++) for (unsigned i = 0; i < g_server_params.size(); i++)
g_server_params[i]->findYourDataInAChildOf(root); g_server_params[i]->findYourDataInAChildOf(root);

View File

@ -269,7 +269,7 @@ namespace ServerConfig
// ======================================================================== // ========================================================================
/** Server version, will be advanced if there are protocol changes. */ /** Server version, will be advanced if there are protocol changes. */
static const uint32_t m_server_version = 1; static const uint32_t m_server_version = 2;
// ======================================================================== // ========================================================================
void loadServerConfig(const std::string& path = ""); void loadServerConfig(const std::string& path = "");
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -308,7 +308,6 @@ void STKHost::init()
{ {
m_network_timer.store(StkTime::getRealTimeMs()); m_network_timer.store(StkTime::getRealTimeMs());
m_shutdown = false; m_shutdown = false;
m_shutdown_delay = 0;
m_authorised = false; m_authorised = false;
m_network = NULL; m_network = NULL;
m_exit_timeout.store(std::numeric_limits<uint64_t>::max()); m_exit_timeout.store(std::numeric_limits<uint64_t>::max());
@ -635,8 +634,7 @@ void STKHost::setErrorMessage(const irr::core::stringw &message)
{ {
if (!message.empty()) if (!message.empty())
{ {
irr::core::stringc s(message.c_str()); Log::error("STKHost", "%s", StringUtils::wideToUtf8(message).c_str());
Log::error("STKHost", "%s", s.c_str());
} }
m_error_message = message; m_error_message = message;
} // setErrorMessage } // setErrorMessage

View File

@ -117,9 +117,6 @@ private:
* triggers a shutdown of the STKHost (and the Protocolmanager). */ * triggers a shutdown of the STKHost (and the Protocolmanager). */
std::atomic_bool m_shutdown; std::atomic_bool m_shutdown;
/** Used as a timeout for shedule shutdown. */
std::atomic<uint64_t> m_shutdown_delay;
/** True if this local host is authorised to control a server. */ /** True if this local host is authorised to control a server. */
std::atomic_bool m_authorised; std::atomic_bool m_authorised;
@ -211,21 +208,8 @@ public:
void requestShutdown() void requestShutdown()
{ {
m_shutdown.store(true); m_shutdown.store(true);
m_shutdown_delay.store(0);
} // requestExit } // requestExit
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
void requestShutdownDelayed(int delay)
{
m_shutdown.store(true);
m_shutdown_delay.store(StkTime::getRealTimeMs() + delay);
}
//-------------------------------------------------------------------------
void cancelShutdown()
{
m_shutdown.store(false);
m_shutdown_delay.store(0);
}
//-------------------------------------------------------------------------
void shutdown(); void shutdown();
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
void sendPacketToAllPeersInServer(NetworkString *data, void sendPacketToAllPeersInServer(NetworkString *data,
@ -282,11 +266,7 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns true if a shutdown of the network infrastructure was /** Returns true if a shutdown of the network infrastructure was
* requested. */ * requested. */
bool requestedShutdown() const bool requestedShutdown() const { return m_shutdown.load(); }
{
return m_shutdown.load() &&
m_shutdown_delay.load() < StkTime::getRealTimeMs();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
int receiveRawPacket(char *buffer, int buffer_len, int receiveRawPacket(char *buffer, int buffer_len,
TransportAddress* sender, int max_tries = -1) TransportAddress* sender, int max_tries = -1)

View File

@ -326,7 +326,11 @@ namespace Online
setProgress(-1.0f); setProgress(-1.0f);
Request::afterOperation(); Request::afterOperation();
curl_easy_cleanup(m_curl_session); if (m_curl_session)
{
curl_easy_cleanup(m_curl_session);
m_curl_session = NULL;
}
} // afterOperation } // afterOperation
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -63,7 +63,7 @@ namespace Online
std::string m_filename; std::string m_filename;
/** Pointer to the curl data structure for this request. */ /** Pointer to the curl data structure for this request. */
CURL *m_curl_session; CURL *m_curl_session = NULL;
/** curl return code. */ /** curl return code. */
CURLcode m_curl_code; CURLcode m_curl_code;
@ -93,7 +93,14 @@ namespace Online
int priority = 1); int priority = 1);
HTTPRequest(const char * const filename, bool manage_memory = false, HTTPRequest(const char * const filename, bool manage_memory = false,
int priority = 1); int priority = 1);
virtual ~HTTPRequest() {} virtual ~HTTPRequest()
{
if (m_curl_session)
{
curl_easy_cleanup(m_curl_session);
m_curl_session = NULL;
}
}
virtual bool isAllowedToAdd() const OVERRIDE; virtual bool isAllowedToAdd() const OVERRIDE;
void setApiURL(const std::string& url, const std::string &action); void setApiURL(const std::string& url, const std::string &action);
void setAddonsURL(const std::string& path); void setAddonsURL(const std::string& path);

View File

@ -213,7 +213,13 @@ namespace Online
me->m_current_request->execute(); me->m_current_request->execute();
// This test is necessary in case that execute() was aborted // This test is necessary in case that execute() was aborted
// (otherwise the assert in addResult will be triggered). // (otherwise the assert in addResult will be triggered).
if (!me->getAbort()) me->addResult(me->m_current_request); if (!me->getAbort())
me->addResult(me->m_current_request);
else if (me->m_current_request->manageMemory())
{
delete me->m_current_request;
me->m_current_request = NULL;
}
me->m_request_queue.lock(); me->m_request_queue.lock();
} // while handle all requests } // while handle all requests

View File

@ -22,7 +22,6 @@
#include "LinearMath/btIDebugDraw.h" #include "LinearMath/btIDebugDraw.h"
#include "BulletDynamics/ConstraintSolver/btContactConstraint.h" #include "BulletDynamics/ConstraintSolver/btContactConstraint.h"
#include "config/stk_config.hpp"
#include "graphics/material.hpp" #include "graphics/material.hpp"
#include "karts/kart.hpp" #include "karts/kart.hpp"
#include "karts/kart_model.hpp" #include "karts/kart_model.hpp"
@ -51,12 +50,6 @@ btKart::btKart(btRigidBody* chassis, btVehicleRaycaster* raycaster,
Kart *kart) Kart *kart)
: m_vehicleRaycaster(raycaster) : m_vehicleRaycaster(raycaster)
{ {
btScalar height = 0;
for (int i=0;i<stk_config->time2Ticks(HISTORY_TIME);i++)
{
m_ground_height.push_back(height);
}
m_chassisBody = chassis; m_chassisBody = chassis;
m_indexRightAxis = 0; m_indexRightAxis = 0;
m_indexUpAxis = 1; m_indexUpAxis = 1;
@ -127,10 +120,6 @@ void btKart::reset()
wheel.m_raycastInfo.m_suspensionLength = 0; wheel.m_raycastInfo.m_suspensionLength = 0;
updateWheelTransform(i, true); updateWheelTransform(i, true);
} }
for (int i=0;i<stk_config->time2Ticks(HISTORY_TIME);i++)
{
m_ground_height[i] = 0;
}
m_visual_wheels_touch_ground = false; m_visual_wheels_touch_ground = false;
m_allow_sliding = false; m_allow_sliding = false;
m_num_wheels_on_ground = 0; m_num_wheels_on_ground = 0;
@ -141,7 +130,6 @@ void btKart::reset()
m_max_speed = -1.0f; m_max_speed = -1.0f;
m_min_speed = 0.0f; m_min_speed = 0.0f;
m_cushioning_disable_time = 0; m_cushioning_disable_time = 0;
m_new_ground_height = 999.9f;
// Set the brakes so that karts don't slide downhill // Set the brakes so that karts don't slide downhill
setAllBrakes(5.0f); setAllBrakes(5.0f);
@ -289,10 +277,10 @@ btScalar btKart::rayCast(unsigned int index, float fraction)
btScalar max_susp_len = wheel.getSuspensionRestLength() btScalar max_susp_len = wheel.getSuspensionRestLength()
+ wheel.m_maxSuspensionTravel; + wheel.m_maxSuspensionTravel;
// Do a longer raycast to see if the kart might soon hit the // Do a slightly longer raycast to see if the kart might soon hit the
// ground and some 'cushioning' is needed to avoid that the chassis // ground and some 'cushioning' is needed to avoid that the chassis
// hits the ground. // hits the ground.
btScalar raylen = max_susp_len*10 + 0.5f; btScalar raylen = max_susp_len + 0.5f;
btVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); btVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen);
const btVector3& source = wheel.m_raycastInfo.m_hardPointWS; const btVector3& source = wheel.m_raycastInfo.m_hardPointWS;
@ -308,9 +296,6 @@ btScalar btKart::rayCast(unsigned int index, float fraction)
wheel.m_raycastInfo.m_groundObject = 0; wheel.m_raycastInfo.m_groundObject = 0;
btScalar depth = raylen * rayResults.m_distFraction; btScalar depth = raylen * rayResults.m_distFraction;
if (depth < m_new_ground_height)
m_new_ground_height = depth;
if (object && depth < max_susp_len) if (object && depth < max_susp_len)
{ {
wheel.m_raycastInfo.m_contactNormalWS = rayResults.m_hitNormalInWorld; wheel.m_raycastInfo.m_contactNormalWS = rayResults.m_hitNormalInWorld;
@ -532,7 +517,6 @@ void btKart::updateVehicle( btScalar step )
if(m_cushioning_disable_time>0) m_cushioning_disable_time --; if(m_cushioning_disable_time>0) m_cushioning_disable_time --;
bool needed_cushioning = false; bool needed_cushioning = false;
btVector3 v = btVector3 v =
m_chassisBody->getVelocityInLocalPoint(m_wheelInfo[wheel_index] m_chassisBody->getVelocityInLocalPoint(m_wheelInfo[wheel_index]
.m_chassisConnectionPointCS); .m_chassisConnectionPointCS);
@ -543,7 +527,7 @@ void btKart::updateVehicle( btScalar step )
#ifdef DEBUG_CUSHIONING #ifdef DEBUG_CUSHIONING
Log::verbose("physics", Log::verbose("physics",
"World %d wheel %d lsuspl %f vdown %f overall speed %f length %f", "World %d wheel %d lsuspl %f vdown %f overall speed %f lenght %f",
World::getWorld()->getTimeTicks(), World::getWorld()->getTimeTicks(),
wheel_index, wheel_index,
m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength, m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength,
@ -551,14 +535,6 @@ void btKart::updateVehicle( btScalar step )
-v_down.getY() + 9.8*step, -v_down.getY() + 9.8*step,
step * (-v_down.getY() + 9.8*step)+offset); step * (-v_down.getY() + 9.8*step)+offset);
#endif #endif
// Update the ground height history
for (int i=stk_config->time2Ticks(HISTORY_TIME)-1;i>0;i--)
{
m_ground_height[i] = m_ground_height[i-1];
}
m_ground_height[0] = m_new_ground_height;
m_new_ground_height = 999.9f;
// If the kart is falling, estimate the distance the kart will fall // If the kart is falling, estimate the distance the kart will fall
// in the next time step: the speed gets increased by the gravity*dt. // in the next time step: the speed gets increased by the gravity*dt.
// This approximation is still not good enough (either because of // This approximation is still not good enough (either because of
@ -567,25 +543,19 @@ void btKart::updateVehicle( btScalar step )
// predict the upcoming collision correcty - so we add an offset // predict the upcoming collision correcty - so we add an offset
// to the predicted kart movement, which was found experimentally: // to the predicted kart movement, which was found experimentally:
btScalar gravity = m_chassisBody->getGravity().length(); btScalar gravity = m_chassisBody->getGravity().length();
btScalar predicted_fall = -v_down.getY() + gravity*step +
(m_ground_height[stk_config->time2Ticks(HISTORY_TIME)-1]
-m_ground_height[0])/stk_config->time2Ticks(HISTORY_TIME);
if (v_down.getY()<0 && m_cushioning_disable_time==0 && if (v_down.getY()<0 && m_cushioning_disable_time==0 &&
m_ground_height[0] < step*predicted_fall + offset && m_wheelInfo[wheel_index].m_raycastInfo.m_suspensionLength
m_ground_height[0] > 0 && < step * (-v_down.getY()+gravity*step)+offset)
m_ground_height[stk_config->time2Ticks(HISTORY_TIME)-1] > 0.4)
{ {
// Disable more cushioning for 1 second. This avoids the problem // Disable more cushioning for 1 second. This avoids the problem
// of hovering: a kart gets cushioned on a down-sloping area, still // of hovering: a kart gets cushioned on a down-sloping area, still
// moves forwards, gets cushioned again etc. --> kart is hovering // moves forwards, gets cushioned again etc. --> kart is hovering
// and not controllable. // and not controllable.
m_cushioning_disable_time = stk_config->time2Ticks(1); m_cushioning_disable_time = 120;
needed_cushioning = true; needed_cushioning = true;
btVector3 impulse = down * predicted_fall btVector3 impulse = down * (-v_down.getY() + gravity*step)
/ m_chassisBody->getInvMass(); / m_chassisBody->getInvMass();
#ifdef DEBUG_CUSHIONING #ifdef DEBUG_CUSHIONING
float v_old = m_chassisBody->getLinearVelocity().getY(); float v_old = m_chassisBody->getLinearVelocity().getY();
#endif #endif
@ -593,13 +563,12 @@ void btKart::updateVehicle( btScalar step )
#ifdef DEBUG_CUSHIONING #ifdef DEBUG_CUSHIONING
Log::verbose("physics", Log::verbose("physics",
"World %d Cushioning imp %f vdown %f from %f m/s to %f m/s " "World %d Cushioning imp %f vdown %f from %f m/s to %f m/s "
"height %f contact %f kart %f susp %f relspeed %f", "contact %f kart %f susp %f relspeed %f",
World::getWorld()->getTimeTicks(), World::getWorld()->getTimeTicks(),
impulse.getY(), impulse.getY(),
-v_down.getY(), -v_down.getY(),
v_old, v_old,
m_chassisBody->getLinearVelocity().getY(), m_chassisBody->getLinearVelocity().getY(),
m_ground_height[0],
m_wheelInfo[wheel_index].m_raycastInfo.m_isInContact ? m_wheelInfo[wheel_index].m_raycastInfo.m_isInContact ?
m_wheelInfo[wheel_index].m_raycastInfo.m_contactPointWS.getY() m_wheelInfo[wheel_index].m_raycastInfo.m_contactPointWS.getY()
: -100, : -100,

View File

@ -11,8 +11,6 @@
#ifndef BT_KART_HPP #ifndef BT_KART_HPP
#define BT_KART_HPP #define BT_KART_HPP
#include <vector>
#include "BulletDynamics/Dynamics/btRigidBody.h" #include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" #include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
#include "physics/btKartRaycast.hpp" #include "physics/btKartRaycast.hpp"
@ -119,13 +117,6 @@ private:
* physics steps, so need to be set again by the application. */ * physics steps, so need to be set again by the application. */
btScalar m_max_speed; btScalar m_max_speed;
/** Smallest wheel height to the ground, with a few frames of history */
std::vector<btScalar> m_ground_height;
const float HISTORY_TIME = 0.05f;
/** Used to get the smallest weel height in the current frame */
btScalar m_new_ground_height;
/** True if the visual wheels touch the ground. */ /** True if the visual wheels touch the ground. */
bool m_visual_wheels_touch_ground; bool m_visual_wheels_touch_ground;
@ -301,3 +292,4 @@ public:
}; // class btKart }; // class btKart
#endif //BT_KART_HPP #endif //BT_KART_HPP

View File

@ -354,13 +354,6 @@ void PhysicalObject::init(const PhysicalObject::Settings& settings)
case MP_EXACT: case MP_EXACT:
{ {
extend.setY(0); extend.setY(0);
// In case of readonly materials we have to get the material from
// the mesh, otherwise from the node. This is esp. important for
// water nodes, which only have the material defined in the node,
// but not in the mesh at all!
bool is_readonly_material = false;
scene::IMesh* mesh = NULL; scene::IMesh* mesh = NULL;
switch (presentation->getNode()->getType()) switch (presentation->getNode()->getType())
{ {
@ -371,7 +364,6 @@ void PhysicalObject::init(const PhysicalObject::Settings& settings)
scene::IMeshSceneNode *node = scene::IMeshSceneNode *node =
(scene::IMeshSceneNode*)presentation->getNode(); (scene::IMeshSceneNode*)presentation->getNode();
mesh = node->getMesh(); mesh = node->getMesh();
is_readonly_material = node->isReadOnlyMaterials();
break; break;
} }
case scene::ESNT_ANIMATED_MESH : case scene::ESNT_ANIMATED_MESH :
@ -380,7 +372,6 @@ void PhysicalObject::init(const PhysicalObject::Settings& settings)
scene::IAnimatedMeshSceneNode *node = scene::IAnimatedMeshSceneNode *node =
(scene::IAnimatedMeshSceneNode*)presentation->getNode(); (scene::IAnimatedMeshSceneNode*)presentation->getNode();
mesh = node->getMesh()->getMesh(0); mesh = node->getMesh()->getMesh(0);
is_readonly_material = node->isReadOnlyMaterials();
break; break;
} }
default: default:
@ -436,27 +427,27 @@ void PhysicalObject::init(const PhysicalObject::Settings& settings)
mb->getVertexType()); mb->getVertexType());
continue; continue;
} }
const video::SMaterial& irrMaterial = mb->getMaterial();
// Handle readonly materials correctly: mb->getMaterial can return std::string t1_full_path, t2_full_path;
// NULL if the node is not using readonly materials. E.g. in case video::ITexture* t1 = irrMaterial.getTexture(0);
// of a water scene node, the mesh (which is the animated copy of if (t1)
// the original mesh) does not contain any material information,
// the material is only available in the node.
const video::SMaterial &irrMaterial =
is_readonly_material ? mb->getMaterial()
: presentation->getNode()->getMaterial(i);
video::ITexture* t=irrMaterial.getTexture(0);
const Material* material=0;
if(t)
{ {
std::string image = t1_full_path = t1->getName().getPtr();
std::string(core::stringc(t->getName()).c_str()); t1_full_path = file_manager->getFileSystem()->getAbsolutePath(
material = material_manager t1_full_path.c_str()).c_str();
->getMaterial(StringUtils::getBasename(image));
if(material->isIgnore())
continue;
} }
video::ITexture* t2 = irrMaterial.getTexture(1);
if (t2)
{
t2_full_path = t2->getName().getPtr();
t2_full_path = file_manager->getFileSystem()->getAbsolutePath(
t2_full_path.c_str()).c_str();
}
const Material* material = material_manager->getMaterialSPM(
t1_full_path, t2_full_path);
if (material->isIgnore())
continue;
if (mb->getVertexType() == video::EVT_STANDARD) if (mb->getVertexType() == video::EVT_STANDARD)
{ {
irr::video::S3DVertex* mbVertices = irr::video::S3DVertex* mbVertices =

View File

@ -30,7 +30,7 @@
#include "utils/translation.hpp" #include "utils/translation.hpp"
HighscoreManager* highscore_manager=0; HighscoreManager* highscore_manager=0;
const unsigned int HighscoreManager::CURRENT_HSCORE_FILE_VERSION = 3; const unsigned int HighscoreManager::CURRENT_HSCORE_FILE_VERSION = 4;
HighscoreManager::HighscoreManager() HighscoreManager::HighscoreManager()
{ {

View File

@ -40,7 +40,7 @@ public:
typedef std::string HighscoreType; typedef std::string HighscoreType;
private: private:
enum {HIGHSCORE_LEN = 3}; // It's a top 3 list enum {HIGHSCORE_LEN = 5}; // It's a top 5 list
std::string m_track; std::string m_track;
HighscoreType m_highscore_type; HighscoreType m_highscore_type;
int m_number_of_karts; int m_number_of_karts;

View File

@ -327,6 +327,7 @@ void RaceManager::startNew(bool from_overworld)
m_num_ghost_karts = ReplayPlay::get()->getNumGhostKart(); m_num_ghost_karts = ReplayPlay::get()->getNumGhostKart();
m_started_from_overworld = from_overworld; m_started_from_overworld = from_overworld;
if (m_started_from_overworld) m_continue_saved_gp = false;
m_saved_gp = NULL; // There will be checks for this being NULL done later m_saved_gp = NULL; // There will be checks for this being NULL done later
if (m_major_mode==MAJOR_MODE_GRAND_PRIX) if (m_major_mode==MAJOR_MODE_GRAND_PRIX)
@ -510,6 +511,31 @@ void RaceManager::startNextRace()
} }
} // not first race } // not first race
// set boosted AI status for AI karts
int boosted_ai_count = std::min<int>(m_ai_kart_list.size(),
(m_kart_status.size()-2)/4 + 1);
if (boosted_ai_count > 4) boosted_ai_count = 4;
int ai_count = m_ai_kart_list.size();
for (unsigned int i=0;i<m_kart_status.size();i++)
{
if (m_kart_status[i].m_kart_type == KT_AI)
{
if (boosted_ai_count > 0 &&
(UserConfigParams::m_gp_most_points_first ||
ai_count == boosted_ai_count))
{
m_kart_status[i].m_boosted_ai = true;
boosted_ai_count--;
}
else
{
m_kart_status[i].m_boosted_ai = false;
}
ai_count--;
}
}
// the constructor assigns this object to the global // the constructor assigns this object to the global
// variable world. Admittedly a bit ugly, but simplifies // variable world. Admittedly a bit ugly, but simplifies
// handling of objects which get created in the constructor // handling of objects which get created in the constructor

Some files were not shown because too many files have changed in this diff Show More