Store achievements in a goal tree ; update achievements progress entirely depending on config
This commit is contained in:
parent
3eab9b11b6
commit
a4d81beda4
@ -1,10 +1,42 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- List of counters the achievements can query.
|
||||
The format to use is <name_of_the_counter value="X"/>
|
||||
where X is the desired value of the counter ;
|
||||
for example <won_races value="10"/>
|
||||
TODO : support asking for sum of values
|
||||
TODO : support logical relations (AND/OR) between values
|
||||
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.
|
||||
@ -66,6 +98,7 @@
|
||||
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.
|
||||
|
||||
@ -73,63 +106,73 @@
|
||||
banana-1race
|
||||
skidding
|
||||
skidding-1race
|
||||
skidding-1lap -->
|
||||
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" 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" name="Strike!" description="Hit 10 karts with a bowling-ball.">
|
||||
<ball goal="10"/>
|
||||
</achievement>
|
||||
<achievement id="3" name="Arch Enemy" description="Hit the same kart at least 5 times in one race.">
|
||||
<hit goal="5"/>
|
||||
</achievement>
|
||||
<achievement id="4" name="Marathoner" description="Finish a race with at least twice the track's default lap number.">
|
||||
<laps goal="1"/>
|
||||
</achievement>
|
||||
<achievement id="5" name="Skid-row" description="Make 5 skidding in a single lap.">
|
||||
<skidding goal="5"/>
|
||||
</achievement>
|
||||
<achievement id="6" name="Gold driver" description="Win against at least 3 AIs in normal race, time-trial, and follow the leader.">
|
||||
<standard goal="1"/>
|
||||
<std_timetrial goal="1"/>
|
||||
<follow_leader goal="1"/>
|
||||
</achievement>
|
||||
<achievement id="7" name="Powerup Love" description="Use 10 or more powerups in a race.">
|
||||
<poweruplover goal="10"/>
|
||||
</achievement>
|
||||
<achievement id="8" name="Beyond Luck" description="Win 5 single races in a row against at least 3 AIs. Beware, restarting a race counts as a loss.">
|
||||
<wins goal="5"/>
|
||||
</achievement>
|
||||
<achievement id="9" name="Banana Lover" description="Collect at least 5 bananas in one race.">
|
||||
<banana goal="5"/>
|
||||
</achievement>
|
||||
<achievement id="10" name="It's secret" description="Really ... a secret.">
|
||||
</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.">
|
||||
<swatter goal="5"/>
|
||||
</achievement>
|
||||
<achievement id="12" name="Unstoppable" 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.">
|
||||
<wins goal="10"/>
|
||||
</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="Beyond Luck" 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="Unstoppable" 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>
|
||||
|
@ -32,10 +32,11 @@
|
||||
/** 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_achieved = false;
|
||||
m_achievement_info->copyGoalTree(m_progress_goal_tree, m_achievement_info->m_goal_tree, true /*set values to 0*/);
|
||||
} // Achievement
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -63,109 +64,186 @@ void Achievement::saveProgress(UTFWriter &out)
|
||||
out << "/>\n";
|
||||
} // save
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the value for a key.
|
||||
*/
|
||||
int Achievement::getValue(const std::string & key)
|
||||
{
|
||||
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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** 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'.
|
||||
* The AchievementInfo adds up the number of goals if there are several,
|
||||
* or take the target value of the goal if there is only one.
|
||||
* This do the same, but with achieved value or fullfilled goals.
|
||||
*/
|
||||
irr::core::stringw Achievement::getProgressAsString() const
|
||||
irr::core::stringw Achievement::getProgressAsString()
|
||||
{
|
||||
//TODO : add a progress computation function.
|
||||
int progress = 0;
|
||||
std::map<std::string, int>::const_iterator iter;
|
||||
irr::core::stringw target = getInfo()->toString();
|
||||
|
||||
// For now return N/N in case of an achieved achievement.
|
||||
if (m_achieved)
|
||||
return getInfo()->toString() +"/" + getInfo()->toString();
|
||||
return target + "/" + target;
|
||||
|
||||
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
|
||||
{
|
||||
progress += iter->second;
|
||||
}
|
||||
|
||||
return StringUtils::toWString(progress) + "/" + getInfo()->toString();
|
||||
return StringUtils::toWString(progress) + "/" + target;
|
||||
} // getProgressAsString
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** 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.
|
||||
/** 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::increase(const std::string & key,
|
||||
const std::string &goal_key, int increase)
|
||||
void Achievement::setGoalValue(std::string &goal_string, int value)
|
||||
{
|
||||
std::map<std::string, int>::iterator it;
|
||||
it = m_progress_map.find(key);
|
||||
if (it != m_progress_map.end())
|
||||
if(m_achieved) // This should not happen, but it costs little to double-check
|
||||
return;
|
||||
|
||||
bool AO = true;
|
||||
bool SAAO = true;
|
||||
if (goal_string.compare(0 /*start of sub-string*/,5/*length*/,"LOGC-") == 0)
|
||||
{
|
||||
it->second += increase;
|
||||
if (it->second > m_achievement_info->getGoalValue(goal_key))
|
||||
it->second = m_achievement_info->getGoalValue(goal_key);
|
||||
AO = false;
|
||||
goal_string = goal_string.substr(5,999);
|
||||
}
|
||||
else
|
||||
else if (goal_string.compare(0 /*start of sub-string*/,5/*length*/,"LOGM-") == 0)
|
||||
{
|
||||
if (increase>m_achievement_info->getGoalValue(goal_key))
|
||||
increase = m_achievement_info->getGoalValue(goal_key);
|
||||
m_progress_map[key] = increase;
|
||||
SAAO = false;
|
||||
goal_string = goal_string.substr(5,999);
|
||||
}
|
||||
check();
|
||||
} // increase
|
||||
|
||||
bool found = recursiveSetGoalValue(m_progress_goal_tree, goal_string, value, AO, SAAO);
|
||||
|
||||
// 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", getID());
|
||||
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
|
||||
|
@ -33,12 +33,7 @@ class XMLNode;
|
||||
// ============================================================================
|
||||
/** 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. 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.
|
||||
* status to be saved, and detects when an achievement is fulfilled.
|
||||
* \ingroup achievements
|
||||
*/
|
||||
class AchievementInfo;
|
||||
@ -47,38 +42,36 @@ class Achievement
|
||||
{
|
||||
private:
|
||||
/** 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;
|
||||
/** 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;
|
||||
|
||||
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);
|
||||
public:
|
||||
|
||||
Achievement(const AchievementInfo * info);
|
||||
Achievement(AchievementInfo * info);
|
||||
virtual ~Achievement();
|
||||
virtual void loadProgress(const XMLNode *node);
|
||||
virtual void saveProgress(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 reset();
|
||||
virtual irr::core::stringw getProgressAsString() const;
|
||||
virtual irr::core::stringw getProgressAsString();
|
||||
|
||||
uint32_t getID() const { return m_achievement_info->getID(); }
|
||||
const AchievementInfo * getInfo() const { return m_achievement_info; }
|
||||
uint32_t getID() const { return m_achievement_info->getID(); }
|
||||
AchievementInfo * getInfo() { return m_achievement_info; }
|
||||
|
||||
void setAchieved() { m_achieved = true; };
|
||||
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
|
||||
|
@ -49,59 +49,124 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
|
||||
|
||||
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 (node->getName() != "goal")
|
||||
continue; // ignore incorrect node
|
||||
|
||||
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);
|
||||
}
|
||||
if (m_goal_values.size() != input->getNumNodes())
|
||||
Log::fatal("AchievementInfo",
|
||||
"Duplicate keys for the entries of a MapAchievement found.");
|
||||
} // AchievementInfo
|
||||
if (parent.children.size() != input->getNumNodes())
|
||||
Log::error("AchievementInfo",
|
||||
"Incorrect goals for the entries of achievement \"%s\".", m_name.c_str());
|
||||
} // parseGoals
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Copy a goal tree to an EMPTY goal tree by recursion. */
|
||||
void AchievementInfo::copyGoalTree(goalTree ©, goalTree &model, bool set_values_to_zero)
|
||||
{
|
||||
copy.type = model.type;
|
||||
copy.value = (set_values_to_zero) ? 0 : model.value;
|
||||
copy.operation = model.operation;
|
||||
|
||||
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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns a string with a numerical value to display the progress of
|
||||
* this achievement. It adds up all the goal values
|
||||
* this achievement.
|
||||
* If it has multiple goal, it returns the number of goals. If it has
|
||||
* only one (it can be a sum), it returns the required value for that goal.
|
||||
* FIXME : don't work well for "all tracks" goals.
|
||||
*/
|
||||
irr::core::stringw AchievementInfo::toString() const
|
||||
irr::core::stringw AchievementInfo::toString()
|
||||
{
|
||||
int count = 0;
|
||||
std::map<std::string, int>::const_iterator iter;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
return StringUtils::toWString(count);
|
||||
|
||||
return StringUtils::toWString(recursiveGoalCount(m_goal_tree));
|
||||
} // toString
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AchievementInfo::checkCompletion(Achievement * achievement) const
|
||||
int AchievementInfo::recursiveGoalCount(goalTree &parent)
|
||||
{
|
||||
std::map<std::string, int>::const_iterator iter;
|
||||
|
||||
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
|
||||
{
|
||||
if (achievement->getValue(iter->first) < iter->second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
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;
|
||||
if (parent.children.size() != 1)
|
||||
return m_goal_tree.children.size();
|
||||
else if (parent.children[0].type == "AND" ||
|
||||
parent.children[0].type == "AND-AT-ONCE" ||
|
||||
parent.children[0].type == "OR")
|
||||
return recursiveGoalCount(parent.children[0]);
|
||||
else
|
||||
return 0;
|
||||
} // getGoalValue
|
||||
// ----------------------------------------------------------------------------
|
||||
return parent.children[0].value;
|
||||
} // recursiveGoalCount
|
||||
|
@ -33,27 +33,28 @@ class Achievement;
|
||||
|
||||
/** This class stores an achievement definition from the xml file, including
|
||||
* title, description, but also how to achieve this achievement.
|
||||
* Constrat with the Achievement class, which is a player-specific instance
|
||||
* Contrast with the Achievement class, which is a player-specific instance
|
||||
* tracking the progress of the achievement.
|
||||
* \ingroup achievements
|
||||
*/
|
||||
class AchievementInfo
|
||||
{
|
||||
public:
|
||||
//FIXME : try to get rid of this list
|
||||
/** 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_BEYOND_LUCK = 8,
|
||||
ACHIEVE_BANANA = 9,
|
||||
ACHIEVE_MOSQUITO = 11,
|
||||
ACHIEVE_UNSTOPPABLE = 12
|
||||
// The operations supported for a goal
|
||||
enum operationType {
|
||||
OP_NONE = 0,
|
||||
OP_ADD = 1,
|
||||
OP_SUBSTRACT = 2,
|
||||
};
|
||||
|
||||
// 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:
|
||||
@ -66,24 +67,29 @@ private:
|
||||
/** The description of this achievement. */
|
||||
irr::core::stringw m_description;
|
||||
|
||||
/** The target values needed to be reached. */
|
||||
std::map<std::string, int> m_goal_values;
|
||||
|
||||
/** A secret achievement has its progress not shown. */
|
||||
bool m_is_secret;
|
||||
|
||||
void parseGoals(const XMLNode * input, goalTree &parent);
|
||||
int recursiveGoalCount(goalTree &parent);
|
||||
protected:
|
||||
friend class Achievement;
|
||||
/** 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 toString();
|
||||
|
||||
uint32_t getID() const { return m_id; }
|
||||
irr::core::stringw getDescription() const { return _(m_description.c_str()); }
|
||||
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
|
||||
|
||||
|
||||
|
@ -58,13 +58,15 @@ AchievementsStatus::AchievementsStatus()
|
||||
|
||||
TrackStats new_track;
|
||||
new_track.ident = curr->getIdent();
|
||||
new_track.race_started = new_track.race_finished = new_track.race_won = 0;
|
||||
new_track.race_finished_reverse = new_track.race_finished_alone = 0;
|
||||
new_track.less_laps = new_track.more_laps = new_track.min_twice_laps = 0;
|
||||
new_track.egg_hunt_started = new_track.egg_hunt_finished = 0;
|
||||
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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -79,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.
|
||||
@ -132,20 +218,21 @@ void AchievementsStatus::load(const XMLNode * input)
|
||||
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].race_started);
|
||||
xml_achievement_tracks[i]->get("fin",&m_track_stats[j].race_finished);
|
||||
xml_achievement_tracks[i]->get("won",&m_track_stats[j].race_won);
|
||||
xml_achievement_tracks[i]->get("fin_rev",&m_track_stats[j].race_finished_reverse);
|
||||
xml_achievement_tracks[i]->get("fin_al",&m_track_stats[j].race_finished_alone);
|
||||
xml_achievement_tracks[i]->get("less_laps",&m_track_stats[j].less_laps);
|
||||
xml_achievement_tracks[i]->get("more_laps",&m_track_stats[j].more_laps);
|
||||
xml_achievement_tracks[i]->get("twice_laps",&m_track_stats[j].min_twice_laps);
|
||||
xml_achievement_tracks[i]->get("eh_sta",&m_track_stats[j].egg_hunt_started);
|
||||
xml_achievement_tracks[i]->get("eh_fin",&m_track_stats[j].egg_hunt_finished);
|
||||
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;
|
||||
}
|
||||
@ -155,16 +242,16 @@ void AchievementsStatus::load(const XMLNode * input)
|
||||
{
|
||||
TrackStats new_track;
|
||||
new_track.ident = ident;
|
||||
xml_achievement_tracks[i]->get("sta",&new_track.race_started);
|
||||
xml_achievement_tracks[i]->get("fin",&new_track.race_finished);
|
||||
xml_achievement_tracks[i]->get("won",&new_track.race_won);
|
||||
xml_achievement_tracks[i]->get("fin_rev",&new_track.race_finished_reverse);
|
||||
xml_achievement_tracks[i]->get("fin_al",&new_track.race_finished_alone);
|
||||
xml_achievement_tracks[i]->get("less_laps",&new_track.less_laps);
|
||||
xml_achievement_tracks[i]->get("more_laps",&new_track.more_laps);
|
||||
xml_achievement_tracks[i]->get("twice_laps",&new_track.min_twice_laps);
|
||||
xml_achievement_tracks[i]->get("eh_sta",&new_track.egg_hunt_started);
|
||||
xml_achievement_tracks[i]->get("eh_fin",&new_track.egg_hunt_finished);
|
||||
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);
|
||||
}
|
||||
@ -172,8 +259,24 @@ void AchievementsStatus::load(const XMLNode * input)
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
@ -203,16 +306,16 @@ void AchievementsStatus::save(UTFWriter &out)
|
||||
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].race_started << "\"";
|
||||
out << " fin=\"" << m_track_stats[n].race_finished << "\"";
|
||||
out << " won=\"" << m_track_stats[n].race_won << "\"";
|
||||
out << " fin_rev=\"" << m_track_stats[n].race_finished_reverse << "\"";
|
||||
out << " fin_al=\"" << m_track_stats[n].race_finished_alone << "\"";
|
||||
out << " less_laps=\"" << m_track_stats[n].less_laps << "\"";
|
||||
out << " more_laps=\"" << m_track_stats[n].more_laps << "\"";
|
||||
out << " twice_laps=\"" << m_track_stats[n].min_twice_laps << "\"";
|
||||
out << " eh_sta=\"" << m_track_stats[n].egg_hunt_started << "\"";
|
||||
out << " eh_fin=\"" << m_track_stats[n].egg_hunt_finished << "\"";
|
||||
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";
|
||||
@ -275,117 +378,70 @@ void AchievementsStatus::sync(const std::vector<uint32_t> & achieved_ids)
|
||||
* \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.
|
||||
FIXME It is currently hard-coded to specific achievements,
|
||||
until it can entirely supersedes the previous system and
|
||||
removes its complications.
|
||||
FIXME : a data id don't cover some situations, and the function itself ignores it */
|
||||
* the update. */
|
||||
void AchievementsStatus::updateAchievementsProgress(UpdateType type, unsigned int enum_id)
|
||||
{
|
||||
Achievement *beyond_luck = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_BEYOND_LUCK);
|
||||
if (!beyond_luck->isAchieved())
|
||||
std::string goal_string[2];
|
||||
int max_across_tracks = -1;
|
||||
int min_across_tracks = -1;
|
||||
int max_kart_hits = -1;
|
||||
if (type == UP_ACHIEVEMENT_DATA)
|
||||
{
|
||||
beyond_luck->reset();
|
||||
beyond_luck->increase("wins", "wins", m_variables[CONS_WON_RACES].counter);
|
||||
}
|
||||
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
|
||||
|
||||
Achievement *unstoppable = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_UNSTOPPABLE);
|
||||
if (!unstoppable->isAchieved())
|
||||
{
|
||||
unstoppable->reset();
|
||||
unstoppable->increase("wins", "wins", m_variables[CONS_WON_RACES_HARD].counter);
|
||||
}
|
||||
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;
|
||||
|
||||
Achievement *gold_driver = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER);
|
||||
if (!gold_driver->isAchieved())
|
||||
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)
|
||||
{
|
||||
gold_driver->reset();
|
||||
gold_driver->increase("standard", "standard", m_variables[WON_NORMAL_RACES].counter);
|
||||
gold_driver->increase("std_timetrial", "std_timetrial", m_variables[WON_TT_RACES].counter);
|
||||
gold_driver->increase("follow_leader", "follow_leader", m_variables[WON_FTL_RACES].counter);
|
||||
}
|
||||
goal_string[0] = "hit-same-kart-1race";
|
||||
|
||||
Achievement *powerup_lover = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_POWERUP_LOVER);
|
||||
if (!powerup_lover->isAchieved())
|
||||
{
|
||||
powerup_lover->reset();
|
||||
powerup_lover->increase("poweruplover", "poweruplover", m_variables[POWERUP_USED_1RACE].counter);
|
||||
}
|
||||
|
||||
Achievement *banana_lover = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_BANANA);
|
||||
if (!banana_lover->isAchieved())
|
||||
{
|
||||
banana_lover->reset();
|
||||
banana_lover->increase("banana", "banana", m_variables[BANANA_1RACE].counter);
|
||||
}
|
||||
|
||||
Achievement *skidding = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_SKIDDING);
|
||||
if (!skidding->isAchieved())
|
||||
{
|
||||
skidding->reset();
|
||||
skidding->increase("skidding", "skidding", m_variables[SKIDDING_1LAP].counter);
|
||||
}
|
||||
|
||||
Achievement *strike = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_STRIKE);
|
||||
if (!strike->isAchieved())
|
||||
{
|
||||
strike->reset();
|
||||
strike->increase("ball", "ball", m_variables[BOWLING_HIT].counter);
|
||||
}
|
||||
|
||||
Achievement *mosquito = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_MOSQUITO);
|
||||
if (!mosquito->isAchieved())
|
||||
{
|
||||
mosquito->reset();
|
||||
mosquito->increase("swatter", "swatter", m_variables[SWATTER_HIT_1RACE].counter);
|
||||
}
|
||||
|
||||
Achievement *arch_enemy = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_ARCH_ENEMY);
|
||||
if (!arch_enemy->isAchieved())
|
||||
{
|
||||
arch_enemy->reset();
|
||||
int max_hits = 0;
|
||||
for (unsigned int i=0;i<m_kart_hits.size();i++)
|
||||
{
|
||||
if (m_kart_hits[i] > max_hits)
|
||||
max_hits = m_kart_hits[i];
|
||||
if (m_kart_hits[i] > max_kart_hits)
|
||||
max_kart_hits = m_kart_hits[i];
|
||||
}
|
||||
arch_enemy->increase("hit", "hit", max_hits);
|
||||
}
|
||||
} // if type == UP_KART_HITS
|
||||
|
||||
|
||||
Achievement *marathoner = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_MARATHONER);
|
||||
if (!marathoner->isAchieved())
|
||||
// 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++)
|
||||
{
|
||||
marathoner->reset();
|
||||
for (unsigned int i=0;i<m_track_stats.size();i++)
|
||||
{
|
||||
// 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;
|
||||
// Don't bother checking again already completed achievements
|
||||
if (i->second->isAchieved())
|
||||
continue;
|
||||
|
||||
if (m_track_stats[i].min_twice_laps >= 1)
|
||||
{
|
||||
marathoner->increase("laps", "laps", 1);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Achievement *columbus = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_COLUMBUS);
|
||||
if (!columbus->isAchieved())
|
||||
{
|
||||
columbus->reset();
|
||||
for (unsigned int i=0;i<m_track_stats.size();i++)
|
||||
{
|
||||
// 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;
|
||||
|
||||
columbus->increase(m_track_stats[i].ident, m_track_stats[i].ident, std::min<int>(m_track_stats[i].race_finished,1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -429,7 +485,23 @@ void AchievementsStatus::onRaceEnd(bool aborted)
|
||||
{
|
||||
onLapEnd();
|
||||
|
||||
updateAchievementsProgress(UP_ACHIEVEMENT_DATA, 0);
|
||||
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;
|
||||
@ -438,20 +510,30 @@ void AchievementsStatus::onRaceEnd(bool aborted)
|
||||
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()
|
||||
{
|
||||
updateAchievementsProgress(UP_ACHIEVEMENT_DATA, 0);
|
||||
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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -469,26 +551,9 @@ void AchievementsStatus::trackEvent(std::string track_ident, AchievementsStatus:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (event==TR_STARTED)
|
||||
m_track_stats[track_id].race_started++;
|
||||
else if (event==TR_FINISHED)
|
||||
m_track_stats[track_id].race_finished++;
|
||||
else if (event==TR_WON)
|
||||
m_track_stats[track_id].race_won++;
|
||||
else if (event==TR_FINISHED_REVERSE)
|
||||
m_track_stats[track_id].race_finished_reverse++;
|
||||
else if (event==TR_FINISHED_ALONE)
|
||||
m_track_stats[track_id].race_finished_alone++;
|
||||
else if (event==TR_LESS_LAPS)
|
||||
m_track_stats[track_id].less_laps++;
|
||||
else if (event==TR_MORE_LAPS)
|
||||
m_track_stats[track_id].more_laps++;
|
||||
else if (event==TR_MIN_TWICE_LAPS)
|
||||
m_track_stats[track_id].min_twice_laps++;
|
||||
else if (event==TR_EGG_HUNT_STARTED)
|
||||
m_track_stats[track_id].egg_hunt_started++;
|
||||
else if (event==TR_EGG_HUNT_FINISHED)
|
||||
m_track_stats[track_id].egg_hunt_finished++;
|
||||
|
||||
m_track_stats[track_id].track_data[(int)event]++;
|
||||
updateAchievementsProgress(UP_TRACK_DATA, (int)event);
|
||||
} // trackEvent
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -55,61 +55,71 @@ public :
|
||||
// 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 = 5,
|
||||
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 = 6,
|
||||
EASY_FINISHED = 7,
|
||||
MEDIUM_STARTED = 8,
|
||||
MEDIUM_FINISHED = 9,
|
||||
HARD_STARTED = 10,
|
||||
HARD_FINISHED = 11,
|
||||
BEST_STARTED = 12,
|
||||
BEST_FINISHED = 13,
|
||||
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 = 14,
|
||||
NORMAL_FINISHED = 15,
|
||||
TT_STARTED = 16,
|
||||
TT_FINISHED = 17,
|
||||
FTL_STARTED = 18,
|
||||
FTL_FINISHED = 19,
|
||||
THREE_STRIKES_STARTED = 20,
|
||||
THREE_STRIKES_FINISHED = 21,
|
||||
SOCCER_STARTED = 22,
|
||||
SOCCER_FINISHED = 23,
|
||||
EGG_HUNT_STARTED = 24,
|
||||
EGG_HUNT_FINISHED = 25,
|
||||
WITH_GHOST_STARTED = 26,
|
||||
WITH_GHOST_FINISHED = 27,
|
||||
CTF_STARTED = 28,
|
||||
CTF_FINISHED = 29,
|
||||
FFA_STARTED = 30,
|
||||
FFA_FINISHED = 31,
|
||||
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 = 32,
|
||||
POWERUP_USED_1RACE = 33,
|
||||
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 = 34,
|
||||
BOWLING_HIT_1RACE = 35,
|
||||
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 = 36,
|
||||
SWATTER_HIT_1RACE = 37,
|
||||
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 = 38,
|
||||
ALL_HITS_1RACE = 39,
|
||||
ALL_HITS = 43,
|
||||
ALL_HITS_1RACE = 44,
|
||||
ALL_HITS_1RACE_MAX = 45,
|
||||
// Count the number of bananas hit
|
||||
BANANA = 40,
|
||||
BANANA_1RACE = 41,
|
||||
BANANA = 46,
|
||||
BANANA_1RACE = 47,
|
||||
BANANA_1RACE_MAX = 48,
|
||||
// Count how many times the player skidded
|
||||
SKIDDING_1LAP = 42,
|
||||
SKIDDING_1RACE = 43,
|
||||
SKIDDING = 44,
|
||||
SKIDDING = 49,
|
||||
SKIDDING_1RACE = 50,
|
||||
SKIDDING_1RACE_MAX = 51,
|
||||
SKIDDING_1LAP = 52,
|
||||
SKIDDING_1LAP_MAX = 53,
|
||||
|
||||
ACHIEVE_DATA_NUM = 45
|
||||
|
||||
ACHIEVE_DATA_NUM = 54
|
||||
};
|
||||
|
||||
private:
|
||||
@ -126,51 +136,55 @@ private:
|
||||
int counter;
|
||||
};
|
||||
|
||||
const int DATA_VERSION = 3;
|
||||
const int DATA_VERSION = 4;
|
||||
|
||||
// The tracked values are defined at compile time
|
||||
AchievementVariable m_variables[ACHIEVE_DATA_NUM];
|
||||
|
||||
// To keep track of track-specific data without hardcoding
|
||||
// a list of tracks, we use a special structure.
|
||||
struct TrackStats
|
||||
{
|
||||
std::string ident;
|
||||
// counters for standard, TT & FTL races
|
||||
int race_started;
|
||||
int race_finished;
|
||||
int race_won; // doesn't count race without any other AI/player
|
||||
int race_finished_reverse;
|
||||
int race_finished_alone; // races against replays are counted, too
|
||||
// counters for standard & TT races, apply to finished races only,
|
||||
// lap number compared to track default.
|
||||
int less_laps;
|
||||
int more_laps;
|
||||
int min_twice_laps; // at least twice the track's default lap count
|
||||
// counters for egg hunts
|
||||
int egg_hunt_started;
|
||||
int egg_hunt_finished;
|
||||
};
|
||||
// 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,
|
||||
TR_LESS_LAPS = 4,
|
||||
TR_MORE_LAPS = 5,
|
||||
TR_MIN_TWICE_LAPS = 6,
|
||||
TR_FINISHED_ALONE = 7,
|
||||
// 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_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,
|
||||
@ -197,6 +211,10 @@ private:
|
||||
SyncAchievementsRequest() : Online::XMLRequest(true) {}
|
||||
};
|
||||
|
||||
void setEnumToString();
|
||||
void updateAchievementsProgress(UpdateType type, unsigned int enum_id);
|
||||
void updateAllAchievementsProgress();
|
||||
|
||||
public :
|
||||
AchievementsStatus();
|
||||
~AchievementsStatus();
|
||||
@ -205,7 +223,6 @@ public :
|
||||
void save(UTFWriter &out);
|
||||
void add(Achievement *achievement);
|
||||
void sync(const std::vector<uint32_t> & achieved_ids);
|
||||
void updateAchievementsProgress(UpdateType type, unsigned int enum_id);
|
||||
void increaseDataVar(unsigned int achieve_data_id, int increase);
|
||||
void resetDataVar(unsigned int achieve_data_id);
|
||||
void onRaceEnd(bool aborted=false);
|
||||
@ -214,7 +231,7 @@ public :
|
||||
void resetKartHits(int num_karts);
|
||||
void addKartHit(int kart_id);
|
||||
// ------------------------------------------------------------------------
|
||||
const std::map<uint32_t, Achievement *>& getAllAchievements()
|
||||
std::map<uint32_t, Achievement *>& getAllAchievements()
|
||||
{
|
||||
return m_achievements;
|
||||
}
|
||||
|
@ -141,30 +141,6 @@ 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);
|
||||
|
||||
} // increaseAchievement
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** A handy shortcut to increase points for an achievement data of the
|
||||
|
@ -98,14 +98,14 @@ void BaseOnlineProfileAchievements::init()
|
||||
// No need to wait for results, since they are local anyway
|
||||
m_waiting_for_achievements = false;
|
||||
m_achievements_list_widget->clear();
|
||||
const std::map<uint32_t, Achievement *> & all_achievements =
|
||||
std::map<uint32_t, Achievement *> & all_achievements =
|
||||
PlayerManager::getCurrentPlayer()->getAchievementsStatus()
|
||||
->getAllAchievements();
|
||||
std::map<uint32_t, Achievement *>::const_iterator it;
|
||||
for (it = all_achievements.begin(); it != all_achievements.end(); ++it)
|
||||
{
|
||||
std::vector<ListWidget::ListCell> row;
|
||||
const Achievement *a = it->second;
|
||||
Achievement *a = it->second;
|
||||
if(a->getInfo()->isSecret() && !a->isAchieved())
|
||||
continue;
|
||||
ListWidget::ListCell title(translations->fribidize(a->getInfo()->getName()), -1, 2);
|
||||
|
Loading…
Reference in New Issue
Block a user