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

This commit is contained in:
hiker
2018-10-19 23:26:39 +11:00
155 changed files with 4936 additions and 1864 deletions

View File

@@ -100,21 +100,17 @@ Tracks
- Mac_DMH : Minigolf
- Rubberduck : STK Enterprise
- Ponzino : Volcan Island
- Canis Lupus : Original 'Northern Resort' & Scotland
- Ivar ten Cate (Varivar) : Original 'Old Mine' &'Snow Peaks' Improvements
Tracks
- Thomas Oppl : Shiny Suburbs
- Canis Lupus : Original 'Northern Resort' & Scotland
- Ivar ten Cate (Varivar) : Original 'Old Mine' &'Snow Peaks' Improvements
- MacIO : Cave X
- Johannes Sjolund : The Island
- Wayne Dennis : Improvements to 'The Island'
- Steve and Oliver Baker : Oliver's Math Class
Karts
- JunglePenguin : Xue
- Minibjorn : Puffy
- Cheleb : Nolok, Pidgin
- Tob : elePHPant
- Néd J. Édoire : Wilber, Hexley
Karts

View File

@@ -1,69 +1,178 @@
<?xml version="1.0"?>
<!-- List of counters the achievements can query.
The format to use is <goal type="name_of_the_counter" value="X"/>
where X is the desired value of the counter ;e.g. <won_races value="10"/>
WARNING! If a goal node is malformed, it is ignored.
___________________________________________________________________________
S - Logical relations and subgoals
When you define multiple goals, the achievement will be completed
if they are all met, but they don't need to be met at once.
To have more possibilities, you can define subgoals and the
logical relationship they need to meet.
The available relations are :
AND // The subgoals have to be met, but not necessarily at once
AND-AT-ONCE // The subgoals have to be met at the same time.
OR // One of the subgoal has to be met
SUM // The subgoals sum must reach a certain (positive !) number.
The format to use for AND, AND-AT-ONCE and OR is :
<goal type="AND">
<goal type="name_of_counter" value="X"/>
<goal type="name_of_counter" value="Y"/>
</goal>
For SUM, it is :
<goal type="SUM" value="X">
<goal type="name_of_counter" operation="+"/>
<goal type="name_of_counter" operation="-"/>
</goal>
With the value of operation (+ or -) defining if the subgoal is added
or substracted from the total.
Sub-goals can also have their own sub-goals,
however a AND, AND-AT-ONCE or OR can't have a SUM goal for parent.
___________________________________________________________________________
I - Won races (normal, time-trial, FTL) counters.
Require to beat at least 3 AIs in any difficulty.
won-races : total number of races won
won-normal-races
won-tt-races
won-ftl-races
II - Consecutive won races counters.
These counters are reset if a race is restarted,
or if a race is lost even if the AI count to increment
is not met.
cons-won-races
cons-won-races-hard // This requires 5+ AIs and expert+ difficulty
III - Race started and finished counters, by difficulty (linear races only)
easy-started
easy-finished
medium-started
medium-finished
hard-started
hard-finished
best-started
best-finished
IV - Race started and finished counters, by game mode.
Races against a ghost are counted both in the base mode counter,
either time-trial or egg hunt, and in their own specific counter.
normal-started
normal-finished
tt-started
tt-finished
ftl-started
ftl-finished
three-strikes-started
three-strikes-finished
soccer-started
soccer-finished
egg-hunt-started
egg-hunt-finished
with-ghost-started
with-ghost-finished
ctf-started
ctf-finished
ffa-started
ffa-finished
IV - Counters related to powerup use. The hit counters reference how many
times the player kart has hit other karts, not how often it was hit.
powerup-used
powerup-used-1race
bowling-hit
bowling-hit-1race
swatter-hit
swatter-hit-1race
all-hits // hits from bowling ball, cake and swatter
all-hits-1race
hit-same-kart-1race
V - Counters related to other race events.
banana // counts how many banana the player's kart has hit
banana-1race
skidding
skidding-1race
skidding-1lap
VI - Per-track counters (at least one track reach the value)
race-started
race-finished
race-won
race-finished-reverse
race-finished-alone
less-laps
more-laps
twice-laps
egg-hunt-started
egg-hunt-finished
VII - Per-track counters (all non-addon tracks reach the value)
race-started-all
race-finished-all
race-won-all
race-finished-reverse-all
race-finished-alone-all
less-laps-all
more-laps-all
twice-laps-all
// For egg hunts, all non-addon tracks with egg hunt support
// must reach the value
egg-hunt-started-all
egg-hunt-finished-all
-->
<achievements>
<achievement id="1" check-type="all-at-least" reset-type="never"
name="Christoffel Columbus" description="Play every official track at least once." >
<candela_city goal="1"/>
<cocoa_temple goal="1"/>
<cornfield_crossing goal="1"/>
<fortmagma goal="1"/>
<gran_paradiso_island goal="1"/>
<greenvalley goal="1"/>
<hacienda goal="1"/>
<lighthouse goal="1"/>
<mansion goal="1"/>
<mines goal="1"/>
<minigolf goal="1"/>
<olivermath goal="1"/>
<sandtrack goal="1"/>
<scotland goal="1"/>
<snowmountain goal="1"/>
<snowtuxpeak goal="1"/>
<stk_enterprise goal="1"/>
<abyss goal="1"/>
<xr591 goal="1"/>
<zengarden goal="1"/>
<volcano_island goal="1"/>
</achievement>
<achievement id="2" check-type="all-at-least" reset-type="never"
name="Strike!" description="Hit 10 karts with a bowling-ball.">
<ball goal="10"/>
</achievement>
<achievement id="3" check-type="one-at-least" reset-type="race"
name="Arch Enemy" description="Hit the same kart at least 5 times in one race.">
<hit goal="5"/>
</achievement>
<achievement id="4" check-type="all-at-least" reset-type="race"
name="Marathoner" description="Make a race with 5 laps or more.">
<laps goal="5"/>
</achievement>
<achievement id="5" check-type="all-at-least" reset-type="lap"
name="Skid-row" description="Make 5 skidding in a single lap.">
<skidding goal="5"/>
</achievement>
<achievement id="6" check-type="all-at-least" reset-type="never"
name="Gold driver" description="Win against at least 3 opponents in all single player modes.">
<standard goal="1"/>
<std_timetrial goal="1"/>
<follow_leader goal="1"/>
<opponents goal="3"/>
</achievement>
<achievement id="7" check-type="all-at-least" reset-type="race"
name="Powerup Love" description="Use 10 or more powerups in a race.">
<poweruplover goal="10"/>
</achievement>
<achievement id="8" check-type="all-at-least" reset-type="never"
name="Unstoppable" description="Win 5 single races in a row.">
<wins goal="5"/>
</achievement>
<achievement id="9" check-type="all-at-least" reset-type="race"
name="Banana Lover" description="Collect at least 5 bananas in one race.">
<banana goal="5"/>
</achievement>
<achievement id="10" secret="yes" check-type="all-at-least" reset-type="race"
name="It's secret" description="Really ... a secret.">
</achievement>
<achievement id="11" check-type="all-at-least" reset-type="race"
name="Mosquito Hunter" description="Take your opponents for mosquitos! With the swatter, squash at least 5 of them in a race.">
<swatter goal="5"/>
</achievement>
<achievement id="1" name="Christoffel Columbus" description="Play every official track at least once." >
<goal type="race-finished-all" value="1"/>
</achievement>
<achievement id="2" name="Strike!" description="Hit 10 karts with a bowling-ball.">
<goal type="bowling-hit" value="10"/>
</achievement>
<achievement id="3" name="Arch Enemy" description="Hit the same kart at least 5 times in one race.">
<goal type="hit-same-kart-1race" value="5"/>
</achievement>
<achievement id="4" name="Marathoner" description="Finish a race with at least twice the track's default lap number.">
<goal type="twice-laps" value="1"/>
</achievement>
<achievement id="5" name="Skid-row" description="Make 5 skidding in a single lap.">
<goal type="skidding-1lap" value="5"/>
</achievement>
<achievement id="6" name="Gold driver" description="Win against at least 3 AIs in normal race, time-trial, and follow the leader.">
<goal type="won-normal-races" value="1"/>
<goal type="won-tt-races" value="1"/>
<goal type="won-ftl-races" value="1"/>
</achievement>
<achievement id="7" name="Powerup Love" description="Use 10 or more powerups in a race.">
<goal type="powerup-used-1race" value="10"/>
</achievement>
<achievement id="8" name="Unstoppable" description="Win 5 single races in a row against at least 3 AIs. Beware, restarting a race counts as a loss.">
<goal type="cons-won-races" value="5"/>
</achievement>
<achievement id="9" name="Banana Lover" description="Collect at least 5 bananas in one race.">
<goal type="banana-1race" value="5"/>
</achievement>
<achievement id="10" name="It's secret" description="Really ... a secret." secret="yes">
</achievement>
<achievement id="11" name="Mosquito Hunter" description="Take your opponents for mosquitos! With the swatter, squash them at least 5 times in a race.">
<goal type="swatter-hit-1race" value="5"/>
</achievement>
<achievement id="12" name="Beyond Luck" description="Win 10 single races in a row in Expert or SuperTux against at least 5 AIs. Beware, restarting a race counts as a loss.">
<goal type="cons-won-races-hard" value="10"/>
</achievement>
</achievements>

View File

@@ -3,7 +3,7 @@
<unlock_list list="false"/>
<track id="fortmagma" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/>
<requirements trophies="190"/>
<requirements trophies="190" challenges="25"/>
<best>
<karts number="2" aiIdent="nolok" superPower="nolokBoss"/>

View File

@@ -2,7 +2,7 @@
<challenge version="3">
<unlock_list list="false"/>
<grandprix id="3_tothemoonandback"/>
<mode major="grandprix" minor="quickrace"/>
<mode major="grandprix" minor="timetrial"/>
<requirements trophies="120"/>
<best>

View File

@@ -3,7 +3,7 @@
<unlock_list list="false"/>
<track id="lighthouse" laps="4" reverse="false"/>
<mode major="single" minor="quickrace"/>
<requirements trophies="125"/>
<requirements trophies="105"/>
<best>
<karts number="10"/>

View File

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

View File

@@ -2,12 +2,12 @@
<challenge version="3">
<unlock_list list="false"/>
<track id="stk_enterprise" laps="3" reverse="false"/>
<mode major="single" minor="quickrace"/>
<requirements trophies="105"/>
<mode major="single" minor="timetrial"/>
<requirements trophies="125"/>
<best>
<karts number="9"/>
<requirements position="1" time="155"/>
<requirements position="1" time="145"/>
</best>
<hard>
<karts number="8"/>

View File

@@ -4,5 +4,6 @@
<!-- This is the point equivalent of finishing the
story mode with all gold except 14 silver challenges -->
<requirements trophies="250"/>
<alt_requirements max-req-in-lower-diff="5"/>
<unlock difficulty="difficulty_best"/>
</challenge>

View File

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

View File

@@ -4,7 +4,7 @@
<track id="gran_paradiso_island" laps="3" reverse="false" />
<track id="greenvalley" laps="3" reverse="false" />
<track id="mansion" laps="3" reverse="false" />
<track id="stk_enterprise" laps="3" reverse="false" />
<track id="lighthouse" laps="3" reverse="false" />
<track id="candela_city" laps="3" reverse="false" />
</supertuxkart_grand_prix>

View File

@@ -1,10 +1,10 @@
<supertuxkart_grand_prix name="At World's End">
<track id="lighthouse" laps="4" reverse="false" />
<track id="snowmountain" laps="3" reverse="false" />
<track id="minigolf" laps="4" reverse="false" />
<track id="xr591" laps="3" reverse="false" />
<track id="mines" laps="3" reverse="false" />
<track id="stk_enterprise" laps="4" reverse="false" />
<track id="snowmountain" laps="3" reverse="false" />
<track id="minigolf" laps="4" reverse="false" />
<track id="xr591" laps="3" reverse="false" />
<track id="mines" laps="3" reverse="false" />
</supertuxkart_grand_prix>

View File

@@ -27,7 +27,7 @@
<label proportion="1" align="center" text_align="right" I18N="In the multitouch settings screen" text="Buttons scale"/>
<div proportion="1" align="center" height="fit" layout="horizontal-row" >
<spacer width="40" height="10" />
<gauge id="scale" proportion="1" min_value="50" max_value="150"/>
<gauge id="scale" proportion="1" min_value="80" max_value="160"/>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="2%" y="2%" width="100%" height="96%" layout="vertical-row">
<!-- The achievement's name is filled in the header at runtime -->
<header id="title" width="96%" height="10%" text_align="center"
word_wrap="true"/>
<spacer width="20" height="1%" />
<label id="description" width="90%" height="12%" align="center"
text_align="center" word_wrap="true" text=""/>
<spacer width="20" height="1%" />
<box width="96%" height="64%" align="center" layout="vertical-row"
padding="6">
<list id="progress-tree" x="0" y="0" width="100%" height="100%" word_wrap="true"/>
</box>
<spacer width="20" height="1%" />
<buttonbar id="options" width="90%" height="10%" align="center">
<icon-button id="ok" width="16" height="16"
icon="gui/icons/green_check.png" text="OK"
label_location="bottom"/>
</buttonbar>
<spacer width="20" height="1%" />
</div>
</stkgui>

View File

@@ -34,7 +34,7 @@
</tabs>
<box proportion="1" width="98%" align="center" layout="vertical-row" padding="6">
<list id="list_addons" x="0" y="0" width="100%" height="100%"/>
<list id="list_addons" x="0" y="0" width="100%" height="100%" word_wrap="true"/>
</box>
<bright width="97%" id="tips_label" text="" align="center"/>

View File

@@ -70,7 +70,7 @@
<spacer width="25" height="25"/>
<bubble proportion="2" height="100%" word_wrap="true"
I18N="In the help menu"
text="You can skid by pressing a special key or button. Short skids help to take sharp turns. If you skid long enough, you will get a boost. You can't stop turning while skidding, so orient your kart carefully before!"/>
text="You can skid by pressing a special key or button. Successive short skids help to take sharp turns; while medium skids will boost your speed, long skids more so. You can't stop turning while skidding, so orient your kart carefully before!"/>
</div>
<spacer height="3%" width="10"/>

View File

@@ -81,7 +81,7 @@
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"/>
<label id="lap-text" proportion="3" 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" >
@@ -90,7 +90,7 @@
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"/>
<label id="ai-text" proportion="3" 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" >
@@ -100,7 +100,7 @@
</div>
</div>
<spacer width="3%"/>
<label id="option-text" proportion="4" I18N="In the track info screen" text_align="left"/>
<label id="option-text" proportion="3" I18N="In the track info screen" text_align="left"/>
</div>
<spacer width="1" height="2%"/>
<div width="100%" height="fit" layout="horizontal-row" >
@@ -110,7 +110,7 @@
</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"/>
<label id="record-race-text" proportion="3" I18N="In the track info screen" text="Record the race for ghost replay" text_align="left"/>
</div>
</box><!-- Race options box -->
</div>

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: supertuxkart\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-26 14:50+0800\n"
"POT-Creation-Date: 2018-10-07 13:11+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -47,7 +47,7 @@ msgid "Marathoner"
msgstr ""
#. I18N: ./data/achievements.xml
msgid "Make a race with 5 laps or more."
msgid "Finish a race with at least twice the track's default lap number."
msgstr ""
#. I18N: ./data/achievements.xml
@@ -63,7 +63,8 @@ msgid "Gold driver"
msgstr ""
#. I18N: ./data/achievements.xml
msgid "Win against at least 3 opponents in all single player modes."
msgid ""
"Win against at least 3 AIs in normal race, time-trial, and follow the leader."
msgstr ""
#. I18N: ./data/achievements.xml
@@ -79,7 +80,9 @@ msgid "Unstoppable"
msgstr ""
#. I18N: ./data/achievements.xml
msgid "Win 5 single races in a row."
msgid ""
"Win 5 single races in a row against at least 3 AIs. Beware, restarting a "
"race counts as a loss."
msgstr ""
#. I18N: ./data/achievements.xml
@@ -104,8 +107,18 @@ msgstr ""
#. I18N: ./data/achievements.xml
msgid ""
"Take your opponents for mosquitos! With the swatter, squash at least 5 of "
"them in a race."
"Take your opponents for mosquitos! With the swatter, squash them at least 5 "
"times in a race."
msgstr ""
#. I18N: ./data/achievements.xml
msgid "Beyond Luck"
msgstr ""
#. I18N: ./data/achievements.xml
msgid ""
"Win 10 single races in a row in Expert or SuperTux against at least 5 AIs. "
"Beware, restarting a race counts as a loss."
msgstr ""
#. I18N: ./data/grandprix/1_penguinplayground.grandprix
@@ -233,7 +246,7 @@ msgstr ""
#: src/network/protocols/client_lobby.cpp:457
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:197
#: src/states_screens/edit_gp_screen.cpp:255
#: src/states_screens/ghost_replay_selection.cpp:380
#: src/states_screens/ghost_replay_selection.cpp:386
msgid "Yes"
msgstr ""
@@ -358,6 +371,7 @@ msgstr ""
#. I18N: ./data/gui/dialogs/general_text_field_dialog.stkgui
#. I18N: In the general textfield dialog
#. I18N: ./data/gui/dialogs/online/achievement_progress_dialog.stkgui
#. I18N: ./data/gui/dialogs/online/player_rankings_dialog.stkgui
#. I18N: ./data/gui/screens/edit_track.stkgui
#. I18N: ./data/gui/screens/online/register.stkgui
@@ -701,7 +715,7 @@ msgstr ""
#. I18N: Difficulty
#. I18N: ./data/gui/screens/race_setup.stkgui
#. I18N: Difficulty
#: src/race/race_manager.hpp:584
#: src/race/race_manager.hpp:586
msgid "Novice"
msgstr ""
@@ -713,7 +727,7 @@ msgstr ""
#. I18N: Difficulty
#. I18N: ./data/gui/screens/race_setup.stkgui
#. I18N: Difficulty
#: src/race/race_manager.hpp:585
#: src/race/race_manager.hpp:587
msgid "Intermediate"
msgstr ""
@@ -725,7 +739,7 @@ msgstr ""
#. I18N: Difficulty
#. I18N: ./data/gui/screens/race_setup.stkgui
#. I18N: Difficulty
#: src/race/race_manager.hpp:586
#: src/race/race_manager.hpp:588
msgid "Expert"
msgstr ""
@@ -735,7 +749,7 @@ msgstr ""
#. I18N: Difficulty
#. I18N: ./data/gui/screens/race_setup.stkgui
#. I18N: Difficulty
#: src/race/race_manager.hpp:587
#: src/race/race_manager.hpp:589
msgid "SuperTux"
msgstr ""
@@ -923,12 +937,12 @@ msgstr ""
#. I18N: ./data/gui/screens/ghost_replay_selection.stkgui
#. I18N: In the ghost replay selection screen
msgid "Hide multiplayer replays"
msgid "Only show replays matching the current version"
msgstr ""
#. I18N: ./data/gui/screens/ghost_replay_selection.stkgui
#. I18N: In the ghost replay selection screen
msgid "Only show replays matching the current version"
msgid "Hide multiplayer replays"
msgstr ""
#. I18N: ./data/gui/screens/ghost_replay_selection.stkgui
@@ -1703,7 +1717,7 @@ msgstr ""
#. I18N: ./data/gui/screens/online/server_selection.stkgui
#. I18N: In the server selection screen
msgid "Show only private server(s)"
msgid "Show private server(s)"
msgstr ""
#. I18N: ./data/gui/screens/online/server_selection.stkgui
@@ -2154,25 +2168,23 @@ msgid "Xue"
msgstr ""
#. I18N: ../stk-assets/tracks/abyss/track.xml
#. I18N: ../stk-assets/tracks/test/abyss/track.xml
msgid "Antediluvian Abyss"
msgstr ""
#. I18N: ../stk-assets/tracks/arena_candela_city/track.xml
#. I18N: ../stk-assets/tracks/candela_city/track.xml
msgid "Candela City"
msgstr ""
#. I18N: ../stk-assets/tracks/battleisland/track.xml
msgid "Battle Island"
msgstr ""
#. I18N: ../stk-assets/tracks/candela_city/track.xml
#. I18N: ../stk-assets/tracks/test/candela_city/track.xml
msgid "Candela City"
msgstr ""
#. I18N: ../stk-assets/tracks/cave/track.xml
msgid "Cave X"
msgstr ""
#. I18N: ../stk-assets/tracks/cocoa_temple/track.xml
#. I18N: ../stk-assets/tracks/test/cocoa_temple/track.xml
msgid "Cocoa Temple"
msgstr ""
@@ -2185,7 +2197,6 @@ msgid "Fort Magma"
msgstr ""
#. I18N: ../stk-assets/tracks/gran_paradiso_island/track.xml
#. I18N: ../stk-assets/tracks/test/gran_paradiso_island/track.xml
msgid "Gran Paradiso Island"
msgstr ""
@@ -2241,7 +2252,6 @@ msgid "Around the lighthouse"
msgstr ""
#. I18N: ../stk-assets/tracks/mansion/track.xml
#. I18N: ../stk-assets/tracks/test/mansion/track.xml
msgid "Blackhill Mansion"
msgstr ""
@@ -2301,7 +2311,7 @@ msgstr ""
msgid "Zen Garden"
msgstr ""
#: src/achievements/achievement.cpp:209
#: src/achievements/achievement.cpp:374
#, c-format
msgid "Completed achievement \"%s\"."
msgstr ""
@@ -2418,8 +2428,8 @@ msgstr ""
#. I18N: 'handicapped' indicates that per-player handicaps are
#. activated for this kart (i.e. it will drive slower)
#: src/guiengine/widgets/player_kart_widget.cpp:384
#: src/guiengine/widgets/player_kart_widget.cpp:756
#: src/guiengine/widgets/player_kart_widget.cpp:386
#: src/guiengine/widgets/player_kart_widget.cpp:758
#: src/karts/controller/local_player_controller.cpp:437
#: src/karts/controller/player_controller.cpp:408
#: src/network/protocols/client_lobby.cpp:691
@@ -2428,7 +2438,7 @@ msgstr ""
msgid "%s (handicapped)"
msgstr ""
#: src/guiengine/widgets/player_kart_widget.cpp:443
#: src/guiengine/widgets/player_kart_widget.cpp:445
#, c-format
msgid "%s is ready"
msgstr ""
@@ -3107,11 +3117,11 @@ msgstr ""
msgid "+1 life."
msgstr ""
#: src/karts/kart.cpp:981 src/karts/kart.cpp:986
#: src/karts/kart.cpp:1000 src/karts/kart.cpp:1005
msgid "You won the race!"
msgstr ""
#: src/karts/kart.cpp:986
#: src/karts/kart.cpp:1005
msgid "You finished the race!"
msgstr ""
@@ -3125,23 +3135,23 @@ msgid ""
"edit \"Connect to the Internet\" and \"Send anonymous HW statistics\")."
msgstr ""
#: src/main.cpp:2069
#: src/main.cpp:2072
msgid "Your screen resolution is too low to run STK."
msgstr ""
#: src/main.cpp:2098
#: src/main.cpp:2101
msgid ""
"Your driver version is too old. Please install the latest video drivers."
msgstr ""
#: src/main.cpp:2116
#: src/main.cpp:2119
#, c-format
msgid ""
"Your OpenGL version appears to be too old. Please verify if an update for "
"your video driver is available. SuperTuxKart requires %s or better."
msgstr ""
#: src/main_loop.cpp:334 src/network/protocols/client_lobby.cpp:84
#: src/main_loop.cpp:341 src/network/protocols/client_lobby.cpp:84
msgid "Server connection timed out."
msgstr ""
@@ -3177,7 +3187,7 @@ msgstr ""
msgid "The blue flag has returned!"
msgstr ""
#: src/modes/easter_egg_hunt.cpp:236
#: src/modes/easter_egg_hunt.cpp:237
#, c-format
msgid "Eggs: %d / %d"
msgstr ""
@@ -3216,11 +3226,11 @@ msgid_plural "%i spare tire karts have been spawned!"
msgstr[0] ""
msgstr[1] ""
#: src/modes/world.cpp:1276
#: src/modes/world.cpp:1238
msgid "You have been eliminated!"
msgstr ""
#: src/modes/world.cpp:1283
#: src/modes/world.cpp:1245
#, c-format
msgid "'%s' has been eliminated."
msgstr ""
@@ -3243,7 +3253,7 @@ msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:197
#: src/states_screens/dialogs/message_dialog.cpp:129
#: src/states_screens/edit_gp_screen.cpp:255
#: src/states_screens/ghost_replay_selection.cpp:380
#: src/states_screens/ghost_replay_selection.cpp:386
msgid "No"
msgstr ""
@@ -3594,6 +3604,340 @@ msgstr[1] ""
msgid "translator-credits"
msgstr ""
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:55
msgctxt "achievement_info"
msgid "Goal"
msgstr ""
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:57
msgctxt "achievement_info"
msgid "Progress"
msgstr ""
#. I18N: For achievements, a parent goal linking logically several subgoals
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:131
msgid "Fulfill all the subogals"
msgstr ""
#. I18N: For achievements, a parent goal linking logically several subgoals
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:133
msgid "Fulfill all the subgoals at the same time"
msgstr ""
#. I18N: For achievements, a parent goal linking logically several subgoals
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:135
msgid "Fulfill at least one subgoal"
msgstr ""
#. I18N: For achievements, a parent goal linking logically several subgoals
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:137
msgid "The sum of the subgoals must reach the indicated value"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:139
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:245
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:270
msgid "Races won"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:141
msgid "Normal races won"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:143
msgid "Time-trial races won"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:145
msgid "Follow-the-Leader races won"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:147
msgid "Consecutive won races"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:149
msgid "Consecutive won races in Expert or SuperTux"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:151
msgid "Novice races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:153
msgid "Novice races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:155
msgid "Intermediate races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:157
msgid "Intermediate races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:159
msgid "Expert races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:161
msgid "Expert races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:163
msgid "SuperTux races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:165
msgid "SuperTux races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:167
msgid "Normal races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:169
msgid "Normal races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:171
msgid "Time-trial races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:173
msgid "Time-trial races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:175
msgid "Follow-the-Leader races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:177
msgid "Follow-the-Leader races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:179
msgid "3 Strikes battle started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:181
msgid "3 Strikes battle finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:183
msgid "Soccer matches started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:185
msgid "Soccer matches finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:187
msgid "Egg Hunts started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:189
msgid "Egg Hunts finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:191
msgid "Races started with a ghost replay"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:193
msgid "Races finished with a ghost replay"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:195
msgid "Capture-the-Flag matches started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:197
msgid "Capture-the-Flag matches finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:199
msgid "Free-for-All matches started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:201
msgid "Free-for-All matches finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:203
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:205
msgid "Powerups used"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:205
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:209
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:213
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:217
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:219
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:223
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:231
msgid " (1 race)"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:207
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:209
msgid "Bowling ball hits"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:211
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:213
msgid "Swatter hits"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:215
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:217
msgid "All hits"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:219
msgid "Hits against the same kart"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:221
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:223
msgid "Bananas collected"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#. I18N: Key binding name
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:225
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:229
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:236
#: src/states_screens/options/options_screen_device.cpp:279
msgid "Skidding"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:238
msgid " (1 lap)"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:241
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:266
msgid "Races started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:241
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:243
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:245
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:247
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:249
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:251
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:253
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:255
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:257
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:263
msgid " (maximum on one official track)"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:243
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:268
msgid "Races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:247
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:272
msgid "Reverse direction races finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:249
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:274
msgid "Races finished alone"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:251
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:276
msgid "Races with less than the default lap number"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:253
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:278
msgid "Races with more than the default lap number"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:255
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:280
msgid "Races with at least twice as much as the default lap number"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:257
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:282
msgid "Egg hunts started"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:261
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:286
msgid "Egg hunts finished"
msgstr ""
#. I18N: A goal for achievements. If this text is in (), it's a precision added to multiple different goals.
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:266
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:268
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:270
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:272
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:274
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:276
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:278
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:280
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:282
#: src/states_screens/dialogs/achievement_progress_dialog.cpp:288
msgid " (official tracks matching the goal)"
msgstr ""
#: src/states_screens/dialogs/add_device_dialog.cpp:64
msgid ""
"To add a new Gamepad/Joystick device, simply start SuperTuxKart with it "
@@ -3741,43 +4085,43 @@ msgid "Very Low"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:143
#: src/states_screens/ghost_replay_selection.cpp:129
#: src/states_screens/ghost_replay_selection.cpp:153
msgctxt "ghost_info"
msgid "Reverse"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:145
#: src/states_screens/ghost_replay_selection.cpp:131
#: src/states_screens/ghost_replay_selection.cpp:155
msgctxt "ghost_info"
msgid "Difficulty"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:148
#: src/states_screens/ghost_replay_selection.cpp:133
#: src/states_screens/ghost_replay_selection.cpp:157
msgctxt "ghost_info"
msgid "Laps"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:150
#: src/states_screens/ghost_replay_selection.cpp:134
#: src/states_screens/ghost_replay_selection.cpp:158
msgctxt "ghost_info"
msgid "Time"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:152
#: src/states_screens/ghost_replay_selection.cpp:135
#: src/states_screens/ghost_replay_selection.cpp:159
msgctxt "ghost_info"
msgid "Kart"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:154
#: src/states_screens/ghost_replay_selection.cpp:136
#: src/states_screens/ghost_replay_selection.cpp:160
msgctxt "ghost_info"
msgid "User"
msgstr ""
#: src/states_screens/dialogs/ghost_replay_info_dialog.cpp:156
#: src/states_screens/ghost_replay_selection.cpp:140
#: src/states_screens/ghost_replay_selection.cpp:164
msgctxt "ghost_info"
msgid "Version"
msgstr ""
@@ -3821,7 +4165,7 @@ msgstr ""
#. score of a player
#: src/states_screens/dialogs/ranking_callback.hpp:68
#, c-format
msgid "%s has a rank of %d with score %f."
msgid "%s is number %d in the rankings with a score of %f."
msgstr ""
#: src/states_screens/dialogs/recovery_dialog.cpp:121
@@ -3972,7 +4316,7 @@ msgid "Reversed"
msgstr ""
#: src/states_screens/edit_gp_screen.cpp:122
#: src/states_screens/ghost_replay_selection.cpp:499
#: src/states_screens/ghost_replay_selection.cpp:505
#: src/states_screens/grand_prix_editor_screen.cpp:109
#, c-format
msgid "Are you sure you want to remove '%s'?"
@@ -4036,12 +4380,12 @@ msgstr ""
msgid "You unlocked grand prix %0"
msgstr ""
#: src/states_screens/ghost_replay_selection.cpp:127
#: src/states_screens/ghost_replay_selection.cpp:151
msgctxt "ghost_info"
msgid "Track"
msgstr ""
#: src/states_screens/ghost_replay_selection.cpp:138
#: src/states_screens/ghost_replay_selection.cpp:162
msgctxt "ghost_info"
msgid "Players"
msgstr ""
@@ -4219,12 +4563,18 @@ msgstr ""
msgid "Finding a quick play server"
msgstr ""
#: src/states_screens/online/online_profile_achievements.cpp:77
#. I18N: Goals in achievement
#: src/states_screens/online/online_profile_achievements.cpp:78
msgid "Goals"
msgstr ""
#. I18N: Progress in achievement
#: src/states_screens/online/online_profile_achievements.cpp:80
msgid "Progress"
msgstr ""
#: src/states_screens/online/online_profile_achievements.cpp:129
#: src/states_screens/online/online_profile_achievements.cpp:174
#: src/states_screens/online/online_profile_achievements.cpp:134
#: src/states_screens/online/online_profile_achievements.cpp:192
msgid "Fetching achievements"
msgstr ""
@@ -4447,11 +4797,6 @@ msgstr ""
msgid "Nitro"
msgstr ""
#. I18N: Key binding name
#: src/states_screens/options/options_screen_device.cpp:279
msgid "Skidding"
msgstr ""
#. I18N: Key binding name
#: src/states_screens/options/options_screen_device.cpp:282
msgid "Look Back"
@@ -4706,12 +5051,12 @@ msgid "Follow the leader!"
msgstr ""
#. I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it
#: src/states_screens/race_gui_base.cpp:838
#: src/states_screens/race_gui_base.cpp:837
#, c-format
msgid "Top %i"
msgstr ""
#: src/states_screens/race_gui.cpp:428 src/states_screens/race_gui.cpp:430
#: src/states_screens/race_gui.cpp:431 src/states_screens/race_gui.cpp:433
msgid "Challenge Failed"
msgstr ""

View File

@@ -20,6 +20,9 @@
min-height: Unused mostly, but defines implicitly
the starting height (as average of
max and min height).
fast-ping-distance: When the ball is closer than
this to the target, it will bounce
low and at a high rate.
target-distance: When the ball is closer than
this to the target, it will aim
directly at the target.

View File

@@ -147,7 +147,7 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHaloBW" image="common/glass_square_focused_bw.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo" image="common/glass_square_focused_black.png"
<element type="squareFocusHalo1" image="common/glass_square_focused_black.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo2" image="common/glass_square_focused_cyan.png"
@@ -159,6 +159,9 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHalo4" image="common/glass_square_focused_green.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo5" image="common/glass_square_focused_yellow.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<!-- Stateless. No splitting into 9 areas is done; the image is just resized. -->
<element type="selectionHalo" image="coal/bubble.png" />
@@ -189,6 +192,9 @@ when the border that intersect at this corner are enabled.
<element type="spinner4" state="neutral" image="common/glass_square_green.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner5" state="neutral" image="common/glass_square_yellow.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- This one is a bit special. Only area(s) LEFT and/or RIGHT will be rendered. They will be overlaid
@@ -269,6 +275,14 @@ when the border that intersect at this corner are enabled.
<!-- Color used in the credits -->
<color type="credits_text" state="neutral" r="220" g="220" b="220" />
<!-- Color used for emphasized items in e.g. lists -->
<color type="emphasis_text" state="neutral" r="230" g="210" b="50" />
<color type="emphasis_text" state="focused" r="255" g="226" b="56" />
<!-- Color used for blue items in list (e.g. player team color in networking) -->
<color type="list_blue" state="neutral" r="0" g="0" b="180" />
<color type="list_blue" state="focused" r="0" g="0" b="255" />
<!-- Color used to fade out background when a dialog is shown -->
<color type="dialog_background" state="neutral" a="120" r="0" g="0" b="0" />

View File

@@ -147,7 +147,7 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHaloBW" image="common/glass_square_focused_bw.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo" image="common/glass_square_focused_green.png"
<element type="squareFocusHalo1" image="common/glass_square_focused_green.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo2" image="common/glass_square_focused_yellow.png"
@@ -159,6 +159,9 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHalo4" image="common/glass_square_focused_cyan.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo5" image="common/glass_square_focused_pink.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<!-- Stateless. No splitting into 9 areas is done; the image is just resized. -->
<element type="selectionHalo" image="forest/bubble.png" />
@@ -189,6 +192,9 @@ when the border that intersect at this corner are enabled.
<element type="spinner4" state="neutral" image="common/glass_square_cyan.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner5" state="neutral" image="common/glass_square_pink.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- This one is a bit special. Only area(s) LEFT and/or RIGHT will be rendered. They will be overlaid
@@ -269,6 +275,14 @@ when the border that intersect at this corner are enabled.
<!-- Color used in the credits -->
<color type="credits_text" state="neutral" r="0" g="55" b="0" />
<!-- Color used for emphasized items in e.g. lists -->
<color type="emphasis_text" state="neutral" r="0" g="0" b="180" />
<color type="emphasis_text" state="focused" r="0" g="0" b="160" />
<!-- Color used for blue items in list (e.g. player team color in networking) -->
<color type="list_blue" state="neutral" r="0" g="0" b="255" />
<color type="list_blue" state="focused" r="0" g="0" b="255" />
<!-- Color used to fade out background when a dialog is shown -->
<color type="dialog_background" state="neutral" a="120" r="0" g="0" b="0" />

View File

@@ -146,7 +146,7 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHaloBW" image="common/glass_square_focused_bw.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo" image="common/glass_square_focused_cyan.png"
<element type="squareFocusHalo1" image="common/glass_square_focused_cyan.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo2" image="common/glass_square_focused_yellow.png"
@@ -158,6 +158,9 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHalo4" image="common/glass_square_focused_green.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo5" image="common/glass_square_focused_pink.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<!-- Stateless. No splitting into 9 areas is done; the image is just resized. -->
<element type="selectionHalo" image="ocean/bubble.png" />
@@ -188,6 +191,9 @@ when the border that intersect at this corner are enabled.
<element type="spinner4" state="neutral" image="common/glass_square_green.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner5" state="neutral" image="common/glass_square_pink.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- This one is a bit special. Only area(s) LEFT and/or RIGHT will be rendered. They will be overlaid
@@ -268,6 +274,14 @@ when the border that intersect at this corner are enabled.
<!-- Color used in the credits -->
<color type="credits_text" state="neutral" r="0" g="0" b="75" />
<!-- Color used for emphasized items in e.g. lists -->
<color type="emphasis_text" state="neutral" r="0" g="0" b="180" />
<color type="emphasis_text" state="focused" r="0" g="0" b="160" />
<!-- Color used for blue items in list (e.g. player team color in networking) -->
<color type="list_blue" state="neutral" r="0" g="0" b="255" />
<color type="list_blue" state="focused" r="0" g="0" b="255" />
<!-- Color used to fade out background when a dialog is shown -->
<color type="dialog_background" state="neutral" a="120" r="0" g="0" b="0" />

View File

@@ -146,7 +146,7 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHaloBW" image="common/glass_square_focused_bw.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo" image="common/glass_square_focused_red.png"
<element type="squareFocusHalo1" image="common/glass_square_focused_red.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo2" image="common/glass_square_focused_cyan.png"
@@ -158,6 +158,9 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHalo4" image="common/glass_square_focused_yellow.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo5" image="common/glass_square_focused_pink.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<!-- Stateless. No splitting into 9 areas is done; the image is just resized. -->
<element type="selectionHalo" image="peach/bubble.png" />
@@ -188,6 +191,9 @@ when the border that intersect at this corner are enabled.
<element type="spinner4" state="neutral" image="common/glass_square_yellow.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner5" state="neutral" image="common/glass_square_pink.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- This one is a bit special. Only area(s) LEFT and/or RIGHT will be rendered. They will be overlaid
on top of the spinner's background -->
@@ -266,6 +272,14 @@ when the border that intersect at this corner are enabled.
<!-- Color used in the credits -->
<color type="credits_text" state="neutral" r="65" g="15" b="0" />
<!-- Color used for emphasized items in e.g. lists -->
<color type="emphasis_text" state="neutral" r="0" g="0" b="180" />
<color type="emphasis_text" state="focused" r="0" g="0" b="160" />
<!-- Color used for blue items in list (e.g. player team color in networking) -->
<color type="list_blue" state="neutral" r="0" g="0" b="255" />
<color type="list_blue" state="focused" r="0" g="0" b="255" />
<!-- Color used to fade out background when a dialog is shown -->
<color type="dialog_background" state="neutral" a="120" r="0" g="0" b="0" />

View File

@@ -147,7 +147,7 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHaloBW" image="common/glass_square_focused_bw.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo" image="common/glass_square_focused_pink.png"
<element type="squareFocusHalo1" image="common/glass_square_focused_pink.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo2" image="common/glass_square_focused_cyan.png"
@@ -159,6 +159,9 @@ when the border that intersect at this corner are enabled.
<element type="squareFocusHalo4" image="common/glass_square_focused_yellow.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<element type="squareFocusHalo5" image="common/glass_square_focused_red.png"
left_border="6" right_border ="6" top_border="6" bottom_border="6"
hborder_out_portion="1.0" />
<!-- Stateless. No splitting into 9 areas is done; the image is just resized. -->
<element type="selectionHalo" image="ruby/bubble.png" />
@@ -177,7 +180,7 @@ when the border that intersect at this corner are enabled.
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- player name spinner color in multiplayer-->
<element type="spinner1" state="neutral" image="common/glass_square_red.png"
<element type="spinner1" state="neutral" image="common/glass_square_pink.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner2" state="neutral" image="common/glass_square_cyan.png"
@@ -189,6 +192,9 @@ when the border that intersect at this corner are enabled.
<element type="spinner4" state="neutral" image="common/glass_square_yellow.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<element type="spinner5" state="neutral" image="common/glass_square_red.png"
left_border="110" right_border="110" top_border="0" bottom_border="36"
preserve_h_aspect_ratios="true" hborder_out_portion="0.0" />
<!-- This one is a bit special. Only area(s) LEFT and/or RIGHT will be rendered. They will be overlaid
on top of the spinner's background -->
@@ -267,6 +273,14 @@ when the border that intersect at this corner are enabled.
<!-- Color used in the credits -->
<color type="credits_text" state="neutral" r="65" g="0" b="15" />
<!-- Color used for emphasized items in e.g. lists -->
<color type="emphasis_text" state="neutral" r="0" g="0" b="180" />
<color type="emphasis_text" state="focused" r="0" g="0" b="160" />
<!-- Color used for blue items in list (e.g. player team color in networking) -->
<color type="list_blue" state="neutral" r="0" g="0" b="255" />
<color type="list_blue" state="focused" r="0" g="0" b="255" />
<!-- Color used to fade out background when a dialog is shown -->
<color type="dialog_background" state="neutral" a="120" r="0" g="0" b="0" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -147,7 +147,7 @@
delta-steering If the steering angle difference exceeds this delta,
new transform event is generated before maximum time. -->
<replay max-frames="12000" delta-t="0.200" delta-speed="0.6"
delta-steering="0.35" />
delta-steering="0.26" />
<!-- Determines the minimap related values.
size: The size of the minimap (scaled afterwards) 480 = full screen height)

View File

@@ -583,7 +583,7 @@ if(ENABLE_WAYLAND_DEVICE)
set(IRRLICHT_SOURCES
${IRRLICHT_SOURCES}
source/Irrlicht/server_decoration_client_protocol.c
source/Irrlicht/xdg-shell-unstable-v6-protocol.c)
source/Irrlicht/xdg_shell_protocol.c)
endif()

View File

@@ -183,6 +183,7 @@ namespace scene
virtual void addAnimationSet(u32 start, u32 end) = 0;
virtual void useAnimationSet(u32 set_num) = 0;
virtual void removeAllAnimationSet() = 0;
virtual core::array<u32>& getAnimationSetFrames() = 0;
};
} // end namespace scene

View File

@@ -304,6 +304,10 @@ public:
See IReferenceCounted::drop() for more information. */
virtual IFileList* createFileList() =0;
//! Creates a list of files and directories in specified directory
//! and returns it.
virtual IFileList* createFileList(const io::path& directory) =0;
//! Creates an empty filelist
/** \return a Pointer to the created IFileList is returned. After the list has been used
it has to be deleted using its IFileList::drop() method.

View File

@@ -170,6 +170,7 @@ namespace scene
virtual void removeAllAnimationSet() { m_animation_set.clear(); }
virtual void useAnimationSet(u32 set_num);
virtual void setFrameLoopOnce(s32 begin, s32 end);
virtual core::array<u32>& getAnimationSetFrames() { return m_animation_set; }
protected:
//! Get a static mesh for the current frame of this animated mesh

View File

@@ -795,7 +795,7 @@ path CFileSystem::getRelativeFilename(const path& filename, const path& director
}
//! Sets the current file systen type
//! Sets the current file system type
EFileSystemType CFileSystem::setFileListSystem(EFileSystemType listType)
{
EFileSystemType current = FileSystemType;
@@ -803,12 +803,18 @@ EFileSystemType CFileSystem::setFileListSystem(EFileSystemType listType)
return current;
}
//! Creates a list of files and directories in the current working directory
IFileList* CFileSystem::createFileList()
{
CFileList* r = 0;
io::path Path = getWorkingDirectory();
return createFileList(Path);
}
//! Creates a list of files and directories in specified directory
IFileList* CFileSystem::createFileList(const io::path& directory)
{
CFileList* r = 0;
io::path Path = directory;
Path.replace('\\', '/');
if (Path.lastChar() != '/')
Path.append('/');
@@ -830,8 +836,10 @@ IFileList* CFileSystem::createFileList()
intptr_t hFile;
#endif
io::path searchPath = Path;
searchPath.append('*');
struct _tfinddata_t c_file;
if( (hFile = _tfindfirst( _T("*"), &c_file )) != -1L )
if( (hFile = _tfindfirst( _T(searchPath.c_str()), &c_file )) != -1L )
{
do
{

View File

@@ -121,6 +121,10 @@ public:
//! and returns it.
virtual IFileList* createFileList();
//! Creates a list of files and directories in specified directory
//! and returns it.
virtual IFileList* createFileList(const io::path& directory);
//! Creates an empty filelist
virtual IFileList* createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths);

View File

@@ -186,6 +186,9 @@ void CIrrDeviceAndroid::createVideoModeList()
int width = ANativeWindow_getWidth(Android->window);
int height = ANativeWindow_getHeight(Android->window);
os::Printer::log("Window width:", core::stringc(width).c_str(), ELL_DEBUG);
os::Printer::log("Window height:", core::stringc(height).c_str(), ELL_DEBUG);
if (width > 0 && height > 0)
{

View File

@@ -73,9 +73,9 @@ public:
static const wl_output_listener output_listener;
static const wl_shell_surface_listener shell_surface_listener;
static const wl_registry_listener registry_listener;
static const zxdg_shell_v6_listener xdg_shell_listener;
static const zxdg_surface_v6_listener xdg_surface_listener;
static const zxdg_toplevel_v6_listener xdg_toplevel_listener;
static const xdg_wm_base_listener wm_base_listener;
static const xdg_surface_listener surface_listener;
static const xdg_toplevel_listener toplevel_listener;
static void pointer_enter(void* data, wl_pointer* pointer, uint32_t serial,
wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy)
@@ -514,23 +514,23 @@ public:
{
}
static void xdg_shell_ping(void* data, zxdg_shell_v6* shell,
uint32_t serial)
static void xdg_wm_base_ping(void* data, xdg_wm_base* shell,
uint32_t serial)
{
zxdg_shell_v6_pong(shell, serial);
xdg_wm_base_pong(shell, serial);
}
static void xdg_surface_configure(void* data, zxdg_surface_v6* surface,
static void xdg_surface_configure(void* data, xdg_surface* surface,
uint32_t serial)
{
CIrrDeviceWayland* device = static_cast<CIrrDeviceWayland*>(data);
zxdg_surface_v6_ack_configure(surface, serial);
xdg_surface_ack_configure(surface, serial);
device->m_surface_configured = true;
}
static void xdg_toplevel_configure(void* data, zxdg_toplevel_v6* toplevel,
static void xdg_toplevel_configure(void* data, xdg_toplevel* toplevel,
int32_t width, int32_t height,
wl_array* states)
{
@@ -553,7 +553,7 @@ public:
//}
}
static void xdg_toplevel_close(void* data, zxdg_toplevel_v6* xdg_toplevel)
static void xdg_toplevel_close(void* data, xdg_toplevel* xdg_toplevel)
{
CIrrDeviceWayland* device = static_cast<CIrrDeviceWayland*>(data);
@@ -609,10 +609,10 @@ public:
wl_registry_bind(registry, name,
&org_kde_kwin_server_decoration_manager_interface, 1));
}
else if (interface_str == "zxdg_shell_v6")
else if (interface_str == "xdg_wm_base")
{
device->m_has_xdg_shell = true;
device->m_xdg_shell_name = name;
device->m_has_xdg_wm_base = true;
device->m_xdg_wm_base_name = name;
}
}
@@ -668,17 +668,17 @@ const wl_registry_listener WaylandCallbacks::registry_listener =
WaylandCallbacks::registry_global_remove
};
const zxdg_shell_v6_listener WaylandCallbacks::xdg_shell_listener =
const xdg_wm_base_listener WaylandCallbacks::wm_base_listener =
{
WaylandCallbacks::xdg_shell_ping
WaylandCallbacks::xdg_wm_base_ping
};
const zxdg_surface_v6_listener WaylandCallbacks::xdg_surface_listener =
const xdg_surface_listener WaylandCallbacks::surface_listener =
{
WaylandCallbacks::xdg_surface_configure
};
const zxdg_toplevel_v6_listener WaylandCallbacks::xdg_toplevel_listener =
const xdg_toplevel_listener WaylandCallbacks::toplevel_listener =
{
WaylandCallbacks::xdg_toplevel_configure,
WaylandCallbacks::xdg_toplevel_close
@@ -724,12 +724,12 @@ CIrrDeviceWayland::CIrrDeviceWayland(const SIrrlichtCreationParameters& params)
m_has_wl_shell = false;
m_wl_shell_name = 0;
m_xdg_shell = NULL;
m_xdg_wm_base = NULL;
m_xdg_surface = NULL;
m_xdg_toplevel = NULL;
m_has_xdg_shell = false;
m_has_xdg_wm_base = false;
m_surface_configured = false;
m_xdg_shell_name = 0;
m_xdg_wm_base_name = 0;
m_decoration_manager = NULL;
m_decoration = NULL;
@@ -818,13 +818,13 @@ CIrrDeviceWayland::~CIrrDeviceWayland()
wl_cursor_theme_destroy(m_cursor_theme);
if (m_xdg_toplevel)
zxdg_toplevel_v6_destroy(m_xdg_toplevel);
xdg_toplevel_destroy(m_xdg_toplevel);
if (m_xdg_surface)
zxdg_surface_v6_destroy(m_xdg_surface);
xdg_surface_destroy(m_xdg_surface);
if (m_xdg_shell)
zxdg_shell_v6_destroy(m_xdg_shell);
if (m_xdg_wm_base)
xdg_wm_base_destroy(m_xdg_wm_base);
if (m_shell_surface)
wl_shell_surface_destroy(m_shell_surface);
@@ -908,7 +908,7 @@ bool CIrrDeviceWayland::initWayland()
return false;
}
if (!m_has_wl_shell && !m_has_xdg_shell)
if (!m_has_wl_shell && !m_has_xdg_wm_base)
{
os::Printer::log("Shell protocol is not available.", ELL_ERROR);
return false;
@@ -916,13 +916,13 @@ bool CIrrDeviceWayland::initWayland()
if (CreationParams.DriverType != video::EDT_NULL)
{
if (m_has_xdg_shell)
if (m_has_xdg_wm_base)
{
m_xdg_shell = static_cast<zxdg_shell_v6*>(wl_registry_bind(
m_registry, m_xdg_shell_name, &zxdg_shell_v6_interface, 1));
m_xdg_wm_base = static_cast<xdg_wm_base*>(wl_registry_bind(
m_registry, m_xdg_wm_base_name, &xdg_wm_base_interface, 1));
zxdg_shell_v6_add_listener(m_xdg_shell,
&WaylandCallbacks::xdg_shell_listener, this);
xdg_wm_base_add_listener(m_xdg_wm_base,
&WaylandCallbacks::wm_base_listener, this);
}
else if (m_has_wl_shell)
{
@@ -1001,29 +1001,26 @@ bool CIrrDeviceWayland::createWindow()
return false;
}
if (m_xdg_shell != NULL)
if (m_xdg_wm_base != NULL)
{
m_xdg_surface = zxdg_shell_v6_get_xdg_surface(m_xdg_shell, m_surface);
m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface);
zxdg_surface_v6_add_listener(m_xdg_surface,
&WaylandCallbacks::xdg_surface_listener,
this);
xdg_surface_add_listener(m_xdg_surface,
&WaylandCallbacks::surface_listener, this);
m_xdg_toplevel = zxdg_surface_v6_get_toplevel(m_xdg_surface);
m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface);
zxdg_toplevel_v6_add_listener(m_xdg_toplevel,
&WaylandCallbacks::xdg_toplevel_listener,
this);
xdg_toplevel_add_listener(m_xdg_toplevel,
&WaylandCallbacks::toplevel_listener, this);
wl_surface_commit(m_surface);
if (CreationParams.Fullscreen)
{
zxdg_toplevel_v6_set_fullscreen(m_xdg_toplevel, NULL);
xdg_toplevel_set_fullscreen(m_xdg_toplevel, NULL);
}
zxdg_surface_v6_set_window_geometry(m_xdg_surface, 0, 0, m_width,
m_height);
xdg_surface_set_window_geometry(m_xdg_surface, 0, 0, m_width, m_height);
while (!m_surface_configured)
{
@@ -1221,7 +1218,7 @@ void CIrrDeviceWayland::setWindowCaption(const wchar_t* text)
if (m_xdg_toplevel)
{
zxdg_toplevel_v6_set_title(m_xdg_toplevel, title);
xdg_toplevel_set_title(m_xdg_toplevel, title);
}
else if (m_shell_surface)
{
@@ -1234,7 +1231,7 @@ void CIrrDeviceWayland::setWindowClass(const char* text)
{
if (m_xdg_toplevel)
{
zxdg_toplevel_v6_set_app_id(m_xdg_toplevel, text);
xdg_toplevel_set_app_id(m_xdg_toplevel, text);
}
else if (m_shell_surface)
{
@@ -1287,8 +1284,8 @@ void CIrrDeviceWayland::setResizable(bool resize)
int width = resize ? 0 : m_width;
int height = resize ? 0 : m_height;
zxdg_toplevel_v6_set_min_size(m_xdg_toplevel, width, height);
zxdg_toplevel_v6_set_max_size(m_xdg_toplevel, width, height);
xdg_toplevel_set_min_size(m_xdg_toplevel, width, height);
xdg_toplevel_set_max_size(m_xdg_toplevel, width, height);
}
}
@@ -1303,7 +1300,7 @@ void CIrrDeviceWayland::minimizeWindow()
{
if (m_xdg_toplevel)
{
zxdg_toplevel_v6_set_minimized(m_xdg_toplevel);
xdg_toplevel_set_minimized(m_xdg_toplevel);
}
}
@@ -1312,7 +1309,7 @@ void CIrrDeviceWayland::maximizeWindow()
{
if (m_xdg_toplevel)
{
zxdg_toplevel_v6_set_maximized(m_xdg_toplevel);
xdg_toplevel_set_maximized(m_xdg_toplevel);
}
}
@@ -1321,7 +1318,7 @@ void CIrrDeviceWayland::restoreWindow()
{
if (m_xdg_toplevel)
{
zxdg_toplevel_v6_unset_maximized(m_xdg_toplevel);
xdg_toplevel_unset_maximized(m_xdg_toplevel);
}
}

View File

@@ -27,7 +27,7 @@
#include "IImagePresenter.h"
#include "ICursorControl.h"
#include "server_decoration_client_protocol.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "xdg_shell_client_protocol.h"
#include <wayland-client.h>
#include <wayland-cursor.h>
@@ -183,12 +183,12 @@ namespace irr
bool m_has_wl_shell;
uint32_t m_wl_shell_name;
zxdg_shell_v6* m_xdg_shell;
zxdg_surface_v6* m_xdg_surface;
zxdg_toplevel_v6* m_xdg_toplevel;
bool m_has_xdg_shell;
xdg_wm_base* m_xdg_wm_base;
xdg_surface* m_xdg_surface;
xdg_toplevel* m_xdg_toplevel;
bool m_has_xdg_wm_base;
bool m_surface_configured;
uint32_t m_xdg_shell_name;
uint32_t m_xdg_wm_base_name;
org_kde_kwin_server_decoration_manager* m_decoration_manager;
org_kde_kwin_server_decoration* m_decoration;

View File

@@ -333,6 +333,11 @@ void CBillboardTextSceneNode::OnRegisterSceneNode()
//! render
void CBillboardTextSceneNode::render()
{
// FIXME: Billboard text is broken with GLES 2.0.
#ifdef _IRR_COMPILE_WITH_OGLES2_
return;
#endif
if ( !Mesh )
return;

View File

@@ -1,10 +1,12 @@
/* Generated by wayland-scanner 1.12.0 */
/* Generated by wayland-scanner 1.14.0 */
/*
* Copyright © 2008-2013 Kristian Høgsberg
* Copyright © 2013 Rafael Antognolli
* Copyright © 2013 Jasper St. Pierre
* Copyright © 2010-2013 Intel Corporation
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
* Copyright © 2015-2017 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -33,24 +35,24 @@
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface zxdg_popup_v6_interface;
extern const struct wl_interface zxdg_positioner_v6_interface;
extern const struct wl_interface zxdg_surface_v6_interface;
extern const struct wl_interface zxdg_toplevel_v6_interface;
extern const struct wl_interface xdg_popup_interface;
extern const struct wl_interface xdg_positioner_interface;
extern const struct wl_interface xdg_surface_interface;
extern const struct wl_interface xdg_toplevel_interface;
static const struct wl_interface *types[] = {
NULL,
NULL,
NULL,
NULL,
&zxdg_positioner_v6_interface,
&zxdg_surface_v6_interface,
&xdg_positioner_interface,
&xdg_surface_interface,
&wl_surface_interface,
&zxdg_toplevel_v6_interface,
&zxdg_popup_v6_interface,
&zxdg_surface_v6_interface,
&zxdg_positioner_v6_interface,
&zxdg_toplevel_v6_interface,
&xdg_toplevel_interface,
&xdg_popup_interface,
&xdg_surface_interface,
&xdg_positioner_interface,
&xdg_toplevel_interface,
&wl_seat_interface,
NULL,
NULL,
@@ -65,24 +67,24 @@ static const struct wl_interface *types[] = {
NULL,
};
static const struct wl_message zxdg_shell_v6_requests[] = {
static const struct wl_message xdg_wm_base_requests[] = {
{ "destroy", "", types + 0 },
{ "create_positioner", "n", types + 4 },
{ "get_xdg_surface", "no", types + 5 },
{ "pong", "u", types + 0 },
};
static const struct wl_message zxdg_shell_v6_events[] = {
static const struct wl_message xdg_wm_base_events[] = {
{ "ping", "u", types + 0 },
};
WL_EXPORT const struct wl_interface zxdg_shell_v6_interface = {
"zxdg_shell_v6", 1,
4, zxdg_shell_v6_requests,
1, zxdg_shell_v6_events,
WL_EXPORT const struct wl_interface xdg_wm_base_interface = {
"xdg_wm_base", 2,
4, xdg_wm_base_requests,
1, xdg_wm_base_events,
};
static const struct wl_message zxdg_positioner_v6_requests[] = {
static const struct wl_message xdg_positioner_requests[] = {
{ "destroy", "", types + 0 },
{ "set_size", "ii", types + 0 },
{ "set_anchor_rect", "iiii", types + 0 },
@@ -92,31 +94,31 @@ static const struct wl_message zxdg_positioner_v6_requests[] = {
{ "set_offset", "ii", types + 0 },
};
WL_EXPORT const struct wl_interface zxdg_positioner_v6_interface = {
"zxdg_positioner_v6", 1,
7, zxdg_positioner_v6_requests,
WL_EXPORT const struct wl_interface xdg_positioner_interface = {
"xdg_positioner", 2,
7, xdg_positioner_requests,
0, NULL,
};
static const struct wl_message zxdg_surface_v6_requests[] = {
static const struct wl_message xdg_surface_requests[] = {
{ "destroy", "", types + 0 },
{ "get_toplevel", "n", types + 7 },
{ "get_popup", "noo", types + 8 },
{ "get_popup", "n?oo", types + 8 },
{ "set_window_geometry", "iiii", types + 0 },
{ "ack_configure", "u", types + 0 },
};
static const struct wl_message zxdg_surface_v6_events[] = {
static const struct wl_message xdg_surface_events[] = {
{ "configure", "u", types + 0 },
};
WL_EXPORT const struct wl_interface zxdg_surface_v6_interface = {
"zxdg_surface_v6", 1,
5, zxdg_surface_v6_requests,
1, zxdg_surface_v6_events,
WL_EXPORT const struct wl_interface xdg_surface_interface = {
"xdg_surface", 2,
5, xdg_surface_requests,
1, xdg_surface_events,
};
static const struct wl_message zxdg_toplevel_v6_requests[] = {
static const struct wl_message xdg_toplevel_requests[] = {
{ "destroy", "", types + 0 },
{ "set_parent", "?o", types + 11 },
{ "set_title", "s", types + 0 },
@@ -133,30 +135,30 @@ static const struct wl_message zxdg_toplevel_v6_requests[] = {
{ "set_minimized", "", types + 0 },
};
static const struct wl_message zxdg_toplevel_v6_events[] = {
static const struct wl_message xdg_toplevel_events[] = {
{ "configure", "iia", types + 0 },
{ "close", "", types + 0 },
};
WL_EXPORT const struct wl_interface zxdg_toplevel_v6_interface = {
"zxdg_toplevel_v6", 1,
14, zxdg_toplevel_v6_requests,
2, zxdg_toplevel_v6_events,
WL_EXPORT const struct wl_interface xdg_toplevel_interface = {
"xdg_toplevel", 2,
14, xdg_toplevel_requests,
2, xdg_toplevel_events,
};
static const struct wl_message zxdg_popup_v6_requests[] = {
static const struct wl_message xdg_popup_requests[] = {
{ "destroy", "", types + 0 },
{ "grab", "ou", types + 22 },
};
static const struct wl_message zxdg_popup_v6_events[] = {
static const struct wl_message xdg_popup_events[] = {
{ "configure", "iiii", types + 0 },
{ "popup_done", "", types + 0 },
};
WL_EXPORT const struct wl_interface zxdg_popup_v6_interface = {
"zxdg_popup_v6", 1,
2, zxdg_popup_v6_requests,
2, zxdg_popup_v6_events,
WL_EXPORT const struct wl_interface xdg_popup_interface = {
"xdg_popup", 2,
2, xdg_popup_requests,
2, xdg_popup_events,
};

View File

@@ -19,8 +19,10 @@
#include "achievements/achievement.hpp"
#include "achievements/achievements_manager.hpp"
#include "achievements/achievement_info.hpp"
#include "config/player_manager.hpp"
#include "guiengine/message_queue.hpp"
#include "io/utf_writer.hpp"
#include "config/player_manager.hpp"
@@ -32,11 +34,12 @@
/** Constructur, initialises this object with the data from the
* corresponding AchievementInfo.
*/
Achievement::Achievement(const AchievementInfo * info)
Achievement::Achievement(AchievementInfo * info)
: m_achievement_info(info)
{
m_id = info->getID();
m_achieved = false;
m_id = m_achievement_info->getID();
m_achievement_info->copyGoalTree(m_progress_goal_tree, m_achievement_info->m_goal_tree, true /*set values to 0*/);
} // Achievement
// ----------------------------------------------------------------------------
@@ -47,179 +50,340 @@ Achievement::~Achievement()
// ----------------------------------------------------------------------------
/** Loads the value from an XML node.
* \param input*/
void Achievement::load(const XMLNode *node)
void Achievement::loadProgress(const XMLNode *node)
{
node->get("id", &m_id );
node->get("achieved", &m_achieved);
for (unsigned int i = 0; i < node->getNumNodes(); i++)
{
const XMLNode *n = node->getNode(i);
std::string key = n->getName();
int value = 0;
n->get("value", &value);
m_progress_map[key] = value;
}
} // load
// ----------------------------------------------------------------------------
/** Saves the achievement status to a file.
* \param Output stream.
*/
void Achievement::save(UTFWriter &out)
void Achievement::saveProgress(UTFWriter &out)
{
out << " <achievement id=\"" << m_id << "\" "
out << " <achievement id=\"" << getID() << "\" "
<< "achieved=\"" << m_achieved << "\"";
if (isAchieved())
{
out << "/>\n";
return;
}
out << ">\n";
std::map<std::string, int>::iterator i;
for (i = m_progress_map.begin(); i != m_progress_map.end(); ++i)
{
out << " <" << i->first
<< " value=\"" << i->second << "\"/>\n";
}
out << " </achievement>\n";
out << "/>\n";
} // save
// ----------------------------------------------------------------------------
/** Returns the value for a key.
/** Returns how many goals of an achievement have been achieved,
* in the form n/m.
*/
int Achievement::getValue(const std::string & key)
irr::core::stringw Achievement::getGoalProgressAsString()
{
if (m_progress_map.find(key) != m_progress_map.end())
return m_progress_map[key];
return 0;
}
// ----------------------------------------------------------------------------
/** Resets all currently key values to 0. Called if the reset-after-race flag
* is set for the corresponding AchievementInfo.
*/
void Achievement::reset()
{
std::map<std::string, int>::iterator iter;
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
{
iter->second = 0;
}
} // reset
irr::core::stringw target = getInfo()->goalString();
// ----------------------------------------------------------------------------
/** Returns how much of an achievement has been achieved in the form n/m.
* The AchievementInfo adds up all goal values to get 'm', and this
* this class end up all current key values for 'n'.
*/
irr::core::stringw Achievement::getProgressAsString() const
{
int progress = 0;
std::map<std::string, int>::const_iterator iter;
// For now return N/N in case of an achieved achievement.
// Return N/N in case of an achieved achievement.
if (m_achieved)
return getInfo()->toString() +"/" + getInfo()->toString();
return target + "/" + target;
switch (m_achievement_info->getCheckType())
{
case AchievementInfo::AC_ALL_AT_LEAST:
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
{
progress += iter->second;
}
break;
case AchievementInfo::AC_ONE_AT_LEAST:
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
{
if(iter->second>progress) progress = iter->second;
}
break;
default:
Log::fatal("Achievement", "Missing getProgressAsString for type %d.",
m_achievement_info->getCheckType());
}
return StringUtils::toWString(progress) + "/" + getInfo()->toString();
} // getProgressAsString
int fullfiled_goals = computeFullfiledGoals(m_progress_goal_tree, m_achievement_info->m_goal_tree);
return StringUtils::toWString(fullfiled_goals) + "/" + target;
} // getGoalProgressAsString
// ----------------------------------------------------------------------------
/** Increases the value of a key by a specified amount, but make sure to not
* increase the value above the goal (otherwise the achievement progress
* could be 12/10 (e.g. if one track is used 12 times for the Christoffel
* achievement), even though the achievement is not achieved.
* \param key The key whose value is increased.
* \param increase Amount to add to the value of this key.
*/
void Achievement::increase(const std::string & key,
const std::string &goal_key, int increase)
int Achievement::computeFullfiledGoals(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference)
{
std::map<std::string, int>::iterator it;
it = m_progress_map.find(key);
if (it != m_progress_map.end())
if (progress.children.size() != 1)
{
it->second += increase;
if (it->second > m_achievement_info->getGoalValue(goal_key))
it->second = m_achievement_info->getGoalValue(goal_key);
// This always returns 0 if the achievement has not been completed
if (progress.children[0].type == "OR")
{
bool completed = false;
for (unsigned int i=0;i<progress.children.size();i++)
{
if (recursiveCompletionCheck(progress.children[i], reference.children[i]))
{
completed = true;
break;
}
}
return (completed) ? 1 : 0;
}
else
{
int goals_completed = 0;
for (unsigned int i=0;i<progress.children.size();i++)
{
if (recursiveCompletionCheck(progress.children[i], reference.children[i]))
goals_completed++;
}
return goals_completed;
}
}
else if (progress.children.size() == 1 &&
(progress.children[0].type == "AND" ||
progress.children[0].type == "AND-AT-ONCE" ||
progress.children[0].type == "OR"))
{
return computeFullfiledGoals(progress.children[0], reference.children[0]);
}
else
{
if (increase>m_achievement_info->getGoalValue(goal_key))
increase = m_achievement_info->getGoalValue(goal_key);
m_progress_map[key] = increase;
return (recursiveCompletionCheck(progress.children[0], reference.children[0])) ? 1 : 0;
}
check();
} // increase
} // computeFullfiledGoals
// ----------------------------------------------------------------------------
/** Called at the end of a race to potentially reset values.
/** Returns how much of an achievement has been achieved in the form n/m.
* ONLY applicable for single goal achievements. Returns a string with a single
* space if there are multiple goals.
*/
void Achievement::onRaceEnd()
irr::core::stringw Achievement::getProgressAsString()
{
if(m_achievement_info->needsResetAfterRace())
reset();
} // onRaceEnd
irr::core::stringw target = getInfo()->progressString();
irr::core::stringw empty = " ";// See issue #3081
if (target == "-1")
return empty;
// Return N/N in case of an achieved achievement.
if (m_achieved)
return target + "/" + target;
int progress = computeGoalProgress(m_progress_goal_tree, m_achievement_info->m_goal_tree);
return StringUtils::toWString(progress) + "/" + target;
} // getProgressAsString
// ----------------------------------------------------------------------------
/** Called at the end of a lap to potentially reset values.
*/
void Achievement::onLapEnd()
/** Should ONLY be called if the achievement has one goal (a sum counts as one goal).
* Returning an error code with a number is not full-proof because a sum goal can
* legitimately be negative (a counter can be chosen to count against the
* achievement's fullfilment). */
int Achievement::computeGoalProgress(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference, bool same_tree)
{
if (m_achievement_info->needsResetAfterLap())
reset();
} // onLapEnd
if (progress.children.size() >= 2)
{
// This should NOT happen
assert(false);
return 0;
}
// Can happen when showing the progress status of all parts of the goal tree
else if (progress.children.size() == 0)
{
//TODO : find a more automatic way ; clean up repetition
if (progress.type == "race-started-all" ||
progress.type == "race-finished-all" ||
progress.type == "race-won-all" ||
progress.type == "race-finished-reverse-all" ||
progress.type == "race-finished-alone-all" ||
progress.type == "less-laps-all" ||
progress.type == "more-laps-all" ||
progress.type == "twice-laps-all" ||
progress.type == "egg-hunt-started-all" ||
progress.type == "egg-hunt-finished-all")
{
if (same_tree)
{
return PlayerManager::getCurrentAchievementsStatus()
->getNumTracksAboveValue(0, reference.type);
}
// Compare against the target value (in the reference tree) !
// Progress is only shown for the current local accuont, so we can use the current achievements status
return PlayerManager::getCurrentAchievementsStatus()
->getNumTracksAboveValue(reference.value, reference.type);
}
return progress.value;
}
else if (progress.children.size() == 1 &&
(progress.children[0].type == "AND" ||
progress.children[0].type == "AND-AT-ONCE" ||
progress.children[0].type == "OR"))
{
return computeGoalProgress(progress.children[0], reference.children[0]);
}
else
{
//TODO : find a more automatic way
if (progress.children[0].type == "race-started-all" ||
progress.children[0].type == "race-finished-all" ||
progress.children[0].type == "race-won-all" ||
progress.children[0].type == "race-finished-reverse-all" ||
progress.children[0].type == "race-finished-alone-all" ||
progress.children[0].type == "less-laps-all" ||
progress.children[0].type == "more-laps-all" ||
progress.children[0].type == "twice-laps-all" ||
progress.children[0].type == "egg-hunt-started-all" ||
progress.children[0].type == "egg-hunt-finished-all")
{
// Compare against the target value (in the reference tree) !
// Progress is only shown for the current local accuont, so we can use the current achievements status
return PlayerManager::getCurrentAchievementsStatus()
->getNumTracksAboveValue(reference.children[0].value, reference.children[0].type);
}
else
{
return progress.children[0].value;
}
}
} // computeGoalProgress
// ----------------------------------------------------------------------------
/** Set any leaf of the progress goal tree whose type matches the
* goal_string to the value passed as parameter.
* The goal string can contain a logical prefix.
* If it is LOGC- ; the update is for the current value of a
* resetable counter. It is applied if the parent node is of type
* SUM or AND-AT-ONCE, ignored otherwise.
* If it is LOGM- ; the update is for the highest achieved value of a
* resetable counter. It is appliedif the parent node is of type
* AND or OR, ignored otherwise.
* If there is no logical prefix, the new value is set in all cases.
*
* If the leaf has an operator defined, this will trigger an update of the
* relevant values.
* If the leaf's new value match or exceed its target goal value,
* a check for the achievement's completions is triggered.
*/
void Achievement::setGoalValue(std::string &goal_string, int value)
{
if(m_achieved) // This should not happen, but it costs little to double-check
return;
bool and_or = true;
bool sum_andatonce = true;
if (goal_string.compare(0 /*start of sub-string*/,5/*length*/,"LOGC-") == 0)
{
and_or = false;
goal_string = goal_string.substr(5,999);
}
else if (goal_string.compare(0 /*start of sub-string*/,5/*length*/,"LOGM-") == 0)
{
sum_andatonce = false;
goal_string = goal_string.substr(5,999);
}
bool found = recursiveSetGoalValue(m_progress_goal_tree, goal_string, value, and_or, sum_andatonce);
// If a value has been updated, check for completion
if (found && recursiveCompletionCheck(m_progress_goal_tree, m_achievement_info->m_goal_tree))
{
setAchieved();
onCompletion();
}
} // setGoalValue
bool Achievement::recursiveSetGoalValue(AchievementInfo::goalTree &tree, const std::string &goal_string, int value,
bool and_or, bool sum_andatonce)
{
if (tree.type == goal_string)
{
// We don't update here, because it may yet be cancelled
// depending on the parent tree logical type.
return true;
}
bool can_set_child = ((and_or && (tree.type == "AND" || tree.type == "OR")) ||
(sum_andatonce && (tree.type == "SUM" || tree.type == "AND-AT-ONCE")));
bool value_set = false;
for (unsigned int i=0;i<tree.children.size();i++)
{
if(recursiveSetGoalValue(tree.children[i],goal_string,value, and_or, sum_andatonce))
{
// The value has already been set, pass on the information
if (tree.children[i].type == "AND" ||
tree.children[i].type == "OR" ||
tree.children[i].type == "SUM" ||
tree.children[i].type == "AND-AT-ONCE")
{
value_set = true;
}
// The child has the good type and we can increment the goal;
else if (can_set_child)
{
tree.children[i].value = value;
value_set = true;
}
}
}
// Recompute the sum
if (tree.type == "SUM" && value_set)
{
int new_value = 0;
for (unsigned int i=0;i<tree.children.size();i++)
{
if(tree.children[i].operation == AchievementInfo::OP_ADD)
new_value += tree.children[i].value;
else if(tree.children[i].operation == AchievementInfo::OP_SUBSTRACT)
new_value -= tree.children[i].value;
}
tree.value = new_value;
}
return value_set;
} // recursiveSetGoalValue
// ----------------------------------------------------------------------------
/** Checks if this achievement has been achieved.
*/
void Achievement::check()
bool Achievement::recursiveCompletionCheck(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference)
{
if(m_achieved)
return;
if(m_achievement_info->checkCompletion(this))
bool completed = false;
if (progress.type == "AND" || progress.type == "AND-AT-ONCE")
{
//show achievement
// Note: the "name" variable is required, see issue #2068
// calling _("...", info->getName()) is invalid because getName also calls
// _() and thus the string it returns is mapped to a temporary buffer
// in theory, it should return a copy of the string, but clang tries to
// optimise away the copy
core::stringw name = m_achievement_info->getName();
core::stringw s = _("Completed achievement \"%s\".", name);
MessageQueue::add(MessageQueue::MT_ACHIEVEMENT, s);
// Sends a confirmation to the server that an achievement has been
// completed, if a user is signed in.
if (PlayerManager::isCurrentLoggedIn())
completed = true;
for (unsigned int i=0;i<progress.children.size();i++)
{
Online::HTTPRequest * request = new Online::HTTPRequest(true);
PlayerManager::setUserDetails(request, "achieving");
request->addParameter("achievementid", m_id);
request->queue();
if (!recursiveCompletionCheck(progress.children[i], reference.children[i]))
{
completed = false;
break;
}
}
m_achieved = true;
}
} // check
else if (progress.type == "OR")
{
completed = false;
for (unsigned int i=0;i<progress.children.size();i++)
{
if (recursiveCompletionCheck(progress.children[i], reference.children[i]))
{
completed = true;
break;
}
}
}
// Whether a sum or a leaf node, it has a value.
// The value for sums are updated when the underlying values are,
// we don't need to do it again
else if (progress.value >= reference.value)
{
completed = true;
}
return completed;
} // recursiveCompletionCheck
// ----------------------------------------------------------------------------
/** Manages what needs to happen once the achievement is completed,
* like displaying the completion message to the player or synching
* with the server.
*/
void Achievement::onCompletion()
{
//show achievement
// Note: the "name" variable is required, see issue #2068
// calling _("...", info->getName()) is invalid because getName also calls
// _() and thus the string it returns is mapped to a temporary buffer
// in theory, it should return a copy of the string, but clang tries to
// optimise away the copy
core::stringw name = m_achievement_info->getName();
core::stringw s = _("Completed achievement \"%s\".", name);
MessageQueue::add(MessageQueue::MT_ACHIEVEMENT, s);
// Sends a confirmation to the server that an achievement has been
// completed, if a user is signed in.
if (PlayerManager::isCurrentLoggedIn())
{
Online::HTTPRequest * request = new Online::HTTPRequest(true);
PlayerManager::setUserDetails(request, "achieving");
request->addParameter("achievementid", getID());
request->queue();
}
} // onCompletion

View File

@@ -20,6 +20,7 @@
#ifndef HEADER_ACHIEVEMENT_HPP
#define HEADER_ACHIEVEMENT_HPP
#include "achievements/achievement_info.hpp"
#include "utils/types.hpp"
#include <irrString.h>
@@ -30,13 +31,9 @@ class UTFWriter;
class XMLNode;
// ============================================================================
/** This is the base class for any achievement. It allows achievement status
* to be saved, and detects when an achievement is fulfilled. It provides
* storage for state information by a generic key-value mapping. The values
* are stored as strings, but can be used to store numerical values. E.g.
* you can call increase("key", 10) for an achievement, which will convert
* the string to int, add 10, then convert the result back to string for
* storage.
/** This class tracks the progress of an achievement for a player, whose
* definition is stored by an associated AchievementInfo. It allows achievement
* status to be saved, and detects when an achievement is fulfilled.
* \ingroup achievements
*/
class AchievementInfo;
@@ -44,50 +41,51 @@ class AchievementInfo;
class Achievement
{
private:
/** The id of this achievement. */
uint32_t m_id;
/** True if this achievement has been achieved. */
bool m_achieved;
bool m_achieved;
/** The map of key-value pairs. */
std::map<std::string, int> m_progress_map;
/* When quitting the game, the achievement info is deleted before
* the achievement's status is saved. We need to store the id here
* to prevent saving junk data.
* FIXME: an achievement info should not be removed until all references
* to it have been too.*/
int m_id;
void onCompletion();
bool recursiveSetGoalValue(AchievementInfo::goalTree &tree, const std::string &goal_string, int value,
bool and_or, bool sum_andatonce);
bool recursiveCompletionCheck(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference);
protected:
friend class AchievementProgressDialog;
/** The tree of goals. It is identical to the
* goal tree of the matching AchievementInfo,
* except that the stored values represent the
* achieved values instead of the values to meet. */
AchievementInfo::goalTree m_progress_goal_tree;
/** A pointer to the corresponding AchievementInfo instance. */
const AchievementInfo *m_achievement_info;
void check();
AchievementInfo *m_achievement_info;
int computeFullfiledGoals(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference);
int computeGoalProgress(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference, bool same_tree=false);
public:
Achievement(const AchievementInfo * info);
Achievement(AchievementInfo * info);
virtual ~Achievement();
virtual void load(const XMLNode *node);
virtual void save(UTFWriter &out);
virtual int getValue(const std::string & key);
void increase(const std::string & key, const std::string &goal_key,
int increase = 1);
virtual void loadProgress(const XMLNode *node);
virtual void saveProgress(UTFWriter &out);
virtual irr::core::stringw getProgressAsString();
virtual irr::core::stringw getGoalProgressAsString();
uint32_t getID() const { return m_id; }
AchievementInfo * getInfo() { return m_achievement_info; }
virtual void reset();
virtual irr::core::stringw getProgressAsString() const;
void onRaceEnd();
void onLapEnd();
// ------------------------------------------------------------------------
/** Returns the id of this achievement. */
uint32_t getID() const { return m_id; }
// ------------------------------------------------------------------------
/** Returns the AchievementInfo for this achievement. */
const AchievementInfo * getInfo() const { return m_achievement_info; }
// ------------------------------------------------------------------------
/** Sets this achievement to be fulfilled. */
void setAchieved() { m_achieved = true; };
// ------------------------------------------------------------------------
/** Returns if this achievement has been fulfilled. */
bool isAchieved() const { return m_achieved; }
// ------------------------------------------------------------------------
const std::map<std::string, int>& getProgress() const
{
return m_progress_map;
} // getProgress
void setGoalValue(std::string &goal_string, int value);
}; // class Achievement
#endif

View File

@@ -18,6 +18,10 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "achievements/achievement_info.hpp"
#include "achievements/achievement.hpp"
#include "achievements/achievements_status.hpp"
#include "config/player_manager.hpp"
#include "utils/log.hpp"
#include <sstream>
@@ -29,7 +33,6 @@
*/
AchievementInfo::AchievementInfo(const XMLNode * input)
{
m_reset_type = NEVER;
m_id = 0;
m_name = "";
m_description = "";
@@ -47,121 +50,203 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
m_description.c_str());
}
// Load the reset-type
std::string s;
input->get("reset-type", &s);
if (s == "race")
m_reset_type = AFTER_RACE;
else if (s == "lap")
m_reset_type = AFTER_LAP;
else if (s != "never")
Log::warn("AchievementInfo", "Achievement check type '%s' unknown.",
s.c_str());
// Load check-type
m_check_type = AC_ALL_AT_LEAST;
input->get("check-type", &s);
if (s == "all-at-least")
m_check_type = AC_ALL_AT_LEAST;
else if (s == "one-at-least")
m_check_type = AC_ONE_AT_LEAST;
else
Log::warn("AchievementInfo", "Achievement check type '%s' unknown.",
s.c_str());
input->get("secret", &m_is_secret);
m_goal_tree.type = "AND";
m_goal_tree.value = -1;
m_goal_tree.operation = OP_NONE;
parseGoals(input, m_goal_tree);
} // AchievementInfo
// ----------------------------------------------------------------------------
/** Parses recursively the list of goals, to construct the tree of goals */
void AchievementInfo::parseGoals(const XMLNode * input, goalTree &parent)
{
// Now load the goal nodes
for (unsigned int n = 0; n < input->getNumNodes(); n++)
{
const XMLNode *node = input->getNode(n);
std::string key = node->getName();
int goal = 0;
node->get("goal", &goal);
m_goal_values[key] = goal;
}
if (m_goal_values.size() != input->getNumNodes())
Log::fatal("AchievementInfo",
"Duplicate keys for the entries of a MapAchievement found.");
if (node->getName() != "goal")
continue; // ignore incorrect node
if (m_check_type == AC_ONE_AT_LEAST)
{
if (m_goal_values.size() != 1)
Log::fatal("AchievementInfo",
"A one-at-least achievement must have exactly one goal.");
std::string type;
if(!node->get("type", &type))
continue; // missing type, ignore node
int value;
if (!node->get("value", &value))
value = -1;
std::string operation;
if (!node->get("operation", &operation))
operation = "none";
goalTree child;
child.type = type;
child.value = value;
if (operation == "none")
child.operation = OP_NONE;
else if (operation == "+")
child.operation = OP_ADD;
else if (operation == "-")
child.operation = OP_SUBSTRACT;
else
continue; // incorrect operation type, ignore node
if (type=="AND" || type=="AND-AT-ONCE" || type=="OR" || type=="SUM")
{
if (type == "SUM")
{
if (value <= 0)
continue; // SUM nodes need a strictly positive value
}
else
{
// Logical operators don't have a value or operation defined
if (value != -1)
continue;
if (child.operation != OP_NONE)
continue;
if (parent.type == "SUM")
continue;
}
parseGoals(node, child);
if (child.children.size() == 0)
continue;
}
else
{
if (value <= 0)
continue; // Leaf nodes need a strictly positive value
if (parent.type == "SUM" && child.operation == OP_NONE)
continue; // Leaf nodes of a SUM node need an operator
}
parent.children.push_back(child);
}
} // AchievementInfo
if (parent.children.size() != input->getNumNodes())
Log::error("AchievementInfo",
"Incorrect goals for the entries of achievement \"%s\".", m_name.c_str());
} // parseGoals
// ----------------------------------------------------------------------------
/** Returns a string with a numerical value to display the progress of
* this achievement. It adds up all the goal values
*/
irr::core::stringw AchievementInfo::toString() const
/** Copy a goal tree to an EMPTY goal tree by recursion. */
void AchievementInfo::copyGoalTree(goalTree &copy, goalTree &model, bool set_values_to_zero)
{
int count = 0;
std::map<std::string, int>::const_iterator iter;
switch (m_check_type)
{
case AC_ALL_AT_LEAST:
// If all values need to be reached, add up all goal values
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
{
count += iter->second;
}
break;
case AC_ONE_AT_LEAST:
// Only one goal is defined for a one-at-least
count = m_goal_values.begin()->second;
break;
default:
Log::fatal("AchievementInfo", "Missing toString for type %d.",
m_check_type);
}
return StringUtils::toWString(count);
copy.type = model.type;
copy.value = (set_values_to_zero) ? 0 : model.value;
copy.operation = model.operation;
} // toString
for (unsigned int i=0;i<model.children.size();i++)
{
goalTree copy_child;
copyGoalTree(copy_child, model.children[i],set_values_to_zero);
copy.children.push_back(copy_child);
}
} // copyGoalTree
// ----------------------------------------------------------------------------
bool AchievementInfo::checkCompletion(Achievement * achievement) const
/** Returns the goal tree's depth. If an AND/OR/ANT-AT-ONCE contains only
* one element, it is ignored (this is useful because the root is always
* AND ; so for e.g. an OR achievement, we prefer to not display it). */
int AchievementInfo::getRecursiveDepth(goalTree &parent)
{
std::map<std::string, int>::const_iterator iter;
switch (m_check_type)
if (parent.children.size() != 1)
{
case AC_ALL_AT_LEAST:
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
int max = 0;
for (unsigned int i=0;i<parent.children.size();i++)
{
if (achievement->getValue(iter->first) < iter->second)
return false;
int depth = getRecursiveDepth(parent.children[i]);
if (depth > max)
max = depth;
}
return true;
case AC_ONE_AT_LEAST:
{
// Test all progress values the kart has.
const std::map<std::string, int> &progress = achievement->getProgress();
for (iter = progress.begin(); iter != progress.end(); iter++)
{
// A one-at-least achievement has only one goal, so use it
if (iter->second >= m_goal_values.begin()->second)
return true;
}
return false;
return max+1;
}
else if (parent.children.size() == 1 &&
(parent.children[0].type == "AND" ||
parent.children[0].type == "AND-AT-ONCE" ||
parent.children[0].type == "OR"))
{
return getRecursiveDepth(parent.children[0]);
}
default:
Log::fatal("AchievementInfo", "Missing check for type %d.",
m_check_type);
} // switch
// Avoid compiler warning
return false;
}
// ----------------------------------------------------------------------------
int AchievementInfo::getGoalValue(const std::string &key) const
{
std::map<std::string, int>::const_iterator it;
it = m_goal_values.find(key);
if (it != m_goal_values.end())
return it->second;
else
return 0;
} // getGoalValue
return 1;
} // getRecursiveDepth
// ----------------------------------------------------------------------------
/** Returns a string with the number of goals to fullfil to
* get this achievements.
*/
irr::core::stringw AchievementInfo::goalString()
{
return StringUtils::toWString(recursiveGoalCount(m_goal_tree));
} // goalString
// ----------------------------------------------------------------------------
int AchievementInfo::recursiveGoalCount(goalTree &parent)
{
if (parent.children.size() >= 2 &&
parent.type != "OR")
{
return parent.children.size();
}
else if (parent.children.size() == 1 &&
(parent.children[0].type == "AND" ||
parent.children[0].type == "AND-AT-ONCE" ||
parent.children[0].type == "OR"))
{
return recursiveGoalCount(parent.children[0]);
}
else
return 1;
} // recursiveGoalCount
// ----------------------------------------------------------------------------
/** Returns a string with the target of the goal if the
* achievement has only one goal (a sum counts as one goal).
*/
irr::core::stringw AchievementInfo::progressString()
{
return StringUtils::toWString(recursiveProgressCount(m_goal_tree));
} // progressString
// ----------------------------------------------------------------------------
int AchievementInfo::recursiveProgressCount(goalTree &parent)
{
if (parent.children.size() != 1)
{
return -1; // signal that this is invalid.
}
else if (parent.children.size() == 1 &&
(parent.children[0].type == "AND" ||
parent.children[0].type == "AND-AT-ONCE" ||
parent.children[0].type == "OR"))
{
return recursiveGoalCount(parent.children[0]);
}
else
{
//TODO : find a more automatic way
if (parent.children[0].type == "race-started-all" ||
parent.children[0].type == "race-finished-all" ||
parent.children[0].type == "race-won-all" ||
parent.children[0].type == "race-finished-reverse-all" ||
parent.children[0].type == "race-finished-alone-all" ||
parent.children[0].type == "less-laps-all" ||
parent.children[0].type == "more-laps-all" ||
parent.children[0].type == "twice-laps-all" ||
parent.children[0].type == "egg-hunt-started-all" ||
parent.children[0].type == "egg-hunt-finished-all")
{
return PlayerManager::getCurrentAchievementsStatus()->getNumAchieveTracks();
}
else
{
return parent.children[0].value;
}
}
} // recursiveProgressCount

View File

@@ -20,7 +20,6 @@
#ifndef HEADER_ACHIEVEMENT_INFO_HPP
#define HEADER_ACHIEVEMENT_INFO_HPP
#include "achievements/achievement.hpp"
#include "io/xml_node.hpp"
#include "utils/translation.hpp"
#include "utils/types.hpp"
@@ -32,47 +31,30 @@
class Achievement;
/** This is the base class for storing the definition of an achievement, e.g.
* title, description (which is common for all achievements), but also how
* to achieve this achievement.
/** This class stores an achievement definition from the xml file, including
* title, description, but also how to achieve this achievement.
* Contrast with the Achievement class, which is a player-specific instance
* tracking the progress of the achievement.
* \ingroup achievements
*/
class AchievementInfo
{
public:
/** Some handy names for the various achievements. */
enum { ACHIEVE_COLUMBUS = 1,
ACHIEVE_FIRST = ACHIEVE_COLUMBUS,
ACHIEVE_STRIKE = 2,
ACHIEVE_ARCH_ENEMY = 3,
ACHIEVE_MARATHONER = 4,
ACHIEVE_SKIDDING = 5,
ACHIEVE_GOLD_DRIVER = 6,
ACHIEVE_POWERUP_LOVER = 7,
ACHIEVE_UNSTOPPABLE = 8,
ACHIEVE_BANANA = 9,
ACHIEVE_MOSQUITO = 11
// The operations supported for a goal
enum operationType {
OP_NONE = 0,
OP_ADD = 1,
OP_SUBSTRACT = 2,
};
/** Achievement check type:
* ALL_AT_LEAST: All goal values must be reached (or exceeded).
* ONE_AT_LEAST: At least one current value reaches or exceedes the goal.
*/
enum AchievementCheckType
{
AC_ALL_AT_LEAST,
AC_ONE_AT_LEAST
};
/** Achievement reset type:
* AFTER_LAP: Achievement needs to be reset after each lap.
* AFTER_RACE: Achievement needs to be reset after each race.
* NEVER: Achievement does not need to be reset
*/
enum ResetType
{
AFTER_LAP = 1,
AFTER_RACE = 2,
NEVER = 3
// We store goals in a recursive tree.
// This structure matching the algorithms
// we use to manipulate it simplify code.
struct goalTree {
std::string type;
int value;
operationType operation;
std::vector<goalTree> children;
};
private:
@@ -85,46 +67,34 @@ private:
/** The description of this achievement. */
irr::core::stringw m_description;
/** Determines how this achievement is checked if it is successful. */
AchievementCheckType m_check_type;
/** The target values needed to be reached. */
std::map<std::string, int> m_goal_values;
/** Determines when the achievement needs to be reset */
ResetType m_reset_type;
/** A secret achievement has its progress not shown. */
bool m_is_secret;
void parseGoals(const XMLNode * input, goalTree &parent);
int recursiveGoalCount(goalTree &parent);
int recursiveProgressCount(goalTree &parent);
int getRecursiveDepth(goalTree &parent);
protected:
friend class Achievement;
friend class AchievementProgressDialog;
/** The tree storing all goals */
goalTree m_goal_tree;
public:
AchievementInfo(const XMLNode * input);
virtual ~AchievementInfo() {};
virtual irr::core::stringw toString() const;
virtual bool checkCompletion(Achievement * achievement) const;
int getGoalValue(const std::string &key) const;
virtual irr::core::stringw goalString();
virtual irr::core::stringw progressString();
// ------------------------------------------------------------------------
/** Returns the id of this achievement. */
uint32_t getID() const { return m_id; }
// ------------------------------------------------------------------------
/** Returns the description of this achievement. */
int getDepth() { return getRecursiveDepth(m_goal_tree); }
uint32_t getID() const { return m_id; }
irr::core::stringw getDescription() const { return _(m_description.c_str()); }
// ------------------------------------------------------------------------
/** Returns the name of this achievement. */
irr::core::stringw getName() const { return _LTR(m_name.c_str()); }
// ------------------------------------------------------------------------
bool needsResetAfterRace() const { return m_reset_type == AFTER_RACE; }
// ------------------------------------------------------------------------
bool needsResetAfterLap() const { return m_reset_type == AFTER_LAP; }
// ------------------------------------------------------------------------
/** Returns the check type for this achievement. */
AchievementCheckType getCheckType() const { return m_check_type; }
// ------------------------------------------------------------------------
/** Returns if this achievement is a secret achievement. */
bool isSecret() const { return m_is_secret; }
// ------------------------------------------------------------------------
irr::core::stringw getName() const { return _LTR(m_name.c_str()); }
bool isSecret() const { return m_is_secret; }
// This function should not be called if copy already has children
void copyGoalTree(goalTree &copy, goalTree &model, bool set_values_to_zero);
}; // class AchievementInfo

View File

@@ -86,6 +86,8 @@ AchievementsStatus*
status->add(achievement);
}
status->updateAllAchievementsProgress();
if (node)
status->load(node);

View File

@@ -24,6 +24,8 @@
#include "achievements/achievements_manager.hpp"
#include "config/player_manager.hpp"
#include "io/utf_writer.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/log.hpp"
#include "utils/ptr_vector.hpp"
#include "utils/translation.hpp"
@@ -41,6 +43,30 @@ AchievementsStatus::AchievementsStatus()
{
m_valid = true;
m_online = true;
for (unsigned int i=0; i<ACHIEVE_DATA_NUM; i++)
{
m_variables[i].counter = 0;
}
// Create one TrackStats instance for all existing tracks
const unsigned int track_amount = track_manager->getNumberOfTracks();
for (unsigned int n = 0; n < track_amount; n++)
{
Track* curr = track_manager->getTrack(n);
if (curr->isArena() || curr->isSoccer()||curr->isInternal()) continue;
TrackStats new_track;
new_track.ident = curr->getIdent();
for (unsigned int i=0;i<TR_DATA_NUM;i++)
{
new_track.track_data[i] = 0;
}
m_track_stats.push_back(new_track);
} // for n<track_amount
setEnumToString();
} // AchievementsStatus
// ----------------------------------------------------------------------------
@@ -55,6 +81,90 @@ AchievementsStatus::~AchievementsStatus()
m_achievements.clear();
} // ~AchievementsStatus
/** This function loads a table associating an enum identifier
* with the matching command in achievements.xml.
* counters with anassociated max version are prefixed to allow
* the achievement progress update to do the correct action. */
void AchievementsStatus::setEnumToString()
{
m_ach_enum_to_xml[(int)WON_RACES] = "won-races";
m_ach_enum_to_xml[(int)WON_NORMAL_RACES] = "won-normal-races";
m_ach_enum_to_xml[(int)WON_TT_RACES] = "won-tt-races";
m_ach_enum_to_xml[(int)WON_FTL_RACES] = "won-ftl-races";
m_ach_enum_to_xml[(int)CONS_WON_RACES] = "LOGC-cons-won-races";
m_ach_enum_to_xml[(int)CONS_WON_RACES_MAX] = "LOGM-cons-won-races";
m_ach_enum_to_xml[(int)CONS_WON_RACES_HARD] = "LOGC-cons-won-races-hard";
m_ach_enum_to_xml[(int)CONS_WON_RACES_HARD_MAX] = "LOGM-cons-won-races-hard";
m_ach_enum_to_xml[(int)EASY_STARTED] = "easy-started";
m_ach_enum_to_xml[(int)EASY_FINISHED] = "easy-finished";
m_ach_enum_to_xml[(int)MEDIUM_STARTED] = "medium-started";
m_ach_enum_to_xml[(int)MEDIUM_FINISHED] = "medium-finished";
m_ach_enum_to_xml[(int)HARD_STARTED] = "hard-started";
m_ach_enum_to_xml[(int)HARD_FINISHED] = "hard-finished";
m_ach_enum_to_xml[(int)BEST_STARTED] = "best-started";
m_ach_enum_to_xml[(int)BEST_FINISHED] = "best-finished";
m_ach_enum_to_xml[(int)NORMAL_STARTED] = "normal-started";
m_ach_enum_to_xml[(int)NORMAL_FINISHED] = "normal-finished";
m_ach_enum_to_xml[(int)TT_STARTED] = "tt-started";
m_ach_enum_to_xml[(int)TT_FINISHED] = "tt-finished";
m_ach_enum_to_xml[(int)FTL_STARTED] = "ftl-started";
m_ach_enum_to_xml[(int)FTL_FINISHED] = "ftl-finished";
m_ach_enum_to_xml[(int)THREE_STRIKES_STARTED] = "three-strikes-started";
m_ach_enum_to_xml[(int)THREE_STRIKES_FINISHED] = "three-strikes-finished";
m_ach_enum_to_xml[(int)SOCCER_STARTED] = "soccer-started";
m_ach_enum_to_xml[(int)SOCCER_FINISHED] = "soccer-finished";
m_ach_enum_to_xml[(int)EGG_HUNT_STARTED] = "egg-hunt-started";
m_ach_enum_to_xml[(int)EGG_HUNT_FINISHED] = "egg-hunt-finished";
m_ach_enum_to_xml[(int)WITH_GHOST_STARTED] = "with-ghost-started";
m_ach_enum_to_xml[(int)WITH_GHOST_FINISHED] = "with-ghost-finished";
m_ach_enum_to_xml[(int)CTF_STARTED] = "ctf-started";
m_ach_enum_to_xml[(int)CTF_FINISHED] = "ctf-finished";
m_ach_enum_to_xml[(int)FFA_STARTED] = "ffa-started";
m_ach_enum_to_xml[(int)FFA_FINISHED] = "ffa-finished";
m_ach_enum_to_xml[(int)POWERUP_USED] = "powerup-used";
m_ach_enum_to_xml[(int)POWERUP_USED_1RACE] = "LOGC-powerup-used-1race";
m_ach_enum_to_xml[(int)POWERUP_USED_1RACE_MAX] = "LOGM-powerup-used-1race";
m_ach_enum_to_xml[(int)BOWLING_HIT] = "bowling-hit";
m_ach_enum_to_xml[(int)BOWLING_HIT_1RACE] = "LOGC-bowling-hit-1race";
m_ach_enum_to_xml[(int)BOWLING_HIT_1RACE_MAX] = "LOGM-bowling-hit-1race";
m_ach_enum_to_xml[(int)SWATTER_HIT] = "swatter-hit";
m_ach_enum_to_xml[(int)SWATTER_HIT_1RACE] = "LOGC-swatter-hit-1race";
m_ach_enum_to_xml[(int)SWATTER_HIT_1RACE_MAX] = "LOGM-swatter-hit-1race";
m_ach_enum_to_xml[(int)ALL_HITS] = "all-hits";
m_ach_enum_to_xml[(int)ALL_HITS_1RACE] = "LOGC-all-hits-1race";
m_ach_enum_to_xml[(int)ALL_HITS_1RACE_MAX] = "LOGM-all-hits-1race";
m_ach_enum_to_xml[(int)BANANA] = "banana";
m_ach_enum_to_xml[(int)BANANA_1RACE] = "LOGC-banana-1race";
m_ach_enum_to_xml[(int)BANANA_1RACE_MAX] = "LOGM-banana-1race";
m_ach_enum_to_xml[(int)SKIDDING] = "skidding";
m_ach_enum_to_xml[(int)SKIDDING_1RACE] = "LOGC-skidding-1race";
m_ach_enum_to_xml[(int)SKIDDING_1RACE_MAX] = "LOGM-skidding-1race";
m_ach_enum_to_xml[(int)SKIDDING_1LAP] = "LOGC-skidding-1lap";
m_ach_enum_to_xml[(int)SKIDDING_1LAP_MAX] = "LOGM-skidding-1lap";
m_tr_enum_to_xml[(int)TR_STARTED] = "race-started";
m_tr_enum_to_xml[(int)TR_FINISHED] = "race-finished";
m_tr_enum_to_xml[(int)TR_WON] = "race-won";
m_tr_enum_to_xml[(int)TR_FINISHED_REVERSE] = "race-finished-reverse";
m_tr_enum_to_xml[(int)TR_LESS_LAPS] = "less-laps";
m_tr_enum_to_xml[(int)TR_MORE_LAPS] = "more-laps";
m_tr_enum_to_xml[(int)TR_MIN_TWICE_LAPS] = "twice-laps";
m_tr_enum_to_xml[(int)TR_FINISHED_ALONE] = "race-finished-alone";
m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED] = "egg-hunt-started";
m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED] = "egg-hunt-started";
m_tr_enum_to_xml[(int)TR_STARTED + (int)TR_DATA_NUM] = "race-started-all";
m_tr_enum_to_xml[(int)TR_FINISHED + (int)TR_DATA_NUM] = "race-finished-all";
m_tr_enum_to_xml[(int)TR_WON + (int)TR_DATA_NUM] = "race-won-all";
m_tr_enum_to_xml[(int)TR_FINISHED_REVERSE + (int)TR_DATA_NUM] = "race-finished-reverse-all";
m_tr_enum_to_xml[(int)TR_LESS_LAPS + (int)TR_DATA_NUM] = "less-laps-all";
m_tr_enum_to_xml[(int)TR_MORE_LAPS + (int)TR_DATA_NUM] = "more-laps-all";
m_tr_enum_to_xml[(int)TR_MIN_TWICE_LAPS + (int)TR_DATA_NUM] = "twice-laps-all";
m_tr_enum_to_xml[(int)TR_FINISHED_ALONE + (int)TR_DATA_NUM] = "race-finished-alone-all";
m_tr_enum_to_xml[(int)TR_EGG_HUNT_STARTED + (int)TR_DATA_NUM] = "egg-hunt-started-all";
m_tr_enum_to_xml[(int)TR_EGG_HUNT_FINISHED + (int)TR_DATA_NUM] = "egg-hunt-started-all";
} // setEnumToString
// ----------------------------------------------------------------------------
/** Loads the saved state of all achievements from an XML file.
* \param input The XML node to load the data from.
@@ -75,11 +185,98 @@ void AchievementsStatus::load(const XMLNode * input)
"achievement. Discarding.");
continue;
}
achievement->load(xml_achievements[i]);
achievement->loadProgress(xml_achievements[i]);
} // for i in xml_achievements
// Load achievement data
int data_version = -1;
const XMLNode *n = input->getNode("data");
if (n!=NULL)
n->get("version", &data_version);
if (data_version == DATA_VERSION)
{
std::vector<XMLNode*> xml_achievement_data;
input->getNodes("var", xml_achievement_data);
for (unsigned int i = 0; i < xml_achievement_data.size(); i++)
{
if (i>=ACHIEVE_DATA_NUM)
{
Log::warn("AchievementsStatus",
"Found more saved achievement data "
"than there should be. Discarding.");
continue;
}
xml_achievement_data[i]->get("counter",&m_variables[i].counter);
}
// Load track usage data
std::vector<XMLNode*> xml_achievement_tracks;
input->getNodes("track_stats", xml_achievement_tracks);
//FIXME : while this doesn't happen in a performance-critical part
// of the code, this double-loop is not very efficient.
for (unsigned int i=0; i < xml_achievement_tracks.size(); i++)
{
bool track_found = false;
std::string ident;
xml_achievement_tracks[i]->get("ident",&ident);
// We go over already loaded tracks to avoid duplicates
for (unsigned int j=0 ; j < m_track_stats.size(); j++)
{
if (ident == m_track_stats[j].ident)
{
xml_achievement_tracks[i]->get("sta",&m_track_stats[j].track_data[(int)TR_STARTED]);
xml_achievement_tracks[i]->get("fin",&m_track_stats[j].track_data[(int)TR_FINISHED]);
xml_achievement_tracks[i]->get("won",&m_track_stats[j].track_data[(int)TR_WON]);
xml_achievement_tracks[i]->get("fin_rev",&m_track_stats[j].track_data[(int)TR_FINISHED_REVERSE]);
xml_achievement_tracks[i]->get("fin_al",&m_track_stats[j].track_data[(int)TR_FINISHED_ALONE]);
xml_achievement_tracks[i]->get("l_laps",&m_track_stats[j].track_data[(int)TR_LESS_LAPS]);
xml_achievement_tracks[i]->get("m_laps",&m_track_stats[j].track_data[(int)TR_MORE_LAPS]);
xml_achievement_tracks[i]->get("t_laps",&m_track_stats[j].track_data[(int)TR_MIN_TWICE_LAPS]);
xml_achievement_tracks[i]->get("eh_sta",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_STARTED]);
xml_achievement_tracks[i]->get("eh_fin",&m_track_stats[j].track_data[(int)TR_EGG_HUNT_FINISHED]);
track_found = true;
break;
}
}
// Useful if, e.g. an addon track gets deleted
if (!track_found)
{
TrackStats new_track;
new_track.ident = ident;
xml_achievement_tracks[i]->get("sta",&new_track.track_data[(int)TR_STARTED]);
xml_achievement_tracks[i]->get("fin",&new_track.track_data[(int)TR_FINISHED]);
xml_achievement_tracks[i]->get("won",&new_track.track_data[(int)TR_WON]);
xml_achievement_tracks[i]->get("fin_rev",&new_track.track_data[(int)TR_FINISHED_REVERSE]);
xml_achievement_tracks[i]->get("fin_al",&new_track.track_data[(int)TR_FINISHED_ALONE]);
xml_achievement_tracks[i]->get("l_laps",&new_track.track_data[(int)TR_LESS_LAPS]);
xml_achievement_tracks[i]->get("m_laps",&new_track.track_data[(int)TR_MORE_LAPS]);
xml_achievement_tracks[i]->get("t_laps",&new_track.track_data[(int)TR_MIN_TWICE_LAPS]);
xml_achievement_tracks[i]->get("eh_sta",&new_track.track_data[(int)TR_EGG_HUNT_STARTED]);
xml_achievement_tracks[i]->get("eh_fin",&new_track.track_data[(int)TR_EGG_HUNT_FINISHED]);
m_track_stats.push_back(new_track);
}
}
}
// If there is nothing valid to load ; we keep the init values
// Now that we have retrieved the counters data, we can set
// the progress of the achievements - it isn't saved directly.
updateAllAchievementsProgress();
} // load
void AchievementsStatus::updateAllAchievementsProgress()
{
for (int i=0;i<ACHIEVE_DATA_NUM;i++)
{
updateAchievementsProgress(UP_ACHIEVEMENT_DATA, i);
}
for (int i=0;i<TR_DATA_NUM;i++)
{
updateAchievementsProgress(UP_TRACK_DATA, i);
}
updateAchievementsProgress(UP_KART_HITS, 0);
}
// ----------------------------------------------------------------------------
void AchievementsStatus::add(Achievement *achievement)
{
@@ -99,8 +296,28 @@ void AchievementsStatus::save(UTFWriter &out)
for(i = m_achievements.begin(); i != m_achievements.end(); i++)
{
if (i->second != NULL)
i->second->save(out);
i->second->saveProgress(out);
}
out << " <data version=\"" << DATA_VERSION << "\"/>\n";
for(int i=0;i<ACHIEVE_DATA_NUM;i++)
{
out << " <var counter=\"" << m_variables[i].counter << "\"/>\n";
}
for (unsigned int n = 0; n < m_track_stats.size(); n++)
{
out << " <track_stats ident=\"" << m_track_stats[n].ident << "\"";
out << " sta=\"" << m_track_stats[n].track_data[(int)TR_STARTED] << "\"";
out << " fin=\"" << m_track_stats[n].track_data[(int)TR_FINISHED] << "\"";
out << " won=\"" << m_track_stats[n].track_data[(int)TR_WON] << "\"";
out << " fin_rev=\"" << m_track_stats[n].track_data[(int)TR_FINISHED_REVERSE] << "\"";
out << " fin_al=\"" << m_track_stats[n].track_data[(int)TR_FINISHED_ALONE] << "\"";
out << " l_laps=\"" << m_track_stats[n].track_data[(int)TR_LESS_LAPS] << "\"";
out << " m_laps=\"" << m_track_stats[n].track_data[(int)TR_MORE_LAPS] << "\"";
out << " t_laps=\"" << m_track_stats[n].track_data[(int)TR_MIN_TWICE_LAPS] << "\"";
out << " eh_sta=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_STARTED] << "\"";
out << " eh_fin=\"" << m_track_stats[n].track_data[(int)TR_EGG_HUNT_FINISHED] << "\"";
out << "/>\n";
} // for n<m_track_stats.size()
out << " </achievements>\n";
} // save
@@ -158,21 +375,258 @@ void AchievementsStatus::sync(const std::vector<uint32_t> & achieved_ids)
} // sync
// ----------------------------------------------------------------------------
void AchievementsStatus::onRaceEnd()
/* This function returns for how many tracks the requested goal type
* is matched or exceeded. Addons tracks are ignored.
* It returns -1 if the goal type is invalid.
* \param value - the value to match or exceed
* \param goal_string - the identifier of the value to check. */
int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_string)
{
//reset all values that need to be reset
std::map<uint32_t, Achievement *>::iterator iter;
for ( iter = m_achievements.begin(); iter != m_achievements.end(); ++iter ) {
iter->second->onRaceEnd();
int counter = 0;
int enum_id = -1;
for (int i=0;i<2*(int)TR_DATA_NUM;i++)
{
if (m_tr_enum_to_xml[i] == goal_string)
enum_id = i%(int)TR_DATA_NUM;
}
if (enum_id == -1)
{
Log::warn("AchievementsStatus",
"Number of matching tracks requested for a non-existent "
"data value.");
return -1;
}
for (unsigned int i=0;i<m_track_stats.size();i++)
{
// ignore addons tracks (compare returns 0 when the values are equal)
// Note: non-official tracks installed directly in the tracks folder
// are considered as officials by this method.
if (m_track_stats[i].ident.compare(0 /*start of sub-string*/,5/*length*/,"addon") == 0)
continue;
if (m_track_stats[i].track_data[enum_id] >= value)
counter++;
}
return counter;
} // getNumTracksAboveValue
// ----------------------------------------------------------------------------
/* This function returns the number of tracks valid for by-track achievements. */
int AchievementsStatus::getNumAchieveTracks()
{
int num_tracks = 0;
for (unsigned int i=0;i<m_track_stats.size();i++)
{
// TODO : have a generic function to call instead
// ignore addons tracks (compare returns 0 when the values are equal)
if (m_track_stats[i].ident.compare(0 /*start of sub-string*/,5/*length*/,"addon") == 0)
continue;
num_tracks++;
}
return num_tracks;
} //getNumAchieveTracks
// ----------------------------------------------------------------------------
/* This function checks over achievements to update their goals
* \param type - the data type triggering the update, used to know
* to what enum the enum_id refers to.
* \param enum_id - the id of the enum identifying the change triggering
* the update. */
void AchievementsStatus::updateAchievementsProgress(UpdateType type, unsigned int enum_id)
{
std::string goal_string[2];
int max_across_tracks = -1;
int min_across_tracks = -1;
int max_kart_hits = 0;
if (type == UP_ACHIEVEMENT_DATA)
{
goal_string[0] = m_ach_enum_to_xml[(int)enum_id];
} // if type == UP_ACHIEVEMENT_DATA)
else if (type == UP_TRACK_DATA)
{
goal_string[0] = m_tr_enum_to_xml[(int)enum_id]; // The "one-track at least" goal
goal_string[1] = m_tr_enum_to_xml[(int)enum_id+(int)TR_DATA_NUM]; // The "all tracks" goal
for (unsigned int i=0;i<m_track_stats.size();i++)
{
// ignore addons tracks (compare returns 0 when the values are equal)
// Note: non-official tracks installed directly in the tracks folder
// are considered as officials by this method.
if (m_track_stats[i].ident.compare(0 /*start of sub-string*/,5/*length*/,"addon") == 0)
continue;
if (m_track_stats[i].track_data[enum_id] > max_across_tracks)
max_across_tracks = m_track_stats[i].track_data[enum_id];
if (m_track_stats[i].track_data[enum_id] < min_across_tracks)
min_across_tracks = m_track_stats[i].track_data[enum_id];
}
} // if type == UP_TRACK_DATA
else if (type == UP_KART_HITS)
{
goal_string[0] = "hit-same-kart-1race";
for (unsigned int i=0;i<m_kart_hits.size();i++)
{
if (m_kart_hits[i] > max_kart_hits)
max_kart_hits = m_kart_hits[i];
}
} // if type == UP_KART_HITS
// Now that we know what string to look for, call an Achievement function
// which will look throughout the progress goalTree to update it
std::map<uint32_t, Achievement*>::const_iterator i;
for(i=m_achievements.begin(); i!=m_achievements.end(); i++)
{
// Don't bother checking again already completed achievements
if (i->second->isAchieved())
continue;
if (type == UP_ACHIEVEMENT_DATA)
{
i->second->setGoalValue(goal_string[0],m_variables[enum_id].counter);
}
else if (type == UP_TRACK_DATA)
{
i->second->setGoalValue(goal_string[0],max_across_tracks);
i->second->setGoalValue(goal_string[1],min_across_tracks);
}
else if (type == UP_KART_HITS)
{
i->second->setGoalValue(goal_string[0],max_kart_hits);
}
}
}
// ----------------------------------------------------------------------------
void AchievementsStatus::increaseDataVar(unsigned int achieve_data_id, int increase)
{
if (achieve_data_id<ACHIEVE_DATA_NUM)
{
m_variables[achieve_data_id].counter += increase;
updateAchievementsProgress(UP_ACHIEVEMENT_DATA, achieve_data_id);
}
#ifdef DEBUG
else
{
Log::error("Achievements", "Achievement data id %i don't match any variable.",
achieve_data_id);
}
#endif
} // increaseDataVar
// ----------------------------------------------------------------------------
void AchievementsStatus::resetDataVar(unsigned int achieve_data_id)
{
if (achieve_data_id<ACHIEVE_DATA_NUM)
{
m_variables[achieve_data_id].counter = 0;
}
#ifdef DEBUG
else
{
Log::error("Achievements", "Achievement data id %i don't match any variable.",
achieve_data_id);
}
#endif
} // resetDataVar
// ----------------------------------------------------------------------------
void AchievementsStatus::onRaceEnd(bool aborted)
{
onLapEnd();
if (m_variables[POWERUP_USED_1RACE].counter > m_variables[POWERUP_USED_1RACE_MAX].counter)
m_variables[POWERUP_USED_1RACE_MAX].counter = m_variables[POWERUP_USED_1RACE].counter;
if (m_variables[BANANA_1RACE].counter > m_variables[BANANA_1RACE_MAX].counter)
m_variables[BANANA_1RACE_MAX].counter = m_variables[BANANA_1RACE].counter;
if (m_variables[SKIDDING_1RACE].counter > m_variables[SKIDDING_1RACE_MAX].counter)
m_variables[SKIDDING_1RACE_MAX].counter = m_variables[SKIDDING_1RACE].counter;
if (m_variables[BOWLING_HIT_1RACE].counter > m_variables[BOWLING_HIT_1RACE_MAX].counter)
m_variables[BOWLING_HIT_1RACE_MAX].counter = m_variables[BOWLING_HIT_1RACE].counter;
if (m_variables[SWATTER_HIT_1RACE].counter > m_variables[SWATTER_HIT_1RACE_MAX].counter)
m_variables[SWATTER_HIT_1RACE_MAX].counter = m_variables[SWATTER_HIT_1RACE].counter;
if (m_variables[ALL_HITS_1RACE].counter > m_variables[ALL_HITS_1RACE_MAX].counter)
m_variables[ALL_HITS_1RACE_MAX].counter = m_variables[ALL_HITS_1RACE].counter;
m_variables[POWERUP_USED_1RACE].counter = 0;
m_variables[BANANA_1RACE].counter = 0;
m_variables[SKIDDING_1RACE].counter = 0;
m_variables[BOWLING_HIT_1RACE].counter = 0;
m_variables[SWATTER_HIT_1RACE].counter = 0;
m_variables[ALL_HITS_1RACE].counter = 0;
if (m_variables[CONS_WON_RACES].counter > m_variables[CONS_WON_RACES_MAX].counter)
m_variables[CONS_WON_RACES_MAX].counter = m_variables[CONS_WON_RACES].counter;
if (m_variables[CONS_WON_RACES_HARD].counter > m_variables[CONS_WON_RACES_HARD_MAX].counter)
m_variables[CONS_WON_RACES_HARD_MAX].counter = m_variables[CONS_WON_RACES_HARD].counter;
// Prevent restart from being abused to get consecutive wins achievement
if (aborted)
{
m_variables[CONS_WON_RACES].counter = 0;
m_variables[CONS_WON_RACES_HARD].counter = 0;
}
updateAllAchievementsProgress();
} // onRaceEnd
// ----------------------------------------------------------------------------
void AchievementsStatus::onLapEnd()
{
//reset all values that need to be reset
std::map<uint32_t, Achievement *>::iterator iter;
for (iter = m_achievements.begin(); iter != m_achievements.end(); ++iter) {
iter->second->onLapEnd();
}
if (m_variables[SKIDDING_1LAP].counter > m_variables[SKIDDING_1LAP_MAX].counter)
m_variables[SKIDDING_1LAP_MAX].counter = m_variables[SKIDDING_1LAP].counter;
m_variables[SKIDDING_1LAP].counter = 0;
updateAchievementsProgress(UP_ACHIEVEMENT_DATA, (int)SKIDDING_1LAP);
} // onLapEnd
// ----------------------------------------------------------------------------
/** Use the event type to increment the correct track event counter
* \param track_ident - the internal name of the track
* \param event - the type of counter to increment */
void AchievementsStatus::trackEvent(std::string track_ident, AchievementsStatus::TrackData event)
{
int track_id = -1;
for (unsigned int i=0;i<m_track_stats.size();i++)
{
if (m_track_stats[i].ident == track_ident)
{
track_id = i;
break;
}
}
// This can happen for e.g. cutscenes.
// In this case, don't try to update the track data
// TODO : Update the track list upon addon installation
if (track_id == -1)
return;
m_track_stats[track_id].track_data[(int)event]++;
updateAchievementsProgress(UP_TRACK_DATA, (int)event);
} // trackEvent
// ----------------------------------------------------------------------------
void AchievementsStatus::resetKartHits(int num_karts)
{
m_kart_hits.clear();
m_kart_hits.resize(num_karts);
} // resetKartHits
// ----------------------------------------------------------------------------
void AchievementsStatus::addKartHit(int kart_id)
{
m_kart_hits[kart_id]++;
updateAchievementsProgress(UP_KART_HITS, 0);
} // addKartHit

View File

@@ -38,12 +38,172 @@ class XMLNode;
* an achievement (e.g. an achievement to race every track in STK needs
* to keep information about which tracks have already been used.)
*/
class AchievementsStatus
{
public :
// Warning : changing what an existing id does breaks
// save-game compatibility. Bump version number if doing so.
enum AchievementData {
// Won races values share the following properties :
// 1. Only races with at least 3 AI count unless otherwise specified.
WON_RACES = 0, // Normal, time-trial and FTL
WON_NORMAL_RACES = 1, // Normal race only
WON_TT_RACES = 2, // Time-trial race only
WON_FTL_RACES = 3, // Follow-the-leader race only
// Consecutive won race values :
// 1. Ignore races with not enough AIs for incrementation
// 2. Reset the counter in case of loss against any number of AIs
CONS_WON_RACES = 4,
CONS_WON_RACES_MAX = 5,
// Won races in (at least) hard requires at least 5 AI opponents
CONS_WON_RACES_HARD = 6,
CONS_WON_RACES_HARD_MAX = 7,
// Count how many normal, TT & FTL races were started and finished by difficulty
EASY_STARTED = 8,
EASY_FINISHED = 9,
MEDIUM_STARTED = 10,
MEDIUM_FINISHED = 11,
HARD_STARTED = 12,
HARD_FINISHED = 13,
BEST_STARTED = 14,
BEST_FINISHED = 15,
// Count how many time a race/match was started and finished by game mode.
// Races with ghost replays technically belong to TT or egg hunt race mode,
// they increment both the with_ghost counter and the relevant mode counter.
NORMAL_STARTED = 16,
NORMAL_FINISHED = 17,
TT_STARTED = 18,
TT_FINISHED = 19,
FTL_STARTED = 20,
FTL_FINISHED = 21,
THREE_STRIKES_STARTED = 22,
THREE_STRIKES_FINISHED = 23,
SOCCER_STARTED = 24,
SOCCER_FINISHED = 25,
EGG_HUNT_STARTED = 26,
EGG_HUNT_FINISHED = 27,
WITH_GHOST_STARTED = 28,
WITH_GHOST_FINISHED = 29,
CTF_STARTED = 30,
CTF_FINISHED = 31,
FFA_STARTED = 32,
FFA_FINISHED = 33,
// Count the number of powerups used by the player.
POWERUP_USED = 34,
POWERUP_USED_1RACE = 35,
POWERUP_USED_1RACE_MAX = 36,
// Count how many times a bowling ball from the player hit a kart
BOWLING_HIT = 37,
BOWLING_HIT_1RACE = 38,
BOWLING_HIT_1RACE_MAX = 39,
// Count how many times a swatter from the player hit a kart
SWATTER_HIT = 40,
SWATTER_HIT_1RACE = 41,
SWATTER_HIT_1RACE_MAX = 42,
// Count how many times a swatter, bowling ball or cake from
// the player hit a kart (excluding the player's own kart)
ALL_HITS = 43,
ALL_HITS_1RACE = 44,
ALL_HITS_1RACE_MAX = 45,
// Count the number of bananas hit
BANANA = 46,
BANANA_1RACE = 47,
BANANA_1RACE_MAX = 48,
// Count how many times the player skidded
SKIDDING = 49,
SKIDDING_1RACE = 50,
SKIDDING_1RACE_MAX = 51,
SKIDDING_1LAP = 52,
SKIDDING_1LAP_MAX = 53,
ACHIEVE_DATA_NUM = 54
};
private:
std::map<uint32_t, Achievement *> m_achievements;
bool m_online;
bool m_valid;
// Variables used to track achievements progress,
// one variable may be used by several achievements.
// TODO
// Currently this only uses an int counter.
// Evaluate if additional data keeping (max achived ?) can be useful,
// and either expand the struct or remove it.
struct AchievementVariable
{
int counter;
};
const int DATA_VERSION = 4;
// The tracked values are defined at compile time
AchievementVariable m_variables[ACHIEVE_DATA_NUM];
// We store the enum name and matching goalTree type
// in this table for faster lookup.
std::string m_ach_enum_to_xml[ACHIEVE_DATA_NUM];
// Switching a few times from public to private
// helps here to keep related things next to each other
public:
enum TrackData {
// counters for standard, TT & FTL races
TR_STARTED = 0,
TR_FINISHED = 1,
// doesn't count race without any other AI/player
TR_WON = 2,
TR_FINISHED_REVERSE = 3,
// races against replays are counted, too
TR_FINISHED_ALONE = 4,
// counters for standard & TT races, apply to finished races only,
// lap number compared to track default.
TR_LESS_LAPS = 5,
TR_MORE_LAPS = 6,
TR_MIN_TWICE_LAPS = 7, // at least twice the track's default lap count
// counters for egg hunts
TR_EGG_HUNT_STARTED = 8,
TR_EGG_HUNT_FINISHED = 9,
TR_DATA_NUM = 10
};
private:
// To keep track of track-specific data without hardcoding
// a list of tracks, we use a special structure.
struct TrackStats
{
std::string ident;
int track_data[TR_DATA_NUM];
};
std::vector<TrackStats> m_track_stats;
// We store the enum name and matching goalTree type
// in this table for faster lookup.
// Each track data value matches 2 xml command
std::string m_tr_enum_to_xml[2*TR_DATA_NUM];
// TODO : keep track of battle/soccer arenas
// Keeps track of hits inflicted to other karts,
// identified by their world id.
// Reset at the beginning of a race
std::vector<int> m_kart_hits;
// To avoid updating achievement progress being
// too computationally wasteful, we restrain
// what is checked on an update
enum UpdateType
{
UP_ACHIEVEMENT_DATA = 0,
UP_TRACK_DATA = 1,
UP_KART_HITS = 2,
};
bool m_online;
bool m_valid;
class SyncAchievementsRequest : public Online::XMLRequest {
virtual void callback ();
@@ -51,6 +211,9 @@ private:
SyncAchievementsRequest() : Online::XMLRequest(true) {}
};
void setEnumToString();
void updateAchievementsProgress(UpdateType type, unsigned int enum_id);
public :
AchievementsStatus();
~AchievementsStatus();
@@ -59,10 +222,18 @@ public :
void save(UTFWriter &out);
void add(Achievement *achievement);
void sync(const std::vector<uint32_t> & achieved_ids);
void onRaceEnd();
void increaseDataVar(unsigned int achieve_data_id, int increase);
void resetDataVar(unsigned int achieve_data_id);
void onRaceEnd(bool aborted=false);
void onLapEnd();
void trackEvent(std::string track_ident, AchievementsStatus::TrackData event);
void resetKartHits(int num_karts);
void addKartHit(int kart_id);
void updateAllAchievementsProgress();
int getNumTracksAboveValue(int value, std::string goal_string);
int getNumAchieveTracks();
// ------------------------------------------------------------------------
const std::map<uint32_t, Achievement *>& getAllAchievements()
std::map<uint32_t, Achievement *>& getAllAchievements()
{
return m_achievements;
}

View File

@@ -41,9 +41,10 @@ AnimationBase::AnimationBase(const XMLNode &node)
m_playing = true;
m_anim_type = ATT_CYCLIC;
if (m_all_ipos.size() == 0) // this will happen for some separate but non-animated objects
if (m_all_ipos.size() == 0)
{
m_playing = false;
// Throw to avoid construction completely
throw std::runtime_error("Empty IPO, discard.");
}
reset();
calculateAnimationDuration();

View File

@@ -70,8 +70,15 @@ ThreeDAnimation::~ThreeDAnimation()
// ----------------------------------------------------------------------------
/** Updates position and rotation of this model. Called once per time step.
*/
void ThreeDAnimation::updateWithWorldTicks()
void ThreeDAnimation::updateWithWorldTicks(bool has_physics)
{
const bool track_object_with_physics =
m_object->getPhysicalObject() != nullptr;
if ((has_physics && !track_object_with_physics) ||
(!has_physics && track_object_with_physics))
return;
Vec3 xyz = m_object->getPosition();
Vec3 scale = m_object->getScale();

View File

@@ -68,13 +68,12 @@ private:
*/
bool m_important_animation;
public:
ThreeDAnimation(const XMLNode &node, TrackObject* object);
virtual ~ThreeDAnimation();
virtual void update(float dt) {}
// ------------------------------------------------------------------------
void updateWithWorldTicks();
void updateWithWorldTicks(bool with_physics);
// ------------------------------------------------------------------------
/** Returns true if a collision with this object should
* trigger a rescue. */

View File

@@ -44,8 +44,11 @@ ChallengeData::ChallengeData(const std::string& filename)
m_gp_id = "";
m_version = 0;
m_num_trophies = 0;
m_num_completed_challenges = 0;
m_is_unlock_list = false;
m_is_ghost_replay = false;
m_unlock_special_type = SPECIAL_NONE;
m_unlock_special_value = -1;
for (int d=0; d<RaceManager::DIFFICULTY_COUNT; d++)
{
@@ -120,6 +123,18 @@ ChallengeData::ChallengeData(const std::string& filename)
}
requirements_node->get("trophies", &m_num_trophies);
requirements_node->get("challenges", &m_num_completed_challenges);
if (m_is_unlock_list)
{
requirements_node = root->getNode("alt_requirements");
if (requirements_node != NULL)
{
if(requirements_node->get("max-req-in-lower-diff", &m_unlock_special_value))
m_unlock_special_type = SPECIAL_MAX_REQ_IN_LOWER_DIFF;
}
}
//Don't check further if this is an unlock list
if(m_is_unlock_list)
return;
@@ -268,7 +283,6 @@ ChallengeData::ChallengeData(const std::string& filename)
// This is optional
int energy = -1;
if (requirements_node->get("energy", &energy)) m_energy[d] = energy;
}
} // ChallengeData
@@ -454,8 +468,12 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const
// ----------------------------------------------------------------------------
/** Returns true if this (non-GP) challenge is fulfilled.
* \param check_best : if true, check if the requirement
* for the best difficulty are met at a lower one.
* (requires SuperTux challenges to have a time
ù requirement to make sense)
*/
bool ChallengeData::isChallengeFulfilled() const
bool ChallengeData::isChallengeFulfilled(bool check_best) const
{
// GP's use the grandPrixFinished() function,
// so they can't be fulfilled here.
@@ -466,15 +484,16 @@ bool ChallengeData::isChallengeFulfilled() const
World *world = World::getWorld();
std::string track_name = Track::getCurrentTrack()->getIdent();
int d = race_manager->getDifficulty();
int d = (check_best) ? RaceManager::DIFFICULTY_BEST :
race_manager->getDifficulty();
AbstractKart* kart = world->getPlayerKart(0);
if (kart->isEliminated() ) return false;
if (track_name != m_track_id ) 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_position[d] > 0 && kart->getPosition() > m_position[d]) return false;
if (kart->isEliminated() ) return false;
if (track_name != m_track_id ) return false;
if (((int)world->getNumKarts() < m_default_num_karts[d]) && !check_best) 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;
// Follow the leader
// -----------------
@@ -498,6 +517,17 @@ bool ChallengeData::isChallengeFulfilled() const
// too slow
if (m_time[d] > 0.0f && kart->getFinishTime() > m_time[d]) return false;
// too slow
if (m_is_ghost_replay)
{
ReplayPlay::get()->addReplayFile(file_manager
->getAsset(FileManager::REPLAY, m_replay_files[d]),
true/*custom_replay*/);
const ReplayPlay::ReplayData& rd = ReplayPlay::get()->getCurrentReplayData();
if (kart->getFinishTime() > rd.m_min_time)
return false;
}
if (m_ai_superpower[d] != RaceManager::SUPERPOWER_NONE &&
race_manager->getAISuperPower() != m_ai_superpower[d])
{

View File

@@ -84,6 +84,13 @@ public:
CM_ANY
};
/** The type of value stored by m_unlock_special_value */
enum SpecialUnlockType
{
SPECIAL_NONE,
SPECIAL_MAX_REQ_IN_LOWER_DIFF
};
private:
@@ -122,6 +129,13 @@ private:
/** Number of trophies required to access this challenge */
int m_num_trophies;
/** Number of completed challenges required to access this challenge
* (esp. useful for the final challenge) */
int m_num_completed_challenges;
/** Variables only used by unlock lists */
SpecialUnlockType m_unlock_special_type;
int m_unlock_special_value;
public:
ChallengeData(const std::string& filename);
@@ -132,7 +146,7 @@ public:
void setRace(RaceManager::Difficulty d) const;
virtual void check() const;
virtual bool isChallengeFulfilled() const;
virtual bool isChallengeFulfilled(bool check_best=false) const;
virtual GPLevel isGPFulfilled() const;
void addUnlockTrackReward(const std::string &track_name);
void addUnlockModeReward(const std::string &internal_mode_name,
@@ -192,6 +206,9 @@ public:
/** Get number of required trophies to start this challenge */
int getNumTrophies() const { return m_num_trophies; }
// ------------------------------------------------------------------------
/** Get number of required completed challenges to start this challenge */
int getNumChallenges() const { return m_num_completed_challenges; }
// ------------------------------------------------------------------------
/** Returns if this challenge is a grand prix. */
bool isGrandPrix() const { return m_mode == CM_GRAND_PRIX; }
// ------------------------------------------------------------------------
@@ -204,6 +221,12 @@ public:
/** Returns if this challenge is an unlock list. */
bool isUnlockList() const { return m_is_unlock_list; }
// ------------------------------------------------------------------------
/** Returns the special unlock list value */
SpecialUnlockType getSpecialType() const { return m_unlock_special_type; }
// ------------------------------------------------------------------------
/** Returns the special unlock list value */
int getSpecialValue() const { return m_unlock_special_value; }
// ------------------------------------------------------------------------
/** Returns the challenge mode of this challenge. */
ChallengeModeType getMode() const { return m_mode; }
// ------------------------------------------------------------------------
@@ -232,10 +255,10 @@ public:
// ------------------------------------------------------------------------
/** Returns the maximum time in which the kart must finish.
*/
float getTime(RaceManager::Difficulty difficulty) const
float getTimeRequirement(RaceManager::Difficulty difficulty) const
{
return m_time[difficulty];
} // getTime
} // getTimeRequirement
// ------------------------------------------------------------------------
/** Return the energy that a kart must at least have at the end of a race.
*/

View File

@@ -73,7 +73,8 @@ void ChallengeStatus::load(const XMLNode* challenges_node)
m_state[3] = CH_SOLVED;
}
} // if has 'solved' attribute
if (!node->get("best_while_slower", &m_max_req_in_lower_diff))
m_max_req_in_lower_diff = false;
} // load
//-----------------------------------------------------------------------------
@@ -106,13 +107,15 @@ void ChallengeStatus::save(UTFWriter& writer)
{
writer << " <" << m_data->getChallengeId();
if (isSolved(RaceManager::DIFFICULTY_BEST))
writer << " solved=\"best\"/>\n";
writer << " solved=\"best\"";
else if (isSolved(RaceManager::DIFFICULTY_HARD))
writer << " solved=\"hard\"/>\n";
writer << " solved=\"hard\"";
else if (isSolved(RaceManager::DIFFICULTY_MEDIUM))
writer << " solved=\"medium\"/>\n";
writer << " solved=\"medium\"";
else if (isSolved(RaceManager::DIFFICULTY_EASY))
writer << " solved=\"easy\"/>\n";
writer << " solved=\"easy\"";
else
writer << " solved=\"none\"/>\n";
writer << " solved=\"none\"";
writer << " best_while_slower=\"" << m_max_req_in_lower_diff << "\"/>\n";
} // save

View File

@@ -60,6 +60,10 @@ private:
m_state[RaceManager::DIFFICULTY_COUNT];
// If the challenge's SuperTux time requirement has been beaten
// in a (s)lower difficulty.
bool m_max_req_in_lower_diff;
/** Pointer to the original challenge data. */
const ChallengeData* m_data;
@@ -71,6 +75,7 @@ public:
m_state[RaceManager::DIFFICULTY_MEDIUM] = CH_INACTIVE;
m_state[RaceManager::DIFFICULTY_HARD] = CH_INACTIVE;
m_state[RaceManager::DIFFICULTY_BEST] = CH_INACTIVE;
m_max_req_in_lower_diff = false;
}
virtual ~ChallengeStatus() {};
void load(const XMLNode* config);
@@ -114,6 +119,21 @@ public:
/** Returns if this challenge is a grand prix */
bool isGrandPrix();
// ------------------------------------------------------------------------
/** Used when a challenge's requirement in the hardest difficulty are
* matched in a lower difficulty. Don't apply to GP */
void setMaxReqInLowerDiff()
{
if (!isGrandPrix() && !isUnlockList())
m_max_req_in_lower_diff = true;
} // setMaxReqInLowerDiff
// ------------------------------------------------------------------------
/** Returns if the hardest difficulty requirements have been met in a lower
* difficulty. */
bool areMaxReqMetInLowerDiff() const
{
return m_max_req_in_lower_diff;
} // areMaxReqMetInLowerDiff
// ------------------------------------------------------------------------
/** Returns a pointer to the actual Challenge data.
*/
const ChallengeData* getData() const { return m_data; }

View File

@@ -223,6 +223,7 @@ void StoryModeStatus::unlockFeatureByList()
continue;
bool newly_solved = unlock_manager->unlockByPoints(m_points,i->second);
newly_solved = newly_solved || unlock_manager->unlockSpecial(i->second, getNumReqMetInLowerDiff());
// Add to list of recently unlocked features
if(newly_solved)
@@ -301,6 +302,14 @@ void StoryModeStatus::setCurrentChallenge(const std::string &challenge_id)
*/
void StoryModeStatus::raceFinished()
{
if(m_current_challenge &&
race_manager->getDifficulty() != RaceManager::DIFFICULTY_BEST &&
m_current_challenge->getData()->isChallengeFulfilled(true /*best*/))
{
ChallengeStatus* c = const_cast<ChallengeStatus*>(m_current_challenge);
c->setMaxReqInLowerDiff();
}
if(m_current_challenge &&
m_current_challenge->isActive(race_manager->getDifficulty()) &&
m_current_challenge->getData()->isChallengeFulfilled() )
@@ -372,3 +381,15 @@ void StoryModeStatus::save(UTFWriter &out)
}
out << " </story-mode>\n";
} // save
int StoryModeStatus::getNumReqMetInLowerDiff() const
{
int counter = 0;
for ( const auto &c : m_challenges_state)
{
counter += (c.second->areMaxReqMetInLowerDiff()) ? 1 : 0;
}
return counter;
} // getNumReqMetInLowerDiff
/* EOF */

View File

@@ -99,6 +99,13 @@ public:
/** Clear the list of recently unlocked challenges */
void clearUnlocked () {m_unlocked_features.clear(); }
// ------------------------------------------------------------------------
/** Returns the number of completed challenges. */
int getNumCompletedChallenges () const { return (m_easy_challenges + m_medium_challenges +
m_hard_challenges + m_best_challenges); }
// ------------------------------------------------------------------------
/** Returns the number of challenges with the superTux time beaten in a lower difficulty. */
int getNumReqMetInLowerDiff () const;
// ------------------------------------------------------------------------
/** Returns the number of points accumulated. */
int getPoints () const { return m_points; }
// ------------------------------------------------------------------------

View File

@@ -297,7 +297,6 @@ void UnlockManager::findWhatWasUnlocked(int points_before, int points_now,
*/
bool UnlockManager::unlockByPoints(int points, ChallengeStatus* unlock_list)
{
//TODO : add support for other conditions (achievements...) for alternative unlock paths
if( unlock_list!=NULL && unlock_list->getData()->getNumTrophies() <= points)
{
unlock_list->setSolved(RaceManager::DIFFICULTY_BEST);
@@ -306,4 +305,24 @@ bool UnlockManager::unlockByPoints(int points, ChallengeStatus* unlock_list)
return false;
} // unlockByPoints
//-----------------------------------------------------------------------------
/** This functions sets as completed the "challenges" requiring some special conditions
* Returns true if the challenge has been completed
*/
bool UnlockManager::unlockSpecial(ChallengeStatus* unlock_list, int max_req_in_lower_diff)
{
if ( unlock_list!=NULL && unlock_list->getData()->getSpecialType() != ChallengeData::SPECIAL_NONE)
{
if (unlock_list->getData()->getSpecialType() == ChallengeData::SPECIAL_MAX_REQ_IN_LOWER_DIFF)
{
if (max_req_in_lower_diff >= unlock_list->getData()->getSpecialValue())
{
unlock_list->setSolved(RaceManager::DIFFICULTY_BEST);
return true;
}
}
}
return false;
} // unlockSpecial
/* EOF */

View File

@@ -69,6 +69,7 @@ public:
std::vector<std::string>& karts,
std::vector<const ChallengeData*>& unlocked);
bool unlockByPoints(int points, ChallengeStatus* unlock_list);
bool unlockSpecial(ChallengeStatus* unlock_list, int max_req_in_lower_diff);
StoryModeStatus *createStoryModeStatus(const XMLNode *node=NULL);

View File

@@ -141,30 +141,61 @@ public:
{
return PlayerManager::getCurrentPlayer()->getAchievementsStatus();
} // getCurrentAchievementsStatus
// ------------------------------------------------------------------------
/** A handy shortcut to increase points for an achievement key of the
* current player.
* \param achievement_id The achievement id.
* \param key The key of the current value to increase.
* \param increase How much to increase the current value.
* \param goal_key Optional: The goal key to compare the current value
* with. If not set, defaults to key.
*/
static void increaseAchievement(unsigned int achievement_id,
const std::string &key,
int increase = 1,
const std::string &goal_key="")
{
Achievement *a = getCurrentAchievementsStatus()
->getAchievement(achievement_id);
if (!a)
{
Log::fatal("PlayerManager", "Achievement '%d' not found.",
achievement_id);
}
a->increase(key, goal_key.empty() ? key : goal_key, increase);
// ------------------------------------------------------------------------
/** A handy shortcut to increase points for an achievement data of the
* current player.
* \param achievement_data_id The achievement data id
* \param increase How much to increase the current value.
*/
static void increaseAchievement(unsigned int achievement_data_id,
int increase = 1)
{
getCurrentAchievementsStatus()
->increaseDataVar(achievement_data_id, increase);
} // increaseAchievement
// ------------------------------------------------------------------------
/** Reset an achievement data for current player.
* \param achievement_data_id The achievement data id
*/
static void resetAchievementData(unsigned int achievement_data_id)
{
getCurrentAchievementsStatus()
->resetDataVar(achievement_data_id);
} // resetAchievementData
// ------------------------------------------------------------------------
/** Reset achievements which have to be done in one race
* \param restart - if the race has been restarted
*/
static void onRaceEnd(bool restart)
{
getCurrentAchievementsStatus()->onRaceEnd(restart);
} // onRaceEnd
// ----------------------------------------------------------------------------
/** Transmit an incrementation request of one of the track event counters
* \param track_ident - the internal name of the track
* \param event - the type of counter to increment */
static void trackEvent(std::string track_ident, AchievementsStatus::TrackData event)
{
getCurrentAchievementsStatus()->trackEvent(track_ident, event);
} // trackEvent
// ----------------------------------------------------------------------------
static void resetKartHits(int num_karts)
{
getCurrentAchievementsStatus()->resetKartHits(num_karts);
} // resetKartHits
// ----------------------------------------------------------------------------
static void addKartHit(int kart_id)
{
getCurrentAchievementsStatus()->addKartHit(kart_id);
} // addKartHit
// ------------------------------------------------------------------------
}; // PlayerManager
#endif

View File

@@ -228,6 +228,8 @@ public:
* fulfilled). */
void grandPrixFinished() { m_story_mode_status->grandPrixFinished(); }
// ------------------------------------------------------------------------
unsigned int getNumCompletedChallenges() const { return m_story_mode_status->getNumCompletedChallenges(); }
// ------------------------------------------------------------------------
unsigned int getPoints() const { return m_story_mode_status->getPoints(); }
// ------------------------------------------------------------------------
unsigned int getPointsBefore() const { return m_story_mode_status->getPointsBefore(); }

View File

@@ -35,6 +35,14 @@ private:
/** True if black border will be drawn when rendering. */
bool m_black_border;
/** True if a custom colored border will be drawn when rendering.
* If both a black border and a colored border are set to be used,
* the colored border will take priority. */
bool m_colored_border;
/* True if the border to draw should be thin */
bool m_thin_border;
/** If true, characters will have right alignment when rendering, for RTL
* language. */
bool m_rtl;
@@ -48,20 +56,28 @@ private:
/** Save the color of shadow when rendering. */
video::SColor m_shadow_color;
/** Used when m_colored_border is true */
video::SColor m_border_color;
public:
LEAK_CHECK()
// ------------------------------------------------------------------------
/** Constructor. It will initialize all members with default values if no
* parameter is given. */
FontSettings(bool black_border = false, bool rtl = false,
float scale = 1.0f, bool shadow = false,
const video::SColor& color = video::SColor(0, 0, 0, 0))
FontSettings(bool black_border = false, bool colored_border = false,
bool thin_border = false,
bool rtl = false, float scale = 1.0f, bool shadow = false,
const video::SColor& shadow_color = video::SColor(0, 0, 0, 0),
const video::SColor& border_color = video::SColor(0, 0, 0, 0))
{
m_black_border = black_border;
m_colored_border = colored_border;
m_thin_border = thin_border;
m_rtl = rtl;
m_scale = scale;
m_shadow = shadow;
m_shadow_color = color;
m_shadow_color = shadow_color;
m_border_color = border_color;
}
// ------------------------------------------------------------------------
/** Set the scaling.
@@ -89,9 +105,29 @@ public:
* \param border If it's enabled. */
void setBlackBorder(bool border) { m_black_border = border; }
// ------------------------------------------------------------------------
/** Set whether a custom colored border is enabled.
* \param border If it's enabled. */
void setColoredBorder(bool border ) { m_colored_border = border; }
// ------------------------------------------------------------------------
/** Set whether the text outline should be thin or not. */
void setThinBorder(bool thin) { m_thin_border = thin; }
// ------------------------------------------------------------------------
/** Set the color of border (used when a non-black border is requested).
* \param col The color of border to be set. */
void setBorderColor(const video::SColor &col) { m_border_color = col; }
// ------------------------------------------------------------------------
/** Return the color of the border.. */
const video::SColor& getBorderColor() const { return m_border_color; }
// ------------------------------------------------------------------------
/** Return if black border is enabled. */
bool useBlackBorder() const { return m_black_border; }
// ------------------------------------------------------------------------
/** Return if black border is enabled. */
bool useColoredBorder() const { return m_colored_border; }
// ------------------------------------------------------------------------
/** Return if the border should be thin or not. */
bool useThinBorder() const { return m_thin_border; }
// ------------------------------------------------------------------------
/** Set right text alignment for RTL language.
* \param rtl If it's enabled. */
void setRTL(bool rtl) { m_rtl = rtl; }

View File

@@ -516,6 +516,8 @@ void FontWithFace::render(const core::stringw& text,
const bool black_border = font_settings ?
font_settings->useBlackBorder() : false;
const bool colored_border = font_settings ?
font_settings->useColoredBorder() : false;
const bool rtl = font_settings ? font_settings->isRTL() : false;
const float scale = font_settings ? font_settings->getScale() : 1.0f;
const float shadow = font_settings ? font_settings->useShadow() : false;
@@ -639,11 +641,14 @@ void FontWithFace::render(const core::stringw& text,
const int sprite_amount = sprites.size();
if ((black_border || isBold()) && char_collector == NULL)
if ((black_border || colored_border || isBold()) && char_collector == NULL)
{
// Draw black border first, to make it behind the real character
// which make script language display better
video::SColor custom_color = font_settings->getBorderColor();
video::SColor black(color.getAlpha(),0,0,0);
video::SColor border_color = (colored_border) ? custom_color : black;
for (int n = 0; n < indice_amount; n++)
{
const int sprite_id = indices[n];
@@ -671,14 +676,19 @@ void FontWithFace::render(const core::stringw& text,
m_fallback_font->m_spritebank->getTexture(tex_id) :
m_spritebank->getTexture(tex_id));
for (int x_delta = -2; x_delta <= 2; x_delta++)
const bool thin_border = font_settings ?
font_settings->useThinBorder() : false;
int thickness = (thin_border) ? 1 : 2;
for (int x_delta = -thickness; x_delta <= thickness; x_delta++)
{
for (int y_delta = -2; y_delta <= 2; y_delta++)
for (int y_delta = -thickness; y_delta <= thickness; y_delta++)
{
if (x_delta == 0 || y_delta == 0) continue;
draw2DImage(texture, dest + core::position2d<float>
(float(x_delta), float(y_delta)), source, clip,
black, true);
border_color, true);
}
}
}

View File

@@ -882,7 +882,10 @@ void IrrDriver::changeResolution(const int w, const int h,
// is actually called from the gui, i.e. the event loop, i.e. while the
// old device is active - so we can't delete this device (which we must
// do in applyResolutionSettings
m_resolution_changing = RES_CHANGE_YES;
if (w < 1024 || h < 720)
m_resolution_changing = RES_CHANGE_YES_WARN;
else
m_resolution_changing = RES_CHANGE_YES;
} // changeResolution
//-----------------------------------------------------------------------------
@@ -1859,7 +1862,9 @@ void IrrDriver::update(float dt)
{
applyResolutionSettings();
if(m_resolution_changing==RES_CHANGE_YES)
new ConfirmResolutionDialog();
new ConfirmResolutionDialog(false);
else if(m_resolution_changing==RES_CHANGE_YES_WARN)
new ConfirmResolutionDialog(true);
m_resolution_changing = RES_CHANGE_NONE;
}

View File

@@ -114,10 +114,12 @@ private:
/** Flag to indicate if a resolution change is pending (which will be
* acted upon in the next update). None means no change, yes means
* change to new resolution and trigger confirmation dialog.
* Yes_warn means that the new resolution is unsupported and that
* the confirmation dialog needs an additional warning message.
* Same indicates a change of the resolution (back to the original
* one), but no confirmation dialog. */
enum {RES_CHANGE_NONE, RES_CHANGE_YES,
RES_CHANGE_SAME} m_resolution_changing;
RES_CHANGE_SAME, RES_CHANGE_YES_WARN} m_resolution_changing;
public:

View File

@@ -363,7 +363,7 @@ SP::SPMesh* SlipStream::createMesh(Material* material, bool bonus_mesh)
//----------------------------------------------------------------------------- */
void SlipStream::updateSlipstreamingTextures(float f, const AbstractKart *kart)
{
if (!kart || !m_node || !m_node_fast)
if (!kart || kart->isEliminated() || !m_node || !m_node_fast)
{
if (m_node)
{

View File

@@ -56,11 +56,27 @@ void ScalableFont::setShadow(const irr::video::SColor &col)
void ScalableFont::disableShadow()
{
m_font_settings->setShadow(false);
} // setShadow
} // disableShadow
// ----------------------------------------------------------------------------
void ScalableFont::setBlackBorder(bool enabled)
{
m_font_settings->setBlackBorder(enabled);
} // setBlackBorder
// ----------------------------------------------------------------------------
void ScalableFont::setColoredBorder(const irr::video::SColor &col)
{
m_font_settings->setColoredBorder(true);
m_font_settings->setBorderColor(col);
} // setColoredBorder
// ----------------------------------------------------------------------------
void ScalableFont::setThinBorder(bool thin)
{
m_font_settings->setThinBorder(thin);
} // setThinBorder
// ----------------------------------------------------------------------------
void ScalableFont::disableColoredBorder()
{
m_font_settings->setColoredBorder(false);
} // setShadow
// ----------------------------------------------------------------------------

View File

@@ -59,6 +59,12 @@ public:
// ------------------------------------------------------------------------
void setBlackBorder(bool enabled);
// ------------------------------------------------------------------------
void setColoredBorder(const irr::video::SColor &col);
// ------------------------------------------------------------------------
void setThinBorder(bool thin);
// ------------------------------------------------------------------------
void disableColoredBorder();
// ------------------------------------------------------------------------
void updateRTL();
// ------------------------------------------------------------------------
void draw(const core::stringw& text, const core::rect<s32>& position,

View File

@@ -1192,7 +1192,7 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
return;
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
SkinConfig::m_render_params["squareFocusHalo::neutral"]);
SkinConfig::m_render_params["squareFocusHalo1::neutral"]);
nPlayersOnThisItem++;
}
} // end if mark_focused
@@ -1209,11 +1209,24 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
short green_previous = parentRibbonWidget->m_skin_g;
short blue_previous = parentRibbonWidget->m_skin_b;
SColorf color_rgb = getPlayerColor(i);
if (i>=4)
{
SColorf color_rgb = getPlayerColor(i);
parentRibbonWidget->m_skin_r = short(color_rgb.r * 255.0f);
parentRibbonWidget->m_skin_g = short(color_rgb.g * 255.0f);
parentRibbonWidget->m_skin_b = short(color_rgb.b * 255.0f);
parentRibbonWidget->m_skin_r = short(color_rgb.r * 255.0f);
parentRibbonWidget->m_skin_g = short(color_rgb.g * 255.0f);
parentRibbonWidget->m_skin_b = short(color_rgb.b * 255.0f);
}
std::string square_focus;
// 1 = player n°2
// TODO : current skins support 5 custom colors before using the coloring
// but dynamic detection of the number of colors supported would be better
if (i>=5)
square_focus = "squareFocusHaloBW::neutral";
else
square_focus = "squareFocusHalo" + StringUtils::toString(i+1) + "::neutral";
if (nPlayersOnThisItem > 0)
{
@@ -1225,16 +1238,19 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
rect2.LowerRightCorner.Y += enlarge;
drawBoxFromStretchableTexture(parentRibbonWidget, rect2,
SkinConfig::m_render_params["squareFocusHaloBW::neutral"]);
SkinConfig::m_render_params[square_focus.c_str()]);
}
else
{
drawBoxFromStretchableTexture(parentRibbonWidget, rect,
SkinConfig::m_render_params["squareFocusHaloBW::neutral"]);
SkinConfig::m_render_params[square_focus.c_str()]);
}
if (i>=5)
{
parentRibbonWidget->m_skin_r = red_previous;
parentRibbonWidget->m_skin_g = green_previous;
parentRibbonWidget->m_skin_b = blue_previous;
}
parentRibbonWidget->m_skin_r = red_previous;
parentRibbonWidget->m_skin_g = green_previous;
parentRibbonWidget->m_skin_b = blue_previous;
nPlayersOnThisItem++;
}
}
@@ -1295,14 +1311,18 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget,
BoxRenderParams* params;
SpinnerWidget* q = dynamic_cast<SpinnerWidget*>(widget);
std::string texture = "squareFocusHalo::neutral";
std::string texture = "squareFocusHalo1::neutral";
SColorf color_rgb = { 1,1,1,1 };
if(q->getUseBackgroundColor())
{
int player_id=q->getSpinnerWidgetPlayerID();
std::string spinner = "spinner::deactivated";
params = &SkinConfig::m_render_params[
"spinner::deactivated"];
if (player_id <= 4)
spinner = "spinner" + StringUtils::toString(player_id+1) + "::neutral";
params = &SkinConfig::m_render_params[spinner];
color_rgb = getPlayerColor(player_id);
@@ -1320,14 +1340,22 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget,
{
params=&SkinConfig::m_render_params["spinner::neutral"];
}
widget->m_skin_r = short(color_rgb.r * 255.0f);
widget->m_skin_g = short(color_rgb.g * 255.0f);
widget->m_skin_b = short(color_rgb.b * 255.0f);
for (unsigned i = 1; i < MAX_PLAYER_COUNT + 1; i++)
{
if (widget->isFocusedForPlayer(i - 1))
{
if (i<=5)
{
texture = "squareFocusHalo" + StringUtils::toString(i) + "::neutral";
}
else
{
widget->m_skin_r = short(color_rgb.r * 255.0f);
widget->m_skin_g = short(color_rgb.g * 255.0f);
widget->m_skin_b = short(color_rgb.b * 255.0f);
}
core::recti rect2 = rect;
rect2.UpperLeftCorner.X += 2;
rect2.UpperLeftCorner.Y -= 3;
@@ -1522,7 +1550,7 @@ void Skin::drawIconButton(const core::recti &rect, Widget* widget,
IconButtonWidget* icon_widget = (IconButtonWidget*) widget;
if (icon_widget->hasTooltip() > 0)
if (icon_widget->hasTooltip())
{
const core::position2di mouse_position =
irr_driver->getDevice()->getCursorControl()->getPosition();

View File

@@ -11,8 +11,10 @@
#include "IGUIFont.h"
#include "IGUISpriteBank.h"
#include "IGUIScrollBar.h"
#include "utils/string_utils.hpp"
#include "utils/time.hpp"
#include <cstdlib>
namespace irr
{
@@ -384,7 +386,7 @@ bool CGUISTKListBox::OnEvent(const SEvent& event)
}
if (Selecting &&
fabs(event.MouseInput.Y - MousePosY) > ItemHeight/3)
std::abs(event.MouseInput.Y - MousePosY) > ItemHeight/3)
{
Moving = true;
Selecting = false;
@@ -531,46 +533,65 @@ void CGUISTKListBox::draw()
iconPos.Y += textRect.getHeight() / 2;
iconPos.X += ItemsIconWidth/2;
EGUI_LISTBOX_COLOR icon_color = EGUI_LBC_ICON;
bool highlight=false;
if ( i==Selected && hl )
{
IconBank->draw2DSprite(
(u32)Items[i].m_contents[x].m_icon,
iconPos, &clientClip,
hasItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) ?
getItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_ICON_HIGHLIGHT),
selectTime, (u32)StkTime::getTimeSinceEpoch(), false, true);
}
else
{
IconBank->draw2DSprite(
(u32)Items[i].m_contents[x].m_icon,
iconPos,
&clientClip,
hasItemOverrideColor(i, EGUI_LBC_ICON) ? getItemOverrideColor(i, EGUI_LBC_ICON) : getItemDefaultColor(EGUI_LBC_ICON),
0 , (i==Selected) ? (u32)StkTime::getTimeSinceEpoch() : 0, false, true);
icon_color = EGUI_LBC_ICON_HIGHLIGHT;
highlight=true;
}
IconBank->draw2DSprite(
(u32)Items[i].m_contents[x].m_icon,
iconPos, &clientClip,
hasItemOverrideColor(i, icon_color) ? getItemOverrideColor(i, icon_color) : getItemDefaultColor(icon_color),
(highlight) ? selectTime : 0, (i==Selected) ? (u32)StkTime::getTimeSinceEpoch() : 0, false, true);
textRect.UpperLeftCorner.X += ItemsIconWidth;
}
textRect.UpperLeftCorner.X += 3;
EGUI_LISTBOX_COLOR font_color = EGUI_LBC_TEXT;
if ( i==Selected && hl )
font_color = EGUI_LBC_TEXT_HIGHLIGHT;
if (!Items[i].m_contents[x].m_broken_text)
{
int text_width = (textRect.LowerRightCorner.X - textRect.UpperLeftCorner.X);
std::wstring cell_text = Items[i].m_contents[x].m_text.c_str();
std::vector<std::wstring> cell_text_lines;
if (Items[i].m_word_wrap)
StringUtils::breakText(cell_text, cell_text_lines, text_width, Font);
else
cell_text_lines.push_back(cell_text);
for (unsigned int j=0; j<cell_text_lines.size(); j++)
{
irr::core::stringw text_line = cell_text_lines[j].c_str();
Items[i].m_contents[x].m_text_lines.push_back(text_line);
}
Items[i].m_contents[x].m_broken_text = true;
}
core::rect<s32> lineRect = textRect;
int line_height = Font->getDimension(L"A").Height;
int supp_lines = Items[i].m_contents[x].m_text_lines.size() - 1;
lineRect.UpperLeftCorner.Y -= (line_height*supp_lines)/2;
lineRect.LowerRightCorner.Y -= (line_height*supp_lines)/2;
for (unsigned int j=0; j<Items[i].m_contents[x].m_text_lines.size(); j++)
{
Font->draw(
Items[i].m_contents[x].m_text.c_str(),
textRect,
hasItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) ?
getItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_TEXT_HIGHLIGHT),
Items[i].m_contents[x].m_center, true, &clientClip);
}
else
{
Font->draw(
Items[i].m_contents[x].m_text.c_str(),
textRect,
hasItemOverrideColor(i, EGUI_LBC_TEXT) ? getItemOverrideColor(i, EGUI_LBC_TEXT) : getItemDefaultColor(EGUI_LBC_TEXT),
Items[i].m_contents[x].m_text_lines[j],
lineRect,
hasItemOverrideColor(i, font_color) ? getItemOverrideColor(i, font_color) : getItemDefaultColor(font_color),
Items[i].m_contents[x].m_center, true, &clientClip);
lineRect.UpperLeftCorner.Y += line_height;
lineRect.LowerRightCorner.Y += line_height;
}
//Position back to inital pos
if (IconBank && (Items[i].m_contents[x].m_icon > -1))
textRect.UpperLeftCorner.X -= ItemsIconWidth;
@@ -673,6 +694,8 @@ void CGUISTKListBox::setCell(u32 row_num, u32 col_num, const wchar_t* text, s32
Items[row_num].m_contents[col_num].m_text = text;
Items[row_num].m_contents[col_num].m_icon = icon;
Items[row_num].m_contents[col_num].m_broken_text = false;
Items[row_num].m_contents[col_num].m_text_lines.clear();
recalculateItemHeight();
recalculateIconWidth();

View File

@@ -12,6 +12,7 @@
#include "IGUIElement.h"
#include "irrArray.h"
#include <string>
#include <vector>
namespace irr
{
@@ -30,6 +31,8 @@ namespace irr
struct ListCell
{
irr::core::stringw m_text;
bool m_broken_text;
std::vector<irr::core::stringw> m_text_lines;
int m_proportion;
s32 m_icon;
bool m_center;
@@ -40,6 +43,8 @@ namespace irr
m_proportion = proportion;
m_icon = icon;
m_center = center;
m_broken_text = false;
m_text_lines.clear();
}
};
@@ -49,6 +54,8 @@ namespace irr
std::string m_internal_name;
int m_current_id;
bool m_word_wrap = false;
// A multicolor extension
struct ListItemOverrideColor
{

View File

@@ -239,6 +239,7 @@ void ListWidget::addItem( const std::string& internal_name,
ListItem newItem;
newItem.m_internal_name = internal_name;
newItem.m_contents.push_back(cell);
newItem.m_word_wrap = (m_properties[PROP_WORD_WRAP] == "true");
CGUISTKListBox* list = getIrrlichtElement<CGUISTKListBox>();
assert(list != NULL);
@@ -266,6 +267,7 @@ void ListWidget::addItem(const std::string& internal_name,
{
newItem.m_contents.push_back(contents[i]);
}
newItem.m_word_wrap = (m_properties[PROP_WORD_WRAP] == "true");
CGUISTKListBox* list = getIrrlichtElement<CGUISTKListBox>();
assert(list != NULL);
@@ -431,8 +433,8 @@ void ListWidget::markItemRed(const int id, bool red)
}
else
{
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, video::SColor(255,0,0,0) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, video::SColor(255,255,255,255) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, GUIEngine::getSkin()->getColor("text::neutral"));
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, GUIEngine::getSkin()->getColor("text::focused"));
}
}
@@ -447,18 +449,39 @@ void ListWidget::markItemBlue(const int id, bool blue)
if (blue)
{
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, video::SColor(255,0,0,255) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, video::SColor(255,0,0,255) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, GUIEngine::getSkin()->getColor("list_blue::neutral"));
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, GUIEngine::getSkin()->getColor("list_blue::focused"));
}
else
{
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, video::SColor(255,0,0,0) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, video::SColor(255,255,255,255) );
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, GUIEngine::getSkin()->getColor("text::neutral"));
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, GUIEngine::getSkin()->getColor("text::focused"));
}
}
// -----------------------------------------------------------------------------
void ListWidget::emphasisItem(const int id, bool enable)
{
// May only be called AFTER this widget has been add()ed
assert(m_element != NULL);
CGUISTKListBox* irritem = getIrrlichtElement<CGUISTKListBox>();
if (enable)
{
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, GUIEngine::getSkin()->getColor("emphasis_text::neutral"));
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, GUIEngine::getSkin()->getColor("emphasis_text::focused"));
}
else
{
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT, GUIEngine::getSkin()->getColor("text::neutral"));
irritem->setItemOverrideColor( id, EGUI_LBC_TEXT_HIGHLIGHT, GUIEngine::getSkin()->getColor("text::focused"));
}
} // emphasisItem
// -----------------------------------------------------------------------------
EventPropagation ListWidget::transmitEvent(Widget* w,
const std::string& originator,
const int playerID)

View File

@@ -223,6 +223,7 @@ namespace GUIEngine
*/
void markItemRed(const int id, bool red=true);
void markItemBlue(const int id, bool blue=true);
void emphasisItem(const int id, bool enable=true);
/**
* \brief Make an item red to mark an error, for instance
@@ -242,6 +243,13 @@ namespace GUIEngine
markItemBlue( id, blue );
}
void emphasisItem(const std::string &internalName, bool enable=true)
{
const int id = getItemID(internalName);
assert(id != -1);
emphasisItem(id, enable);
}
/** Override callback from Widget */
virtual EventPropagation transmitEvent(Widget* w,
const std::string& originator,

View File

@@ -129,6 +129,7 @@ bool DeviceManager::initialize()
// Some systems report a disk accelerometer as a gamepad, skip that
if (name.find("LIS3LV02DL") != -1) continue;
if (name == "applesmc") continue;
if (name.find("VirtualBox") == 0) continue;
if(m_irrlicht_gamepads[id].HasGenericName)
{

View File

@@ -42,9 +42,9 @@
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "replay/replay_recorder.hpp"
#include "states_screens/dialogs/splitscreen_player_dialog.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "states_screens/online/networking_lobby.hpp"
#include "states_screens/options/options_screen_device.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/debug.hpp"
@@ -82,6 +82,7 @@ InputManager::InputManager() : m_mode(BOOTSTRAP),
m_timer_in_use = false;
m_master_player_only = false;
m_timer = 0;
m_timer_use_count = 0;
}
// -----------------------------------------------------------------------------
@@ -116,7 +117,7 @@ void InputManager::handleStaticAction(int key, int value)
// When no players... a cutscene
if (race_manager->getNumPlayers() == 0 && world != NULL && value > 0 &&
(key == IRR_KEY_SPACE || key == IRR_KEY_RETURN ||
(key == IRR_KEY_SPACE || key == IRR_KEY_RETURN ||
key == IRR_KEY_BUTTON_A))
{
world->onFirePressed(NULL);
@@ -729,10 +730,15 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
{
device = m_device_manager->getGamePadFromIrrID(deviceID);
}
if (device && (action == PA_FIRE || action == PA_MENU_SELECT))
if (device && (action == PA_FIRE || action == PA_MENU_SELECT) &&
!GUIEngine::ModalDialog::isADialogActive())
{
if (!GUIEngine::ModalDialog::isADialogActive())
new SplitscreenPlayerDialog(device);
GUIEngine::Screen* screen = GUIEngine::getCurrentScreen();
NetworkingLobby* lobby = dynamic_cast<NetworkingLobby*>(screen);
if (lobby!=NULL)
{
lobby->openSplitscreenDialog(device);
}
return;
}
}
@@ -818,12 +824,6 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
// ... when in menus
else
{
// reset timer when released
if (abs(value) == 0 && type == Input::IT_STICKBUTTON)
{
m_timer_in_use = false;
m_timer = 0;
}
// When in master-only mode, we can safely assume that players
// are set up, contrarly to early menus where we accept every
@@ -855,7 +855,10 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
if (abs(value) > Input::MAX_VALUE*2/3)
{
m_timer_in_use = true;
m_timer = 0.25;
// After three iterations of the timer, pick up the scrolling pace
m_timer_use_count++;
m_timer = m_timer_use_count > 3 ? 0.05 : 0.25;
}
// player may be NULL in early menus, before player setup has
@@ -882,6 +885,14 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
->processGUIAction(action, deviceID, abs(value), type,
playerID);
}
// reset timer when released
if (abs(value) == 0)
{
m_timer_in_use = false;
m_timer = 0;
m_timer_use_count = 0;
}
}
}
else if (type == Input::IT_KEYBOARD)
@@ -1024,7 +1035,7 @@ EventPropagation InputManager::input(const SEvent& event)
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox())
{
if (key == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
if (key == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
key == IRR_KEY_SHIFT)
{
return EVENT_LET;
@@ -1057,7 +1068,7 @@ EventPropagation InputManager::input(const SEvent& event)
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox())
{
if (key == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
if (key == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
key == IRR_KEY_SHIFT)
{
return EVENT_LET;

View File

@@ -46,9 +46,6 @@ public:
BOOTSTRAP
};
// to put a delay before a new gamepad axis move is considered in menu
bool m_timer_in_use;
float m_timer;
private:
@@ -68,6 +65,11 @@ private:
*/
int m_mouse_val_x, m_mouse_val_y;
// to put a delay before a new gamepad axis move is considered in menu
bool m_timer_in_use;
int m_timer_use_count;
float m_timer;
void dispatchInput(Input::InputType, int deviceID, int btnID,
Input::AxisDirection direction, int value,
bool shift_mask = false);

View File

@@ -1324,25 +1324,17 @@ void FileManager::listFiles(std::set<std::string>& result,
{
result.clear();
if(!isDirectory(dir))
if (!isDirectory(dir))
return;
io::path previous_cwd = m_file_system->getWorkingDirectory();
irr::io::IFileList* files = m_file_system->createFileList(dir.c_str());
if(!m_file_system->changeWorkingDirectoryTo( dir.c_str() ))
for (int n = 0; n < (int)files->getFileCount(); n++)
{
Log::error("FileManager", "listFiles : Could not change CWD!\n");
return;
}
irr::io::IFileList* files = m_file_system->createFileList();
for(int n=0; n<(int)files->getFileCount(); n++)
{
result.insert(make_full_path ? dir+"/"+ files->getFileName(n).c_str()
: files->getFileName(n).c_str() );
result.insert(make_full_path ? dir + "/" + files->getFileName(n).c_str()
: files->getFileName(n).c_str());
}
m_file_system->changeWorkingDirectoryTo( previous_cwd );
files->drop();
} // listFiles

View File

@@ -19,7 +19,7 @@
#include "items/attachment.hpp"
#include <algorithm>
#include "achievements/achievement_info.hpp"
#include "achievements/achievements_status.hpp"
#include "audio/sfx_base.hpp"
#include "config/player_manager.hpp"
#include "config/stk_config.hpp"
@@ -329,8 +329,8 @@ void Attachment::hitBanana(ItemState *item_state)
{
if (m_kart->getController()->canGetAchievements())
{
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_BANANA,
"banana", 1);
PlayerManager::increaseAchievement(AchievementsStatus::BANANA, 1);
PlayerManager::increaseAchievement(AchievementsStatus::BANANA_1RACE, 1);
}
//Bubble gum shield effect:
if(m_type == ATTACH_BUBBLEGUM_SHIELD ||

View File

@@ -26,6 +26,8 @@
#include <IMeshManipulator.h>
#include <IMeshSceneNode.h>
#include "achievements/achievements_status.hpp"
#include "config/player_manager.hpp"
#include "graphics/explosion.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/material.hpp"
@@ -34,6 +36,7 @@
#include "io/xml_node.hpp"
#include "items/projectile_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/explosion_animation.hpp"
#include "modes/linear_world.hpp"
#include "network/compress_network_body.hpp"
@@ -562,6 +565,14 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object,
{
world->kartHit(kart->getWorldKartId(),
m_owner->getWorldKartId());
if (m_owner->getController()->canGetAchievements())
{
if (m_owner->getWorldKartId() != kart->getWorldKartId())
PlayerManager::addKartHit(kart->getWorldKartId());
PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS, 1);
PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS_1RACE, 1);
}
}
}
}

View File

@@ -224,6 +224,10 @@ public:
// ------------------------------------------------------------------------
/** Returns the type of flyable. */
PowerupManager::PowerupType getType() const {return m_type;}
// ------------------------------------------------------------------------
/** Returns the owner's kart */
AbstractKart *getOwner() const { return m_owner;}
// ------------------------------------------------------------------------
/** Sets wether Flyable should update TerrainInfo as part of its update
* call, or if the inheriting object will update TerrainInfo itself

View File

@@ -18,11 +18,9 @@
#include "items/powerup.hpp"
#include "achievements/achievement_info.hpp"
#include "config/player_manager.hpp"
#include "audio/sfx_base.hpp"
#include "audio/sfx_manager.hpp"
#include "config/player_manager.hpp"
#include "config/stk_config.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp"
@@ -254,7 +252,8 @@ void Powerup::use()
if (m_type != PowerupManager::POWERUP_NOTHING &&
m_kart->getController()->canGetAchievements() )
{
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_POWERUP_LOVER, "poweruplover");
PlayerManager::increaseAchievement(AchievementsStatus::POWERUP_USED, 1);
PlayerManager::increaseAchievement(AchievementsStatus::POWERUP_USED_1RACE, 1);
}
// Play custom kart sound when collectible is used //TODO: what about the bubble gum?

View File

@@ -201,7 +201,8 @@ bool ProjectileManager::projectileIsClose(const AbstractKart * const kart,
* \param type The type of projectile checked
*/
int ProjectileManager::getNearbyProjectileCount(const AbstractKart * const kart,
float radius, PowerupManager::PowerupType type)
float radius, PowerupManager::PowerupType type,
bool exclude_owned)
{
float r2 = radius * radius;
int projectile_count = 0;
@@ -212,6 +213,9 @@ int ProjectileManager::getNearbyProjectileCount(const AbstractKart * const kart,
continue;
if (i->second->getType() == type)
{
if (exclude_owned && (i->second->getOwner() == kart))
continue;
float dist2 = i->second->getXYZ().distance2(kart->getXYZ());
if (dist2 < r2)
{
@@ -310,3 +314,4 @@ std::shared_ptr<Rewinder>
return nullptr;
}
} // addProjectileFromNetworkState

View File

@@ -72,7 +72,8 @@ public:
float radius);
int getNearbyProjectileCount(const AbstractKart * const kart,
float radius, PowerupManager::PowerupType type);
float radius, PowerupManager::PowerupType type,
bool exclude_owned=false);
// ------------------------------------------------------------------------
/** Adds a special hit effect to be shown.
* \param hit_effect The hit effect to be added. */
@@ -100,3 +101,4 @@ extern ProjectileManager *projectile_manager;
#endif
/* EOF */

View File

@@ -22,6 +22,7 @@
#include "utils/no_copy.hpp"
#include "utils/vec3.hpp"
#include <cinttypes>
#include <memory>
namespace SP

View File

@@ -25,7 +25,8 @@
// TODO: move some constants to KartProperties, use all constants from KartProperties
#include "items/swatter.hpp"
#include "achievements/achievement_info.hpp"
#include "achievements/achievements_status.hpp"
#include "audio/sfx_base.hpp"
#include "audio/sfx_manager.hpp"
#include "config/player_manager.hpp"
@@ -344,7 +345,9 @@ void Swatter::squashThingsAround()
AbstractKart* closest_kart = m_closest_kart;
float duration = kp->getSwatterSquashDuration();
float slowdown = kp->getSwatterSquashSlowdown();
closest_kart->setSquash(duration, slowdown);
// The squash attempt may fail because of invulnerability, shield, etc.
// Making a bomb explode counts as a success
bool success = closest_kart->setSquash(duration, slowdown);
// Locally add a event to replay the squash during rewind
if (NetworkConfig::get()->isNetworking() &&
@@ -359,13 +362,23 @@ void Swatter::squashThingsAround()
}));
}
// Handle achievement if the swatter is used by the current player
if (m_kart->getController()->canGetAchievements())
if (success)
{
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_MOSQUITO,
"swatter", 1);
World::getWorld()->kartHit(m_closest_kart->getWorldKartId(),
m_kart->getWorldKartId());
// Handle achievement if the swatter is used by the current player
if (m_kart->getController()->canGetAchievements())
{
PlayerManager::addKartHit(m_closest_kart->getWorldKartId());
PlayerManager::increaseAchievement(AchievementsStatus::SWATTER_HIT, 1);
PlayerManager::increaseAchievement(AchievementsStatus::SWATTER_HIT_1RACE, 1);
PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS, 1);
PlayerManager::increaseAchievement(AchievementsStatus::ALL_HITS_1RACE, 1);
}
}
//FIXME : setSquash also do a bomb check
if (m_closest_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB)
{ // make bomb explode
m_closest_kart->getAttachment()->update(10000);
@@ -375,12 +388,6 @@ void Swatter::squashThingsAround()
projectile_manager->addHitEffect(he);
ExplosionAnimation::create(m_closest_kart);
} // if kart has bomb attached
if (m_closest_kart->isSquashed())
{
// The kart may not be squashed if it was protected by a bubblegum shield
World::getWorld()->kartHit(m_closest_kart->getWorldKartId(),
m_kart->getWorldKartId());
}
// TODO: squash items
} // squashThingsAround

View File

@@ -72,8 +72,11 @@ AbstractKart::AbstractKart(const std::string& ident,
AbstractKart::~AbstractKart()
{
delete m_kart_model;
if(m_kart_animation)
if (m_kart_animation)
{
m_kart_animation->handleResetRace();
delete m_kart_animation;
}
} // ~AbstractKart
// ----------------------------------------------------------------------------
@@ -82,8 +85,9 @@ void AbstractKart::reset()
// important to delete animations before calling reset, as some animations
// set the kart velocity in their destructor (e.g. cannon) which "reset"
// can then cancel. See #2738
if(m_kart_animation)
if (m_kart_animation)
{
m_kart_animation->handleResetRace();
delete m_kart_animation;
m_kart_animation = NULL;
}

View File

@@ -272,9 +272,10 @@ public:
// ------------------------------------------------------------------------
/** Squashes this kart: it will scale the kart in up direction, and causes
* a slowdown while this kart is squashed.
* Returns true if the squash is successful, false otherwise.
* \param time How long the kart will be squashed.
* \param slowdown Reduction of max speed. */
virtual void setSquash(float time, float slowdown) = 0;
virtual bool setSquash(float time, float slowdown) = 0;
// ------------------------------------------------------------------------
/** Makes the kart unsquashed again. */
virtual void unsetSquash() = 0;

View File

@@ -108,6 +108,10 @@ public:
int getEndTicks() const { return m_end_ticks; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const = 0;
// ------------------------------------------------------------------------
/* Remove the timer changes by checkNetworkAnimationCreationSucceed if
* m_kart has been eliminated by network. */
void handleResetRace() { m_timer = 9999; }
}; // AbstractKartAnimation
#endif

View File

@@ -23,8 +23,10 @@
#include "items/attachment.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "modes/follow_the_leader.hpp"
#include "modes/world.hpp"
#include "network/network_config.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
/** A static create function that does only create an explosion if
@@ -51,6 +53,14 @@ ExplosionAnimation *ExplosionAnimation::create(AbstractKart *kart,
return NULL;
}
if (race_manager->isFollowMode())
{
FollowTheLeaderRace *ftl_world =
dynamic_cast<FollowTheLeaderRace*>(World::getWorld());
if(ftl_world->isLeader(kart->getWorldKartId()))
ftl_world->leaderHit();
}
return new ExplosionAnimation(kart, pos, direct_hit);
} // create

View File

@@ -1309,6 +1309,11 @@ void Kart::eliminate()
m_attachment->clear();
}
if (m_slipstream)
{
m_slipstream->reset();
}
m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
m_kart_gfx->setGFXInvisible();
if (m_engine_sound)
@@ -1346,8 +1351,8 @@ void Kart::update(int ticks)
m_powerup->update(ticks);
// Reset any instand speed increase in the bullet kart
m_vehicle->setMinSpeed(0);
// Reset any instant speed increase in the bullet kart
m_vehicle->resetMaxSpeed();
if (m_bubblegum_ticks > 0)
{
@@ -1490,24 +1495,47 @@ void Kart::update(int ticks)
m_attachment->update(ticks);
PROFILER_PUSH_CPU_MARKER("Kart::updatePhysics", 0x60, 0x34, 0x7F);
updatePhysics(ticks);
PROFILER_POP_CPU_MARKER();
if(!m_controls.getFire()) m_fire_clicked = 0;
if(m_controls.getFire() && !m_fire_clicked && !m_kart_animation)
// Make sure that the ray doesn't hit the kart. This is done by
// resetting the collision filter group, so that this collision
// object is ignored during raycasting.
short int old_group = 0;
if (m_body->getBroadphaseHandle())
{
if (m_powerup->getType() != PowerupManager::POWERUP_NOTHING)
{
setLastUsedPowerup(m_powerup->getType());
}
// use() needs to be called even if there currently is no collecteable
// since use() can test if something needs to be switched on/off.
m_powerup->use() ;
World::getWorld()->onFirePressed(getController());
m_fire_clicked = 1;
old_group = m_body->getBroadphaseHandle()->m_collisionFilterGroup;
m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0;
}
// After the physics step was done, the position of the wheels (as stored
// in wheelInfo) is actually outdated, since the chassis was moved
// according to the force acting from the wheels. So the center of the
// chassis is not at the center of the wheels anymore, it is somewhat
// moved forward (depending on speed and fps). In very extreme cases
// (see bug 2246) the center of the chassis can actually be ahead of the
// front wheels. So if we do a raycast to detect the terrain from the
// current chassis, that raycast might be ahead of the wheels - which
// results in incorrect rescues (the wheels are still on the ground,
// but the raycast happens ahead of the front wheels and are over
// a rescue texture).
// To avoid this problem, we do the raycast for terrain detection from
// the center of the 4 wheel positions (in world coordinates).
Vec3 from(0.0f, 0.0f, 0.0f);
for (unsigned int i = 0; i < 4; i++)
from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS;
// Add a certain epsilon (0.3) to the height of the kart. This avoids
// problems of the ray being cast from under the track (which happened
// e.g. on tux tollway when jumping down from the ramp, when the chassis
// partly tunnels through the track). While tunneling should not be
// happening (since Z velocity is clamped), the epsilon is left in place
// just to be on the safe side (it will not hit the chassis itself).
from = from/4 + (getTrans().getBasis() * Vec3(0.0f, 0.3f, 0.0f));
m_terrain_info->update(getTrans().getBasis(), from);
if (m_body->getBroadphaseHandle())
{
m_body->getBroadphaseHandle()->m_collisionFilterGroup = old_group;
}
// Check if a kart is (nearly) upside down and not moving much -->
@@ -1543,15 +1571,26 @@ void Kart::update(int ticks)
}
}
// Make sure that the ray doesn't hit the kart. This is done by
// resetting the collision filter group, so that this collision
// object is ignored during raycasting.
short int old_group = 0;
if(m_body->getBroadphaseHandle())
// Update physics from newly updated material
PROFILER_PUSH_CPU_MARKER("Kart::updatePhysics", 0x60, 0x34, 0x7F);
updatePhysics(ticks);
PROFILER_POP_CPU_MARKER();
if(!m_controls.getFire()) m_fire_clicked = 0;
if(m_controls.getFire() && !m_fire_clicked && !m_kart_animation)
{
old_group = m_body->getBroadphaseHandle()->m_collisionFilterGroup;
m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0;
if (m_powerup->getType() != PowerupManager::POWERUP_NOTHING)
{
setLastUsedPowerup(m_powerup->getType());
}
// use() needs to be called even if there currently is no collecteable
// since use() can test if something needs to be switched on/off.
m_powerup->use() ;
World::getWorld()->onFirePressed(getController());
m_fire_clicked = 1;
}
#undef XX
#ifdef XX
Log::verbose("physicsafter", "%s t %f %d xyz(9-11) %f %f %f %f %f %f "
@@ -1583,38 +1622,6 @@ void Kart::update(int ticks)
m_bubblegum_torque // 47
);
#endif
// After the physics step was done, the position of the wheels (as stored
// in wheelInfo) is actually outdated, since the chassis was moved
// according to the force acting from the wheels. So the center of the
// chassis is not at the center of the wheels anymore, it is somewhat
// moved forward (depending on speed and fps). In very extreme cases
// (see bug 2246) the center of the chassis can actually be ahead of the
// front wheels. So if we do a raycast to detect the terrain from the
// current chassis, that raycast might be ahead of the wheels - which
// results in incorrect rescues (the wheels are still on the ground,
// but the raycast happens ahead of the front wheels and are over
// a rescue texture).
// To avoid this problem, we do the raycast for terrain detection from
// the center of the 4 wheel positions (in world coordinates).
Vec3 from(0, 0, 0);
for (unsigned int i = 0; i < 4; i++)
from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS;
// Add a certain epsilon (0.3) to the height of the kart. This avoids
// problems of the ray being cast from under the track (which happened
// e.g. on tux tollway when jumping down from the ramp, when the chassis
// partly tunnels through the track). While tunneling should not be
// happening (since Z velocity is clamped), the epsilon is left in place
// just to be on the safe side (it will not hit the chassis itself).
from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0));
m_terrain_info->update(getTrans().getBasis(), from);
if(m_body->getBroadphaseHandle())
{
m_body->getBroadphaseHandle()->m_collisionFilterGroup = old_group;
}
PROFILER_PUSH_CPU_MARKER("Kart::Update (material)", 0x60, 0x34, 0x7F);
const Material* material=m_terrain_info->getMaterial();
@@ -1800,24 +1807,25 @@ void Kart::showZipperFire()
//-----------------------------------------------------------------------------
/** Squashes this kart: it will scale the kart in up direction, and causes
* a slowdown while this kart is squashed.
* Returns true if the squash is successful, false otherwise.
* \param time How long the kart will be squashed. A value of 0 will reset
* the kart to be unsquashed.
* \param slowdown Reduction of max speed.
*/
void Kart::setSquash(float time, float slowdown)
bool Kart::setSquash(float time, float slowdown)
{
if (isInvulnerable()) return;
if (isInvulnerable()) return false;
if (isShielded())
{
decreaseShieldTime();
return;
return false;
}
if(m_attachment->getType()==Attachment::ATTACH_BOMB && time>0)
{
ExplosionAnimation::create(this);
return;
return true;
}
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_SQUASH, slowdown,
@@ -1847,6 +1855,7 @@ void Kart::setSquash(float time, float slowdown)
m_squash_time = time;
}
#endif
return true;
} // setSquash
void Kart::unsetSquash()
@@ -3180,7 +3189,9 @@ void Kart::updateGraphics(float dt)
#ifndef SERVER_ONLY
// draw skidmarks if relevant (we force pink skidmarks on when hitting
// a bubblegum)
if (m_kart_properties->getSkidEnabled() && m_skidmarks)
if (World::getWorld()->getPhase() !=
WorldStatus::IN_GAME_MENU_PHASE &&
m_kart_properties->getSkidEnabled() && m_skidmarks)
{
m_skidmarks->update(dt,
m_bubblegum_ticks > 0,

View File

@@ -315,7 +315,7 @@ public:
virtual void reset () OVERRIDE;
virtual void handleZipper (const Material *m=NULL,
bool play_sound=false) OVERRIDE;
virtual void setSquash (float time, float slowdown) OVERRIDE;
virtual bool setSquash (float time, float slowdown) OVERRIDE;
virtual void unsetSquash () OVERRIDE;
virtual void crashed (AbstractKart *k, bool update_attachments) OVERRIDE;

View File

@@ -263,8 +263,10 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
setTrans(m_transfrom_from_network);
}
m_vehicle->setMinSpeed(buffer->getFloat());
//m_vehicle->setMinSpeed(buffer->getFloat());
// Unused now
float time_rot = buffer->getFloat();
time_rot = buffer->getFloat();
// Set timed rotation divides by time_rot
m_vehicle->setTimedRotation(time_rot, time_rot*buffer->getVec3());
m_vehicle->setCushioningDisableTime(buffer->getUInt8());

View File

@@ -23,10 +23,12 @@
#include "items/attachment.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "modes/follow_the_leader.hpp"
#include "modes/three_strikes_battle.hpp"
#include "network/network_config.hpp"
#include "physics/physics.hpp"
#include "physics/triangle_mesh.hpp"
#include "race/race_manager.hpp"
#include "tracks/drive_graph.hpp"
#include "tracks/quad.hpp"
#include "tracks/track.hpp"
@@ -45,7 +47,7 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue,
: AbstractKartAnimation(kart, "RescueAnimation")
{
btTransform prev_trans = kart->getTrans();
// Get the required final physicial transform for network, then reset back
// Get the required final physical transform for network, then reset back
// to the original transform
World::getWorld()->moveKartAfterRescue(kart);
@@ -89,6 +91,15 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue,
}
}
// Allow FTL mode to apply special action when the leader is rescued
if (race_manager->isFollowMode())
{
FollowTheLeaderRace *ftl_world =
dynamic_cast<FollowTheLeaderRace*>(World::getWorld());
if(ftl_world->isLeader(kart->getWorldKartId()))
ftl_world->leaderRescued();
}
// Clear powerups when rescue in CTF
if (!from_state)
{

View File

@@ -21,7 +21,7 @@
#ifdef SKID_DEBUG
# include "graphics/show_curve.hpp"
#endif
#include "achievements/achievement_info.hpp"
#include "achievements/achievements_status.hpp"
#include "config/player_manager.hpp"
#include "karts/kart.hpp"
#include "karts/kart_gfx.hpp"
@@ -516,7 +516,11 @@ void Skidding::update(int ticks, bool is_on_ground,
if (m_kart->getController()->canGetAchievements())
{
PlayerManager::increaseAchievement(
AchievementInfo::ACHIEVE_SKIDDING, "skidding");
AchievementsStatus::SKIDDING_1LAP, 1);
PlayerManager::increaseAchievement(
AchievementsStatus::SKIDDING_1RACE, 1);
PlayerManager::increaseAchievement(
AchievementsStatus::SKIDDING, 1);
}
}
else {

View File

@@ -106,9 +106,9 @@ void CaptureTheFlag::init()
} // init
// ----------------------------------------------------------------------------
void CaptureTheFlag::reset()
void CaptureTheFlag::reset(bool restart)
{
FreeForAll::reset();
FreeForAll::reset(restart);
m_red_trans = m_orig_red_trans;
m_blue_trans = m_orig_blue_trans;
m_red_return_ticks = m_blue_return_ticks = m_red_scores =

View File

@@ -73,7 +73,7 @@ public:
// ------------------------------------------------------------------------
virtual void init() OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset() OVERRIDE;
virtual void reset(bool restart=false) OVERRIDE;
// ------------------------------------------------------------------------
virtual void update(int ticks) OVERRIDE;
// ------------------------------------------------------------------------

View File

@@ -172,9 +172,9 @@ CutsceneWorld::~CutsceneWorld()
{
} // ~CutsceneWorld
//-----------------------------------------------------------------------------
void CutsceneWorld::reset()
void CutsceneWorld::reset(bool restart)
{
World::reset();
World::reset(restart);
m_phase = RACE_PHASE;
}
//-----------------------------------------------------------------------------
@@ -206,6 +206,8 @@ void CutsceneWorld::update(int ticks)
{
//printf("INITIAL TIME for CutsceneWorld\n");
music_manager->startMusic();
PtrVector<TrackObject>& objects = Track::getCurrentTrack()
->getTrackObjectManager()->getObjects();
TrackObject* curr;

View File

@@ -65,7 +65,7 @@ public:
virtual void init() OVERRIDE;
virtual void reset() OVERRIDE;
virtual void reset(bool restart=false) OVERRIDE;
// clock events
virtual bool isRaceOver() OVERRIDE;

View File

@@ -212,11 +212,11 @@ bool EasterEggHunt::isRaceOver()
} // isRaceOver
//-----------------------------------------------------------------------------
/** Called then a battle is restarted.
/** Called when an egg hunt is restarted.
*/
void EasterEggHunt::reset()
void EasterEggHunt::reset(bool restart)
{
LinearWorld::reset();
LinearWorld::reset(restart);
for(unsigned int i=0; i<m_eggs_collected.size(); i++)
m_eggs_collected[i] = 0;

View File

@@ -56,7 +56,7 @@ public:
virtual bool isRaceOver() OVERRIDE;
// overriding World methods
virtual void reset() OVERRIDE;
virtual void reset(bool restart=false) OVERRIDE;
virtual bool raceHasLaps() OVERRIDE { return false; }

View File

@@ -75,9 +75,9 @@ FollowTheLeaderRace::~FollowTheLeaderRace()
//-----------------------------------------------------------------------------
/** Called just before a race is started.
*/
void FollowTheLeaderRace::reset()
void FollowTheLeaderRace::reset(bool restart)
{
LinearWorld::reset();
LinearWorld::reset(restart);
m_last_eliminated_time = 0.0f;
m_leader_intervals.clear();
m_leader_intervals = stk_config->m_leader_intervals;
@@ -221,6 +221,15 @@ bool FollowTheLeaderRace::isRaceOver()
}
} // isRaceOver
//-----------------------------------------------------------------------------
/** If the leader kart is hit, increase the delay to the next elimination */
void FollowTheLeaderRace::leaderHit()
{
int countdown = getTimeTicks();
countdown += stk_config->time2Ticks(5.0f);
setTicks(countdown);
} // leaderHit
//-----------------------------------------------------------------------------
/** Called at the end of a race. Updates highscores, pauses the game, and
* informs the unlock manager about the finished race. This function must

View File

@@ -46,7 +46,7 @@ public:
virtual int getScoreForPosition(int p) OVERRIDE;
// overriding World methods
virtual void reset() OVERRIDE;
virtual void reset(bool restart=false) OVERRIDE;
virtual const std::string& getIdent() const OVERRIDE;
virtual const btTransform &getStartTransform(int index) OVERRIDE;
virtual void getKartsDisplayInfo(
@@ -60,6 +60,11 @@ public:
// ------------------------------------------------------------------------
/** Returns if faster music should be used at the end. */
virtual bool useFastMusicNearEnd() const OVERRIDE { return false; }
bool isLeader(int kart_id) { return (kart_id == 0); }
void leaderHit();
// For now, use a similar countdown change as with leaderHit
void leaderRescued() { leaderHit(); }
}; // FollowTheLeader

View File

@@ -58,9 +58,9 @@ void FreeForAll::init()
// ----------------------------------------------------------------------------
/** Called when a battle is restarted.
*/
void FreeForAll::reset()
void FreeForAll::reset(bool restart)
{
WorldWithRank::reset();
WorldWithRank::reset(restart);
if (race_manager->hasTimeTarget())
{
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,

View File

@@ -46,7 +46,7 @@ public:
// ------------------------------------------------------------------------
virtual bool isRaceOver() OVERRIDE;
// ------------------------------------------------------------------------
virtual void reset() OVERRIDE;
virtual void reset(bool restart=false) OVERRIDE;
// ------------------------------------------------------------------------
virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;

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