Merge remote-tracking branch 'origin/master' into network-item-debugging
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
29
data/gui/dialogs/online/achievement_progress_dialog.stkgui
Normal file
29
data/gui/dialogs/online/achievement_progress_dialog.stkgui
Normal 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>
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
BIN
data/skins/common/glass_square_pink.png
Normal file
BIN
data/skins/common/glass_square_pink.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ©, 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
|
||||
|
||||
@@ -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 ©, goalTree &model, bool set_values_to_zero);
|
||||
}; // class AchievementInfo
|
||||
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@ AchievementsStatus*
|
||||
status->add(achievement);
|
||||
}
|
||||
|
||||
status->updateAllAchievementsProgress();
|
||||
|
||||
if (node)
|
||||
status->load(node);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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])
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
namespace SP
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user