diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp index 5927776f1..5c2322ffa 100644 --- a/src/achievements/achievement.cpp +++ b/src/achievements/achievement.cpp @@ -19,8 +19,10 @@ #include "achievements/achievement.hpp" + #include "achievements/achievements_manager.hpp" #include "achievements/achievement_info.hpp" +#include "config/player_manager.hpp" #include "guiengine/message_queue.hpp" #include "io/utf_writer.hpp" #include "config/player_manager.hpp" @@ -65,24 +67,133 @@ void Achievement::saveProgress(UTFWriter &out) } // save // ---------------------------------------------------------------------------- -/** Returns how much of an achievement has been achieved in the form n/m. - * 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. +/** Returns how many goals of an achievement have been achieved, + * in the form n/m. */ -irr::core::stringw Achievement::getProgressAsString() +irr::core::stringw Achievement::getGoalProgressAsString() { - //TODO : add a progress computation function. - int progress = 0; - irr::core::stringw target = getInfo()->toString(); + irr::core::stringw target = getInfo()->goalString(); - // For now return N/N in case of an achieved achievement. + // Return N/N in case of an achieved achievement. if (m_achieved) return target + "/" + target; + int fullfiled_goals = computeFullfiledGoals(m_progress_goal_tree, m_achievement_info->m_goal_tree); + + return StringUtils::toWString(fullfiled_goals) + "/" + target; +} // getGoalProgressAsString + +// ---------------------------------------------------------------------------- +int Achievement::computeFullfiledGoals(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference) +{ + + if (progress.children.size() != 1) + { + // This always returns 0 if the achievement has not been completed + if (progress.children[0].type == "OR") + { + bool completed = false; + for (unsigned int i=0;iprogressString(); + irr::core::stringw empty = " ";// See issue #3081 + + if (target == "-1") + return empty; + + // Return N/N in case of an achieved achievement. + if (m_achieved) + return target + "/" + target; + + int progress = computeGoalProgress(m_progress_goal_tree, m_achievement_info->m_goal_tree); + return StringUtils::toWString(progress) + "/" + target; } // getProgressAsString +// ---------------------------------------------------------------------------- +/** Should ONLY be called if the achievement has one goal (a sum counts as one goal). + * Returning an error code with a number is not full-proof because a sum goal can + * legitimately be negative (a counter can be chosen to count against the + * achievement's fullfilment). */ +int Achievement::computeGoalProgress(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference) +{ + if (progress.children.size() != 1) + { + // This should NOT happen + assert(false); + return 0; + } + else if (progress.children[0].type == "AND" || + progress.children[0].type == "AND-AT-ONCE" || + progress.children[0].type == "OR") + { + return computeGoalProgress(progress.children[0], reference.children[0]); + } + + else + { + //TODO : find a more automatic way + if (progress.children[0].type == "race-started-all" || + progress.children[0].type == "race-finished-all" || + progress.children[0].type == "race-won-all" || + progress.children[0].type == "race-finished-reverse-all" || + progress.children[0].type == "race-finished-alone-all" || + progress.children[0].type == "less-laps-all" || + progress.children[0].type == "more-laps-all" || + progress.children[0].type == "twice-laps-all" || + progress.children[0].type == "egg-hunt-started-all" || + progress.children[0].type == "egg-hunt-finished-all") + { + // Compare against the target value (in the reference tree) ! + // Progress is only shown for the current local accuont, so we can use the current achievements status + return PlayerManager::getCurrentAchievementsStatus() + ->getNumTracksAboveValue(reference.children[0].value, reference.children[0].type); + } + else + { + return progress.children[0].value; + } + } +} // computeGoalProgress + // ---------------------------------------------------------------------------- /** Set any leaf of the progress goal tree whose type matches the * goal_string to the value passed as parameter. diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp index 296985211..654334f2b 100644 --- a/src/achievements/achievement.hpp +++ b/src/achievements/achievement.hpp @@ -57,6 +57,9 @@ private: 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); + + int computeFullfiledGoals(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference); + int computeGoalProgress(AchievementInfo::goalTree &progress, AchievementInfo::goalTree &reference); public: Achievement(AchievementInfo * info); @@ -65,6 +68,7 @@ public: virtual void saveProgress(UTFWriter &out); virtual irr::core::stringw getProgressAsString(); + virtual irr::core::stringw getGoalProgressAsString(); uint32_t getID() const { return m_achievement_info->getID(); } AchievementInfo * getInfo() { return m_achievement_info; } diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp index 5ae6f1a91..e6578b37d 100644 --- a/src/achievements/achievement_info.cpp +++ b/src/achievements/achievement_info.cpp @@ -17,8 +17,11 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "achievements/achievement.hpp" #include "achievements/achievement_info.hpp" + +#include "achievements/achievement.hpp" +#include "achievements/achievements_status.hpp" +#include "config/player_manager.hpp" #include "utils/log.hpp" #include @@ -147,26 +150,75 @@ void AchievementInfo::copyGoalTree(goalTree ©, goalTree &model, bool set_val } // copyGoalTree // ---------------------------------------------------------------------------- -/** Returns a string with a numerical value to display the progress of - * 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. +/** Returns a string with the number of goals to fullfil to + * get this achievements. */ -irr::core::stringw AchievementInfo::toString() +irr::core::stringw AchievementInfo::goalString() { return StringUtils::toWString(recursiveGoalCount(m_goal_tree)); -} // toString +} // goalString // ---------------------------------------------------------------------------- int AchievementInfo::recursiveGoalCount(goalTree &parent) { if (parent.children.size() != 1) - return m_goal_tree.children.size(); + { + if (parent.children[0].type == "OR") + return 1; + else + 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 parent.children[0].value; + return 1; } // recursiveGoalCount + +// ---------------------------------------------------------------------------- +/** Returns a string with the target of the goal if the + * achievement has only one goal (a sum counts as one goal). + */ +irr::core::stringw AchievementInfo::progressString() +{ + return StringUtils::toWString(recursiveProgressCount(m_goal_tree)); +} // progressString + +// ---------------------------------------------------------------------------- +int AchievementInfo::recursiveProgressCount(goalTree &parent) +{ + if (parent.children.size() != 1) + { + return -1; // signal that this is invalid. + } + else if (parent.children[0].type == "AND" || + parent.children[0].type == "AND-AT-ONCE" || + parent.children[0].type == "OR") + { + return recursiveGoalCount(parent.children[0]); + } + else + { + //TODO : find a more automatic way + if (parent.children[0].type == "race-started-all" || + parent.children[0].type == "race-finished-all" || + parent.children[0].type == "race-won-all" || + parent.children[0].type == "race-finished-reverse-all" || + parent.children[0].type == "race-finished-alone-all" || + parent.children[0].type == "less-laps-all" || + parent.children[0].type == "more-laps-all" || + parent.children[0].type == "twice-laps-all" || + parent.children[0].type == "egg-hunt-started-all" || + parent.children[0].type == "egg-hunt-finished-all") + { + return PlayerManager::getCurrentAchievementsStatus()->getNumAchieveTracks(); + } + else + { + return parent.children[0].value; + } + } +} // recursiveProgressCount diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp index 09eb3108d..382c3a8e2 100644 --- a/src/achievements/achievement_info.hpp +++ b/src/achievements/achievement_info.hpp @@ -72,6 +72,7 @@ private: void parseGoals(const XMLNode * input, goalTree &parent); int recursiveGoalCount(goalTree &parent); + int recursiveProgressCount(goalTree &parent); protected: friend class Achievement; /** The tree storing all goals */ @@ -81,7 +82,8 @@ public: AchievementInfo(const XMLNode * input); virtual ~AchievementInfo() {}; - virtual irr::core::stringw toString(); + virtual irr::core::stringw goalString(); + virtual irr::core::stringw progressString(); uint32_t getID() const { return m_id; } irr::core::stringw getDescription() const { return _(m_description.c_str()); } diff --git a/src/achievements/achievements_status.cpp b/src/achievements/achievements_status.cpp index d47707fac..68fd79986 100644 --- a/src/achievements/achievements_status.cpp +++ b/src/achievements/achievements_status.cpp @@ -374,6 +374,63 @@ void AchievementsStatus::sync(const std::vector & achieved_ids) } } // sync +// ---------------------------------------------------------------------------- +/* This function returns for how many tracks the requested goal type + * is matched or exceeded. Addons tracks are ignored. + * It returns -1 if the goal type is invalid. + * \param value - the value to match or exceed + * \param goal_string - the identifier of the value to check. */ +int AchievementsStatus::getNumTracksAboveValue(int value, std::string goal_string) +{ + int counter = 0; + int enum_id = -1; + + for (int i=0;i<2*(int)TR_DATA_NUM;i++) + { + if (m_tr_enum_to_xml[i] == goal_string) + enum_id = i%(int)TR_DATA_NUM; + } + + if (enum_id == -1) + { + Log::warn("AchievementsStatus", + "Number of matching tracks requested for a non-existent " + "data value."); + return -1; + } + + for (unsigned int i=0;i= value) + counter++; + } + return counter; +} // getNumTracksAboveValue + +// ---------------------------------------------------------------------------- +/* This function returns the number of tracks valid for by-track achievements. */ +int AchievementsStatus::getNumAchieveTracks() +{ + int num_tracks = 0; + for (unsigned int i=0;i& getAllAchievements() { diff --git a/src/states_screens/online/online_profile_achievements.cpp b/src/states_screens/online/online_profile_achievements.cpp index f35ee73ff..ce4200571 100644 --- a/src/states_screens/online/online_profile_achievements.cpp +++ b/src/states_screens/online/online_profile_achievements.cpp @@ -74,6 +74,7 @@ void BaseOnlineProfileAchievements::beforeAddingWidget() // = NULL) user achievement progress will also be displayed if(!m_visiting_profile || m_visiting_profile->isCurrentUser()) { + m_achievements_list_widget->addColumn( _("Goals"), 1 ); m_achievements_list_widget->addColumn( _("Progress"), 1 ); } } // beforeAddingWidget @@ -109,8 +110,10 @@ void BaseOnlineProfileAchievements::init() if(a->getInfo()->isSecret() && !a->isAchieved()) continue; ListWidget::ListCell title(translations->fribidize(a->getInfo()->getName()), -1, 2); + ListWidget::ListCell goals(a->getGoalProgressAsString(), -1, 1); ListWidget::ListCell progress(a->getProgressAsString(), -1, 1); row.push_back(title); + row.push_back(goals); row.push_back(progress); const std::string id = StringUtils::toString(a->getInfo()->getID()); m_achievements_list_widget->addItem(id, row);