diff --git a/data/achievements.xml b/data/achievements.xml index f0d8b526b..edc8c7345 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -1,8 +1,8 @@ - - + + diff --git a/sources.cmake b/sources.cmake index 1936ba29e..21c71bf08 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,7 +1,9 @@ # Generated by ./update_file_list.sh. Do not edit this file manually. set(STK_SOURCES src/achievements/achievement.cpp +src/achievements/achievement_info.cpp src/achievements/achievements_manager.cpp +src/achievements/achievements_slot.cpp src/addons/addon.cpp src/addons/addons_manager.cpp src/addons/inetwork_http.cpp @@ -296,7 +298,9 @@ src/utils/vec3.cpp ) set(STK_HEADERS src/achievements/achievement.hpp +src/achievements/achievement_info.hpp src/achievements/achievements_manager.hpp +src/achievements/achievements_slot.hpp src/addons/addon.hpp src/addons/addons_manager.hpp src/addons/dummy_network_http.hpp diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp index eee4d6a21..e23680e07 100644 --- a/src/achievements/achievement.cpp +++ b/src/achievements/achievement.cpp @@ -29,9 +29,9 @@ #include // ============================================================================ -Achievement::Achievement(const XMLNode * input) +Achievement::Achievement(AchievementInfo * info) { - input->get("id", &m_id); + m_id = info->getID(); m_achieved = false; } @@ -42,9 +42,12 @@ Achievement::~Achievement() } // ============================================================================ -void Achievement::onAchieving() +void Achievement::check() { - if(!m_achieved) + if(m_achieved) + return; + + if(m_achievement_info->checkCompletion(this)) { //show achievement //send to server @@ -53,38 +56,77 @@ void Achievement::onAchieving() } // ============================================================================ -SingleAchievement::SingleAchievement(const XMLNode * input) - : Achievement(input) +SingleAchievement::SingleAchievement(AchievementInfo * info) + : Achievement(info) { } // ============================================================================ -void SingleAchievement::check() +void SingleAchievement::load(XMLNode * input) { - if(m_achieved) - return; - if(m_progress >= m_goal) - onAchieving(); -} - - -// ============================================================================ -MapAchievement::MapAchievement(const XMLNode * input) - : Achievement(input) -{ -} - -// ============================================================================ -void MapAchievement::check() -{ - if(m_achieved) - return; - - ProgressMap::iterator iter; - for (iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter) + std::string achieved(""); + input->get("achieved", &achieved); + if(achieved == "true") { - if (!iter->second) - return; + m_achieved = true; + return; } - onAchieving(); + input->get("value", &m_progress); } +// ============================================================================ +void SingleAchievement::save(std::ofstream & out) +{ + out << "\n"; +} // save + +// ============================================================================ +MapAchievement::MapAchievement(AchievementInfo * info) + : Achievement(info) +{ +} + +// ============================================================================ +void MapAchievement::load(XMLNode * input) +{ + std::string achieved(""); + input->get("achieved", &achieved); + if(achieved == "true") + { + m_achieved = true; + return; + } + std::vector xml_entries; + input->getNodes("entry", xml_entries); + for (unsigned int n=0; n < xml_entries.size(); n++) + { + std::string key(""); + xml_entries[n]->get("key", &key); + int value(0); + xml_entries[n]->get("value", &value); + m_progress_map[key] = value; + } +} + +// ============================================================================ +void MapAchievement::save(std::ofstream & out) +{ + out << "\n"; + std::map::iterator iter; + for ( iter = m_progress_map.begin(); iter != m_progress_map.end(); ++iter ) { + out << " first.c_str() << "\" value=\"" << StringUtils::toString(iter->second) << "\"/>\n"; + } + out << "\n"; +} // save + +// ============================================================================ + +int MapAchievement::getValue(const std::string & key) +{ + if ( m_progress_map.find(key) != m_progress_map.end()) + return m_progress_map[key]; + return 0; +} + diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp index 41f0cd453..72e57e15d 100644 --- a/src/achievements/achievement.hpp +++ b/src/achievements/achievement.hpp @@ -24,6 +24,7 @@ #include #include #include "io/xml_node.hpp" +#include "achievements/achievement_info.hpp" // ============================================================================ @@ -32,43 +33,58 @@ * \brief * \ingroup */ +class AchievementInfo; + class Achievement { protected: - uint32_t m_id; - bool m_achieved; - virtual void check() = 0; - void onAchieving(); + uint32_t m_id; + bool m_achieved; + AchievementInfo * m_achievement_info; + void check (); public: - Achievement (const XMLNode * input); - virtual ~Achievement (); - uint32_t getID() const { return m_id; } + Achievement (AchievementInfo * info); + virtual ~Achievement (); + uint32_t getID () const { return m_id; } + virtual void load (XMLNode * input) = 0; + virtual void save (std::ofstream & out) = 0; + + enum AchievementType + { + AT_SINGLE, + AT_MAP + }; + }; // class Achievement class SingleAchievement : public Achievement { -private: - virtual void check(); - int m_goal; +protected: int m_progress; public: - SingleAchievement (const XMLNode * input); - virtual ~SingleAchievement () {}; -}; // class Achievement + SingleAchievement (AchievementInfo * info); + virtual ~SingleAchievement () {}; + + void load (XMLNode * input); + int getValue () const { return m_progress; } + void save (std::ofstream & out); +}; // class SingleAchievement class MapAchievement : public Achievement { -private: - virtual void check(); - typedef std::map ProgressMap; - ProgressMap m_progress_map; +protected: + std::map m_progress_map; public: - MapAchievement (const XMLNode * input); - virtual ~MapAchievement () {}; -}; // class Achievement + MapAchievement (AchievementInfo * info); + virtual ~MapAchievement () {}; + + void load (XMLNode * input); + int getValue (const std::string & key); + void save (std::ofstream & out); +}; // class MapAchievement #endif diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp new file mode 100644 index 000000000..95f6ee253 --- /dev/null +++ b/src/achievements/achievement_info.cpp @@ -0,0 +1,87 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// 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_info.hpp" + +#include "utils/log.hpp" +#include "utils/translation.hpp" +#include "io/xml_writer.hpp" + + +#include +#include +#include + +// ============================================================================ +AchievementInfo::AchievementInfo(const XMLNode * input) +{ + input->get("id", &m_id); + input->get("title", &m_title); + input->get("description", &m_description); + + std::string reset_after_race(""); + input->get("reset_after_race", &reset_after_race); + m_reset_after_race = reset_after_race == "true"; + +} + +// ============================================================================ +SingleAchievementInfo::SingleAchievementInfo(const XMLNode * input) + : AchievementInfo(input) +{ + input->get("goal", &m_goal_value); +} + +// ============================================================================ +bool SingleAchievementInfo::checkCompletion(Achievement * achievement) const +{ + SingleAchievement * single_achievement = (SingleAchievement *) achievement; + if(single_achievement->getValue() >= m_goal_value) + return true; + return false; +} + +// ============================================================================ +MapAchievementInfo::MapAchievementInfo(const XMLNode * input) + : AchievementInfo(input) +{ + std::vector xml_entries; + input->getNodes("entry", xml_entries); + for (unsigned int n=0; n < xml_entries.size(); n++) + { + std::string key(""); + xml_entries[n]->get("key", &key); + int goal(0); + xml_entries[n]->get("goal", &goal); + m_goal_values[key] = goal; + } + if(m_goal_values.size() != xml_entries.size()) + Log::error("MapAchievementInfo","Duplicate keys for the entries of a MapAchievement found."); +} + +// ============================================================================ +bool MapAchievementInfo::checkCompletion(Achievement * achievement) const +{ + MapAchievement * map_achievement = (MapAchievement *) achievement; + std::map::iterator iter; + for ( iter = m_goal_values.begin(); iter != m_goal_values.end(); iter++ ) { + if(map_achievement->getValue(iter->first) < iter->second) + return false; + } + return true; +} diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp new file mode 100644 index 000000000..7ad522be1 --- /dev/null +++ b/src/achievements/achievement_info.hpp @@ -0,0 +1,85 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_ACHIEVEMENT_INFO_HPP +#define HEADER_ACHIEVEMENT_INFO_HPP + +#include "utils/types.hpp" + +#include +#include +#include "io/xml_node.hpp" +#include "achievements/achievement.hpp" + + +// ============================================================================ + +class Achievement; + +/** + * \brief + * \ingroup + */ +class AchievementInfo +{ +protected: + uint32_t m_id; + irr::core::stringw m_title; + irr::core::stringw m_description; + bool m_reset_after_race; + +public: + AchievementInfo (const XMLNode * input); + virtual ~AchievementInfo () {}; + uint32_t getID () const { return m_id; } + irr::core::stringw getDescription () const { return m_description; } + virtual Achievement::AchievementType getType () = 0; + virtual bool checkCompletion (Achievement * achievement) const = 0; +}; // class AchievementInfo + +class SingleAchievementInfo : public AchievementInfo +{ +protected: + int m_goal_value; + +public: + SingleAchievementInfo (const XMLNode * input); + virtual ~SingleAchievementInfo () {}; + int getGoalValue () const { return m_goal_value; } + virtual bool checkCompletion (Achievement * achievement) const; + + virtual Achievement::AchievementType getType() { return Achievement::AT_SINGLE; }; +}; // class SingleAchievementInfo + +class MapAchievementInfo : public AchievementInfo +{ +protected: + std::map m_goal_values; + +public: + MapAchievementInfo (const XMLNode * input); + virtual ~MapAchievementInfo () {}; + int getGoalValue (const std::string & key) { return m_goal_values[key];} + virtual bool checkCompletion (Achievement * achievement) const; + + virtual Achievement::AchievementType getType() { return Achievement::AT_MAP; }; +}; // class MapAchievementInfo + +#endif + +/*EOF*/ diff --git a/src/achievements/achievements_manager.cpp b/src/achievements/achievements_manager.cpp index 51264928c..82ff0b368 100644 --- a/src/achievements/achievements_manager.cpp +++ b/src/achievements/achievements_manager.cpp @@ -18,11 +18,12 @@ #include "achievements/achievements_manager.hpp" -#include "achievements/achievement.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" #include "io/file_manager.hpp" #include "io/xml_writer.hpp" +#include "config/player.hpp" +#include "config/user_config.hpp" #include #include @@ -46,7 +47,8 @@ void AchievementsManager::deallocate() // ============================================================================ AchievementsManager::AchievementsManager() { - parse(); + parseDataFile(); + parseConfigFile(); } // ============================================================================ @@ -55,15 +57,110 @@ AchievementsManager::~AchievementsManager() } // ============================================================================ -void AchievementsManager::parse() +void AchievementsManager::parseDataFile() { - + const std::string file_name = file_manager->getDataFile("achievements.xml"); + const XMLNode *root = file_manager->createXMLTree(file_name); + unsigned int num_nodes = root->getNumNodes(); + for(unsigned int i = 0; i < num_nodes; i++) + { + const XMLNode *node = root->getNode(i); + std::string type(""); + node->get("type", &type); + AchievementInfo * achievement_info; + if(type == "single") + { + achievement_info = new SingleAchievementInfo(node); + } + else if(type == "map") + { + achievement_info = new MapAchievementInfo(node); + } + else + { + Log::error("AchievementsManager::parseAchievements","Non-existent achievement type. Skipping - definitely results in unwanted behaviour."); + continue; + } + m_achievements_info.push_back(achievement_info); + } + if(num_nodes != m_achievements_info.size()) + Log::error("AchievementsManager::parseAchievements","Multiple achievements with the same id!"); } + + // ============================================================================ -void AchievementsManager::load() +void AchievementsManager::parseConfigFile() { + const std::string filename=file_manager->getConfigFile("achievements.xml"); + XMLNode* root = file_manager->createXMLTree(filename); + if(!root || root->getName() != "achievements") + { + Log::info("AchievementsManager", "Achievements file '%s' will be created.", + filename.c_str()); + createSlotsIfNeeded(); + save(); + + if (root) delete root; + return; + } + + std::vector xml_slots; + root->getNodes("slot", xml_slots); + for (unsigned int n=0; n < xml_slots.size(); n++) + { + AchievementsSlot * slot = new AchievementsSlot(xml_slots[n]); + if(!slot->isValid()) + { + Log::warn("AchievementsManager", "Found game slot with faulty or missing information. Discarding it."); + delete slot; + continue; + } + m_slots.push_back(slot); + } + + bool something_changed = createSlotsIfNeeded(); + if (something_changed) save(); + + delete root; +} // load + + + +//----------------------------------------------------------------------------- +/** Creates a slot for players that don't have one yet + * \return true if any were created + */ +bool AchievementsManager::createSlotsIfNeeded() +{ + bool something_changed = false; + + // make sure all players have at least one game slot associated + PtrVector& players = UserConfigParams::m_all_players; + for (int n=0; nisOnline() && m_slots[i]->getID() == players[n].getUniqueID()) + { + exists = true; + break; + } + } + + if (!exists) + { + AchievementsSlot* slot = new AchievementsSlot(players[n].getUniqueID(), false); + m_slots.push_back(slot); + something_changed = true; + } + } + + return something_changed; +} // UnlockManager::createSlotsIfNeeded + -} // ============================================================================ void AchievementsManager::save() { @@ -84,54 +181,10 @@ void AchievementsManager::save() for (unsigned int i = 0; i < m_slots.size(); i++) { - m_slots[i].save(); + m_slots[i]->save(achievements_file); } achievements_file << "\n\n"; achievements_file.close(); } -// ============================================================================ -// ============================================================================ - -void AchievementsManager::AchievementsSlot::parse() -{ - const std::string file_name = file_manager->getDataFile("achievements.xml"); - const XMLNode *root = file_manager->createXMLTree(file_name); - unsigned int num_nodes = root->getNumNodes(); - for(unsigned int i = 0; i < num_nodes; i++) - { - const XMLNode *node = root->getNode(i); - std::string type(""); - node->get("type", &type); - Achievement * achievement; - if(type == "single") - { - achievement = new SingleAchievement(node); - } - else if(type == "map") - { - achievement = new MapAchievement(node); - } - else - { - Log::error("AchievementsManager::parseAchievements","Non-existent achievement type. Skipping - definitely results in unwanted behaviour."); - continue; - } - m_achievements[achievement->getID()] = achievement; - } - if(num_nodes != m_achievements.size()) - Log::error("AchievementsManager::parseAchievements","Multiple achievements with the same id!"); -} // parseAchievements - -// ============================================================================ -void AchievementsManager::AchievementsSlot::load() -{ -} - - -// ============================================================================ -void AchievementsManager::AchievementsSlot::save() -{ - -} diff --git a/src/achievements/achievements_manager.hpp b/src/achievements/achievements_manager.hpp index cf25ebb36..12940f400 100644 --- a/src/achievements/achievements_manager.hpp +++ b/src/achievements/achievements_manager.hpp @@ -20,7 +20,8 @@ #define HEADER_ACHIEVEMENTS_MANAGER_HPP #include "utils/types.hpp" -#include "achievements/achievement.hpp" +#include "achievements/achievement_info.hpp" +#include "achievements/achievements_slot.hpp" #include @@ -38,29 +39,22 @@ class AchievementsManager { private : - - class AchievementsSlot - { - std::map m_achievements; - public : - void parse(); - void load(); - void save(); - }; - std::vector m_slots; + std::vector m_slots; + std::vector m_achievements_info; AchievementsManager (); ~AchievementsManager (); - - + bool createSlotsIfNeeded(); public: /**Singleton */ static AchievementsManager * get(); static void deallocate(); - void parse(); + const std::vector & getAllInfo() const { return m_achievements_info;}; + + void parseDataFile(); + void parseConfigFile(); void save(); - void load(); }; // class AchievementsManager #endif diff --git a/src/achievements/achievements_slot.cpp b/src/achievements/achievements_slot.cpp new file mode 100644 index 000000000..577c0e400 --- /dev/null +++ b/src/achievements/achievements_slot.cpp @@ -0,0 +1,115 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// 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/achievements_slot.hpp" + +#include "achievements/achievement_info.hpp" +#include "achievements/achievements_manager.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" +#include "io/xml_writer.hpp" + +#include +#include +#include +// ============================================================================ +AchievementsSlot::AchievementsSlot(const XMLNode * input) +{ + int fetched_user_id = input->get("user_id", &m_id); + std::string online; + int fetched_online = input->get("online", &online); + if(!fetched_user_id || !fetched_online || !(online == "true" || online == "false")) + { + m_valid = false; + } + m_valid = true; + m_online = online == "true"; + + createFreshSlot(); + + std::vector xml_achievements; + input->getNodes("achievement", xml_achievements); + for( unsigned int i=0; iget("id", &achievement_id); + Achievement * achievement = findAchievement(achievement_id); + if(achievement == NULL) + { + Log::warn("AchievementsSlot", "Found saved achievement data for a non-existent achievement. Discarding."); + continue; + } + achievement->load(xml_achievements[i]); + } +} + +// ============================================================================ +AchievementsSlot::AchievementsSlot(std::string id, bool online) +{ + m_valid = true; + m_online = online; + m_id = id; + + createFreshSlot(); +} + +// ============================================================================ +void AchievementsSlot::createFreshSlot() +{ + m_achievements.clear(); + std::vector all_info = AchievementsManager::get()->getAllInfo(); + for( unsigned int i=0; i < all_info.size(); i++) + { + AchievementInfo * info = all_info[i]; + Achievement::AchievementType achievement_type = info->getType(); + Achievement * achievement; + if(achievement_type == Achievement::AT_SINGLE) + { + achievement = new SingleAchievement(info); + } + else if(achievement_type == Achievement::AT_MAP) + { + achievement = new MapAchievement(info); + } + m_achievements[achievement->getID()] = achievement; + } +} + +// ============================================================================ +void AchievementsSlot::save(std::ofstream & out) +{ + out << " \n"; + std::map::const_iterator i; + for(i = m_achievements.begin(); i != m_achievements.end(); i++) + { + if (i->second != NULL) + i->second->save(out); + } + out << " \n"; +} + +// ============================================================================ +Achievement * AchievementsSlot::findAchievement(uint32_t id) +{ + if ( m_achievements.find(id) != m_achievements.end()) + return m_achievements[id]; + return NULL; +} diff --git a/src/achievements/achievements_slot.hpp b/src/achievements/achievements_slot.hpp new file mode 100644 index 000000000..e5a1b2cc2 --- /dev/null +++ b/src/achievements/achievements_slot.hpp @@ -0,0 +1,52 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_ACHIEVEMENTS_SLOT_HPP +#define HEADER_ACHIEVEMENTS_SLOT_HPP + +#include "utils/types.hpp" +#include "achievements/achievement.hpp" + +#include +#include +#include "io/xml_node.hpp" + + +class AchievementsSlot +{ +private: + std::map m_achievements; + bool m_online; + bool m_valid; + std::string m_id; + + void createFreshSlot(); + +public : + AchievementsSlot(const XMLNode * input); + AchievementsSlot(std::string id, bool online); + bool isValid() const { return m_valid;} + void save(std::ofstream & out); + bool isOnline() const {return m_online;} + const std::string & getID() const {return m_id;} + Achievement * findAchievement(uint32_t id); +}; + +#endif + +/*EOF*/ diff --git a/src/challenges/challenge.cpp b/src/challenges/challenge.cpp index 31fe02036..844e8f981 100644 --- a/src/challenges/challenge.cpp +++ b/src/challenges/challenge.cpp @@ -90,17 +90,11 @@ void Challenge::setSolved(RaceManager::Difficulty d) //----------------------------------------------------------------------------- - -const char* boolstr(bool b) -{ - return (b ? "true" : "false"); -} - void Challenge::save(std::ofstream& writer) { writer << " <" << m_data->getId().c_str() << ">\n" - << " \n" - << " \n" - << " \n" + << " \n" + << " \n" + << " \n" << " getId().c_str() << ">\n"; } // save diff --git a/src/main.cpp b/src/main.cpp index 724cb8708..3dc95f53d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,6 +139,7 @@ #include #include "main_loop.hpp" +#include "achievements/achievements_manager.hpp" #include "addons/addons_manager.hpp" #include "addons/inetwork_http.hpp" #include "addons/news_manager.hpp" diff --git a/src/utils/string_utils.hpp b/src/utils/string_utils.hpp index 73a20cd91..d8c11dddc 100644 --- a/src/utils/string_utils.hpp +++ b/src/utils/string_utils.hpp @@ -413,6 +413,11 @@ namespace StringUtils /** Compute a simple hash of a string */ unsigned int simpleHash(const char* input); + + const char* boolstr(bool b) + { + return (b ? "true" : "false"); + } } // namespace StringUtils #endif