diff --git a/data/achievements.xml b/data/achievements.xml
index ee28b1fd1..b179d6314 100644
--- a/data/achievements.xml
+++ b/data/achievements.xml
@@ -1,6 +1,7 @@
-
+
@@ -12,8 +13,13 @@
-
+
+
+
+
diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp
index 2fd6e282c..e7d9a61aa 100644
--- a/src/achievements/achievement.cpp
+++ b/src/achievements/achievement.cpp
@@ -117,9 +117,23 @@ irr::core::stringw Achievement::getProgressAsString()
{
int progress = 0;
std::map::const_iterator iter;
- for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter)
+ switch (m_achievement_info->getCheckType())
{
- progress += iter->second;
+ 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
@@ -132,12 +146,10 @@ irr::core::stringw Achievement::getProgressAsString()
void Achievement::increase(const std::string & key, int increase)
{
if (m_progress_map.find(key) != m_progress_map.end())
- {
m_progress_map[key] += increase;
- check();
- }
else
m_progress_map[key] = increase;
+ check();
} // increase
// ----------------------------------------------------------------------------
diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp
index f9b1864fd..23ad270fc 100644
--- a/src/achievements/achievement.hpp
+++ b/src/achievements/achievement.hpp
@@ -82,6 +82,10 @@ public:
// ------------------------------------------------------------------------
/** Returns if this achievement has been fulfilled. */
bool isAchieved() const { return m_achieved; }
-
+ // ------------------------------------------------------------------------
+ const std::map& getProgress() const
+ {
+ return m_progress_map;
+ } // getProgress
}; // class Achievement
#endif
\ No newline at end of file
diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp
index 7be5df630..26f604c1d 100644
--- a/src/achievements/achievement_info.cpp
+++ b/src/achievements/achievement_info.cpp
@@ -26,7 +26,9 @@
#include
#include
-
+/** The constructor reads the dat from the xml information.
+ * \param input XML node for this achievement info.
+ */
AchievementInfo::AchievementInfo(const XMLNode * input)
{
m_reset_after_race = false;
@@ -47,6 +49,17 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
}
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
for (unsigned int n = 0; n < input->getNumNodes(); n++)
{
@@ -57,18 +70,41 @@ AchievementInfo::AchievementInfo(const XMLNode * input)
m_goal_values[key] = goal;
}
if (m_goal_values.size() != input->getNumNodes())
- Log::error("MapAchievementInfo",
+ Log::fatal("AchievementInfo",
"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
// ----------------------------------------------------------------------------
+/** 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
{
int count = 0;
std::map::const_iterator iter;
- for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
+ switch (m_check_type)
{
- count += iter->second;
+ 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 tpye %d.",
+ m_check_type);
}
return StringUtils::toWString(count);
@@ -78,11 +114,34 @@ irr::core::stringw AchievementInfo::toString() const
bool AchievementInfo::checkCompletion(Achievement * achievement) const
{
std::map::const_iterator iter;
- for ( iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++ )
+
+ switch (m_check_type)
{
- if(achievement->getValue(iter->first) < iter->second)
- return false;
+ case AC_ALL_AT_LEAST:
+ for (iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++)
+ {
+ if (achievement->getValue(iter->first) < iter->second)
+ return false;
+ }
+ return true;
+ case AC_ONE_AT_LEAST:
+ {
+ // Test all progress values the kart has.
+ const std::map &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 true;
+ default:
+ Log::fatal("AchievementInfo", "Missing check for tpye %d.",
+ m_check_type);
+ } // switch
+
+ // Avoid compiler warning
+ return false;
}
// ----------------------------------------------------------------------------
diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp
index 559349857..80b361275 100644
--- a/src/achievements/achievement_info.hpp
+++ b/src/achievements/achievement_info.hpp
@@ -42,16 +42,22 @@ class AchievementInfo
{
public:
/** Some handy names for the various achievements. */
- enum { ACHIEVE_COLUMBUS = 1,
- ACHIEVE_FIRST = ACHIEVE_COLUMBUS,
- ACHIEVE_STRIKE = 2,
- ACHIEVE_LAST = ACHIEVE_STRIKE
+ enum { ACHIEVE_COLUMBUS = 1,
+ ACHIEVE_FIRST = ACHIEVE_COLUMBUS,
+ ACHIEVE_STRIKE = 2,
+ ACHIEVE_ARCH_ENEMY = 3,
+ ACHIEVE_LAST = ACHIEVE_ARCH_ENEMY
};
- /** Achievement types:
- * SINGLE_AT_LEAST: a single value, which must at least be the
- * goal value.
+ /** 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 AchievementType { AT_SINGLE_AT_LEAST};
+ enum AchievementCheckType
+ {
+ AC_ALL_AT_LEAST,
+ AC_ONE_AT_LEAST
+ };
+
private:
/** The id of this Achievement. */
uint32_t m_id;
@@ -62,7 +68,8 @@ private:
/** The description of this achievement. */
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. */
std::map m_goal_values;
@@ -71,9 +78,9 @@ private:
bool m_reset_after_race;
public:
- AchievementInfo (const XMLNode * input);
- virtual ~AchievementInfo () {};
- virtual AchievementType getType() const { return m_type; }
+ AchievementInfo(const XMLNode * input);
+ virtual ~AchievementInfo() {};
+
virtual irr::core::stringw toString() const;
virtual bool checkCompletion(Achievement * achievement) const;
@@ -88,6 +95,10 @@ public:
irr::core::stringw getTitle() const { return m_title; }
// ------------------------------------------------------------------------
bool needsResetAfterRace() const { return m_reset_after_race; }
+ // ------------------------------------------------------------------------
+ /** Returns the check type for this achievement. */
+ AchievementCheckType getCheckType() const { return m_check_type; }
+
}; // class AchievementInfo
diff --git a/src/achievements/achievements_manager.cpp b/src/achievements/achievements_manager.cpp
index 6ff1f2b71..e13af174e 100644
--- a/src/achievements/achievements_manager.cpp
+++ b/src/achievements/achievements_manager.cpp
@@ -84,7 +84,6 @@ AchievementsStatus*
for (it = m_achievements_info.begin();
it != m_achievements_info.end(); ++it)
{
- AchievementInfo::AchievementType achievement_type = it->second->getType();
Achievement * achievement;
achievement = new Achievement(it->second);
status->add(achievement);
diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp
index 52c8b6b25..dbbedd1ca 100644
--- a/src/physics/physics.cpp
+++ b/src/physics/physics.cpp
@@ -262,23 +262,25 @@ void Physics::update(float dt)
Flyable *f = p->getUserPointer(0)->getPointerFlyable();
f->hit(target_kart);
- // Implement strike achievement
- if (type == PowerupManager::POWERUP_BOWLING)
+ // Check for achievements
+ AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId());
+ PlayerController *c = dynamic_cast(kart->getController());
+
+ // Check that it's not a kart hitting itself (this can
+ // happen at the time a flyable is shot - release too close
+ // to the kart, and it's the current player. At this stage
+ // only the current player can get achievements.
+ if (target_kart != kart && c &&
+ c->getPlayer()->getConstProfile() == PlayerManager::get()->getCurrentPlayer())
{
- //
- AbstractKart * kart = World::getWorld()->getKart(f->getOwnerId());
- PlayerController *c = dynamic_cast(kart->getController());
- // Check that it's not a kart hitting itself (this can
- // happen at the time the ball is shot - release too close
- // to the kart, and it's the current player. At this stage
- // only the current player can get achievements.
- if (target_kart != kart && c &&
- 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,
- "ball", 1);
- } // if target_kart != kart && is a player kart and is current player
- } // is bowling ball
+ "ball", 1);
+ } // is bowling ball
+ } // if target_kart != kart && is a player kart and is current player
}
}