Display the number of goals fullfiled, and if only one goal, its value progress

This commit is contained in:
Alayan 2018-10-06 04:58:02 +02:00 committed by auriamg
parent ff5bb8792e
commit cd3bf122e6
7 changed files with 251 additions and 20 deletions

View File

@ -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;i<progress.children.size();i++)
{
if (recursiveCompletionCheck(progress.children[i], reference.children[i]))
{
completed = true;
break;
}
}
return (completed) ? 1 : 0;
}
else
{
int goals_completed = 0;
for (unsigned int i=0;i<progress.children.size();i++)
{
if (recursiveCompletionCheck(progress.children[i], reference.children[i]))
goals_completed++;
}
return goals_completed;
}
}
else if (progress.children[0].type == "AND" ||
progress.children[0].type == "AND-AT-ONCE" ||
progress.children[0].type == "OR")
{
return computeFullfiledGoals(progress.children[0], reference.children[0]);
}
else
{
return (recursiveCompletionCheck(progress.children[0], reference.children[0])) ? 1 : 0;
}
} // recursiveGoalCount
// ----------------------------------------------------------------------------
/** Returns how much of an achievement has been achieved in the form n/m.
* ONLY applicable for single goal achievements. Returns a string with a single
* space if there are multiple goals.
*/
irr::core::stringw Achievement::getProgressAsString()
{
irr::core::stringw target = getInfo()->progressString();
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.

View File

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

View File

@ -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 <sstream>
@ -147,26 +150,75 @@ void AchievementInfo::copyGoalTree(goalTree &copy, 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)
{
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

View File

@ -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()); }

View File

@ -374,6 +374,63 @@ void AchievementsStatus::sync(const std::vector<uint32_t> & 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<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;
if (m_track_stats[i].track_data[enum_id] >= 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<m_track_stats.size();i++)
{
// TODO : have a generic function to call instead
// 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;
num_tracks++;
}
return num_tracks;
} //getNumAchieveTracks
// ----------------------------------------------------------------------------
/* This function checks over achievements to update their goals
* \param type - the data type triggering the update, used to know
* to what enum the enum_id refers to.

View File

@ -230,6 +230,8 @@ public :
void resetKartHits(int num_karts);
void addKartHit(int kart_id);
void updateAllAchievementsProgress();
int getNumTracksAboveValue(int value, std::string goal_string);
int getNumAchieveTracks();
// ------------------------------------------------------------------------
std::map<uint32_t, Achievement *>& getAllAchievements()
{

View File

@ -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);