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 } }