Added support for different types of achievement checking: besides
'all-at-least' (all keys must be >= the corresponding goal value) now also 'one-at-least' (at least one key is >= a constant goal value) is supported. Implemented 'Arch Enemy' achievement: hit the same kart at least 5 times in one race.
This commit is contained in:
parent
2cb1f1fc78
commit
f8a462a39f
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<achievements>
|
<achievements>
|
||||||
<achievement id="1" type="map" title="Christoffel Columbus" description="Play every official track at least once." >
|
<achievement id="1" check-type="all-at-least" reset-after-race="no"
|
||||||
|
title="Christoffel Columbus" description="Play every official track at least once." >
|
||||||
<farm goal="1"/>
|
<farm goal="1"/>
|
||||||
<scotland goal="1"/>
|
<scotland goal="1"/>
|
||||||
<lighthouse goal="1"/>
|
<lighthouse goal="1"/>
|
||||||
@ -12,8 +13,13 @@
|
|||||||
<hacienda goal="1"/>
|
<hacienda goal="1"/>
|
||||||
<jungle goal="1"/>
|
<jungle goal="1"/>
|
||||||
</achievement>
|
</achievement>
|
||||||
<achievement id="2" type="single" title="Strike!" description="Hit 10 karts with a bowling-ball.">
|
<achievement id="2" check-type="all-at-least" reset-after-race="no"
|
||||||
|
title="Strike!" description="Hit 10 karts with a bowling-ball.">
|
||||||
<ball goal="10"/>
|
<ball goal="10"/>
|
||||||
</achievement>
|
</achievement>
|
||||||
|
<achievement id="3" check-type="one-at-least" reset-after-race="yes"
|
||||||
|
titl="Arch Enemy" description="Hit the same kart at least 5 times in one race">
|
||||||
|
<hit value="5"/>
|
||||||
|
</achievement>
|
||||||
</achievements>
|
</achievements>
|
||||||
|
|
||||||
|
@ -117,10 +117,24 @@ irr::core::stringw Achievement::getProgressAsString()
|
|||||||
{
|
{
|
||||||
int progress = 0;
|
int progress = 0;
|
||||||
std::map<std::string, int>::const_iterator iter;
|
std::map<std::string, int>::const_iterator iter;
|
||||||
|
switch (m_achievement_info->getCheckType())
|
||||||
|
{
|
||||||
|
case AchievementInfo::AC_ALL_AT_LEAST:
|
||||||
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
|
for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
|
||||||
{
|
{
|
||||||
progress += iter->second;
|
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();
|
return StringUtils::toWString(progress) + "/" + getInfo()->toString();
|
||||||
} // getProgressAsString
|
} // getProgressAsString
|
||||||
|
|
||||||
@ -132,12 +146,10 @@ irr::core::stringw Achievement::getProgressAsString()
|
|||||||
void Achievement::increase(const std::string & key, int increase)
|
void Achievement::increase(const std::string & key, int increase)
|
||||||
{
|
{
|
||||||
if (m_progress_map.find(key) != m_progress_map.end())
|
if (m_progress_map.find(key) != m_progress_map.end())
|
||||||
{
|
|
||||||
m_progress_map[key] += increase;
|
m_progress_map[key] += increase;
|
||||||
check();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
m_progress_map[key] = increase;
|
m_progress_map[key] = increase;
|
||||||
|
check();
|
||||||
} // increase
|
} // increase
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -82,6 +82,10 @@ public:
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
/** Returns if this achievement has been fulfilled. */
|
/** Returns if this achievement has been fulfilled. */
|
||||||
bool isAchieved() const { return m_achieved; }
|
bool isAchieved() const { return m_achieved; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
const std::map<std::string, int>& getProgress() const
|
||||||
|
{
|
||||||
|
return m_progress_map;
|
||||||
|
} // getProgress
|
||||||
}; // class Achievement
|
}; // class Achievement
|
||||||
#endif
|
#endif
|
@ -26,7 +26,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
/** The constructor reads the dat from the xml information.
|
||||||
|
* \param input XML node for this achievement info.
|
||||||
|
*/
|
||||||
AchievementInfo::AchievementInfo(const XMLNode * input)
|
AchievementInfo::AchievementInfo(const XMLNode * input)
|
||||||
{
|
{
|
||||||
m_reset_after_race = false;
|
m_reset_after_race = false;
|
||||||
@ -47,6 +49,17 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
|
|||||||
}
|
}
|
||||||
input->get("reset-after-race", &m_reset_after_race);
|
input->get("reset-after-race", &m_reset_after_race);
|
||||||
|
|
||||||
|
m_check_type = AC_ALL_AT_LEAST;
|
||||||
|
std::string s;
|
||||||
|
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());
|
||||||
|
|
||||||
// Now load the goal nodes
|
// Now load the goal nodes
|
||||||
for (unsigned int n = 0; n < input->getNumNodes(); n++)
|
for (unsigned int n = 0; n < input->getNumNodes(); n++)
|
||||||
{
|
{
|
||||||
@ -57,19 +70,42 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
|
|||||||
m_goal_values[key] = goal;
|
m_goal_values[key] = goal;
|
||||||
}
|
}
|
||||||
if (m_goal_values.size() != input->getNumNodes())
|
if (m_goal_values.size() != input->getNumNodes())
|
||||||
Log::error("MapAchievementInfo",
|
Log::fatal("AchievementInfo",
|
||||||
"Duplicate keys for the entries of a MapAchievement found.");
|
"Duplicate keys for the entries of a MapAchievement found.");
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
} // AchievementInfo
|
} // AchievementInfo
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
/** 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
|
irr::core::stringw AchievementInfo::toString() const
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
std::map<std::string, int>::const_iterator iter;
|
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++)
|
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
|
||||||
{
|
{
|
||||||
count += iter->second;
|
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 tpye %d.",
|
||||||
|
m_check_type);
|
||||||
|
}
|
||||||
return StringUtils::toWString(count);
|
return StringUtils::toWString(count);
|
||||||
|
|
||||||
} // toString
|
} // toString
|
||||||
@ -78,11 +114,34 @@ irr::core::stringw AchievementInfo::toString() const
|
|||||||
bool AchievementInfo::checkCompletion(Achievement * achievement) const
|
bool AchievementInfo::checkCompletion(Achievement * achievement) const
|
||||||
{
|
{
|
||||||
std::map<std::string, int>::const_iterator iter;
|
std::map<std::string, int>::const_iterator iter;
|
||||||
|
|
||||||
|
switch (m_check_type)
|
||||||
|
{
|
||||||
|
case AC_ALL_AT_LEAST:
|
||||||
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
|
for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
|
||||||
{
|
{
|
||||||
if (achievement->getValue(iter->first) < iter->second)
|
if (achievement->getValue(iter->first) < iter->second)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log::fatal("AchievementInfo", "Missing check for tpye %d.",
|
||||||
|
m_check_type);
|
||||||
|
} // switch
|
||||||
|
|
||||||
|
// Avoid compiler warning
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -45,13 +45,19 @@ public:
|
|||||||
enum { ACHIEVE_COLUMBUS = 1,
|
enum { ACHIEVE_COLUMBUS = 1,
|
||||||
ACHIEVE_FIRST = ACHIEVE_COLUMBUS,
|
ACHIEVE_FIRST = ACHIEVE_COLUMBUS,
|
||||||
ACHIEVE_STRIKE = 2,
|
ACHIEVE_STRIKE = 2,
|
||||||
ACHIEVE_LAST = ACHIEVE_STRIKE
|
ACHIEVE_ARCH_ENEMY = 3,
|
||||||
|
ACHIEVE_LAST = ACHIEVE_ARCH_ENEMY
|
||||||
};
|
};
|
||||||
/** Achievement types:
|
/** Achievement check type:
|
||||||
* SINGLE_AT_LEAST: a single value, which must at least be the
|
* ALL_AT_LEAST: All goal values must be reached (or exceeded).
|
||||||
* goal value.
|
* ONE_AT_LEAST: At least one current value reaches or exceedes the goal.
|
||||||
*/
|
*/
|
||||||
enum AchievementType { AT_SINGLE_AT_LEAST};
|
enum AchievementCheckType
|
||||||
|
{
|
||||||
|
AC_ALL_AT_LEAST,
|
||||||
|
AC_ONE_AT_LEAST
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** The id of this Achievement. */
|
/** The id of this Achievement. */
|
||||||
uint32_t m_id;
|
uint32_t m_id;
|
||||||
@ -62,7 +68,8 @@ private:
|
|||||||
/** The description of this achievement. */
|
/** The description of this achievement. */
|
||||||
irr::core::stringw m_description;
|
irr::core::stringw m_description;
|
||||||
|
|
||||||
AchievementType m_type;
|
/** Determines how this achievement is checked if it is successful. */
|
||||||
|
AchievementCheckType m_check_type;
|
||||||
|
|
||||||
/** The target values needed to be reached. */
|
/** The target values needed to be reached. */
|
||||||
std::map<std::string, int> m_goal_values;
|
std::map<std::string, int> m_goal_values;
|
||||||
@ -73,7 +80,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
AchievementInfo(const XMLNode * input);
|
AchievementInfo(const XMLNode * input);
|
||||||
virtual ~AchievementInfo() {};
|
virtual ~AchievementInfo() {};
|
||||||
virtual AchievementType getType() const { return m_type; }
|
|
||||||
virtual irr::core::stringw toString() const;
|
virtual irr::core::stringw toString() const;
|
||||||
virtual bool checkCompletion(Achievement * achievement) const;
|
virtual bool checkCompletion(Achievement * achievement) const;
|
||||||
|
|
||||||
@ -88,6 +95,10 @@ public:
|
|||||||
irr::core::stringw getTitle() const { return m_title; }
|
irr::core::stringw getTitle() const { return m_title; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
bool needsResetAfterRace() const { return m_reset_after_race; }
|
bool needsResetAfterRace() const { return m_reset_after_race; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
/** Returns the check type for this achievement. */
|
||||||
|
AchievementCheckType getCheckType() const { return m_check_type; }
|
||||||
|
|
||||||
}; // class AchievementInfo
|
}; // class AchievementInfo
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +84,6 @@ AchievementsStatus*
|
|||||||
for (it = m_achievements_info.begin();
|
for (it = m_achievements_info.begin();
|
||||||
it != m_achievements_info.end(); ++it)
|
it != m_achievements_info.end(); ++it)
|
||||||
{
|
{
|
||||||
AchievementInfo::AchievementType achievement_type = it->second->getType();
|
|
||||||
Achievement * achievement;
|
Achievement * achievement;
|
||||||
achievement = new Achievement(it->second);
|
achievement = new Achievement(it->second);
|
||||||
status->add(achievement);
|
status->add(achievement);
|
||||||
|
@ -262,23 +262,25 @@ void Physics::update(float dt)
|
|||||||
Flyable *f = p->getUserPointer(0)->getPointerFlyable();
|
Flyable *f = p->getUserPointer(0)->getPointerFlyable();
|
||||||
f->hit(target_kart);
|
f->hit(target_kart);
|
||||||
|
|
||||||
// Implement strike achievement
|
// Check for achievements
|
||||||
if (type == PowerupManager::POWERUP_BOWLING)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId());
|
AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId());
|
||||||
PlayerController *c = dynamic_cast<PlayerController*>(kart->getController());
|
PlayerController *c = dynamic_cast<PlayerController*>(kart->getController());
|
||||||
|
|
||||||
// Check that it's not a kart hitting itself (this can
|
// Check that it's not a kart hitting itself (this can
|
||||||
// happen at the time the ball is shot - release too close
|
// happen at the time a flyable is shot - release too close
|
||||||
// to the kart, and it's the current player. At this stage
|
// to the kart, and it's the current player. At this stage
|
||||||
// only the current player can get achievements.
|
// only the current player can get achievements.
|
||||||
if (target_kart != kart && c &&
|
if (target_kart != kart && c &&
|
||||||
c->getPlayer()->getConstProfile() == PlayerManager::get()->getCurrentPlayer())
|
c->getPlayer()->getConstProfile() == PlayerManager::get()->getCurrentPlayer())
|
||||||
|
{
|
||||||
|
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_ARCH_ENEMY,
|
||||||
|
target_kart->getIdent(), 1);
|
||||||
|
if (type == PowerupManager::POWERUP_BOWLING)
|
||||||
{
|
{
|
||||||
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_STRIKE,
|
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_STRIKE,
|
||||||
"ball", 1);
|
"ball", 1);
|
||||||
} // if target_kart != kart && is a player kart and is current player
|
|
||||||
} // is bowling ball
|
} // is bowling ball
|
||||||
|
} // if target_kart != kart && is a player kart and is current player
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user