diff --git a/src/config/favorite_track_status.cpp b/src/config/favorite_track_status.cpp
new file mode 100644
index 00000000000..f5dedb43310
--- /dev/null
+++ b/src/config/favorite_track_status.cpp
@@ -0,0 +1,129 @@
+//
+// SuperTuxKart - a fun racing game with go-kart
+// Copyright (C) 2012-2015 SuperTuxKart-Team
+//
+// 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 "config/favorite_track_status.hpp"
+
+#include "config/player_manager.hpp"
+#include "io/utf_writer.hpp"
+#include "io/xml_node.hpp"
+#include "utils/string_utils.hpp"
+
+const std::string FavoriteTrackStatus::DEFAULT_FAVORITE_GROUP_NAME = "Favorites";
+
+//------------------------------------------------------------------------------
+FavoriteTrackStatus::FavoriteTrackStatus(const XMLNode* node)
+{
+ std::vector xml_favorite_tracks;
+ std::vector xml_favorite_groups;
+
+ if (node)
+ {
+ node->getNodes("track", xml_favorite_tracks);
+ node->getNodes("group", xml_favorite_groups);
+ }
+ for (unsigned int i = 0; i < xml_favorite_tracks.size(); i++)
+ {
+ std::string temp_string;
+ xml_favorite_tracks[i]->get("ident", &temp_string);
+ m_favorite_tracks[DEFAULT_FAVORITE_GROUP_NAME].insert(temp_string);
+ }
+ for (unsigned int i = 0; i < xml_favorite_groups.size(); i++)
+ {
+ std::string temp_group_string;
+ std::vector temp_group;
+
+ xml_favorite_groups[i]->get("name", &temp_group_string);
+ xml_favorite_groups[i]->getNodes("track", temp_group);
+
+ for (unsigned int j = 0; j < temp_group.size(); j++)
+ {
+ std::string temp_string;
+ temp_group[j]->get("ident", &temp_string);
+ m_favorite_tracks[temp_group_string].insert(temp_string);
+ }
+ }
+} // FavoriteTrackStatus
+
+//------------------------------------------------------------------------------
+FavoriteTrackStatus::~FavoriteTrackStatus()
+{
+
+} // ~FavoriteTrackStatus
+
+//------------------------------------------------------------------------------
+/** Adds a new favorite track to this player profile and to the group
+ * of favorite tracks of the Track Manager.
+ * To be used only if this player profile is the current player.
+ */
+bool FavoriteTrackStatus::isFavoriteTrack(std::string ident)
+{
+ return m_favorite_tracks[DEFAULT_FAVORITE_GROUP_NAME].find(ident)
+ != m_favorite_tracks[DEFAULT_FAVORITE_GROUP_NAME].end();
+} // addFavoriteTrack
+
+//------------------------------------------------------------------------------
+/** Adds a new favorite track to this player profile and to the group
+ * of favorite tracks of the Track Manager.
+ */
+void FavoriteTrackStatus::addFavoriteTrack(std::string ident, std::string group)
+{
+ m_favorite_tracks[group].insert(ident);
+} // addFavoriteTrack
+
+//------------------------------------------------------------------------------
+/** Removes a favorite track from this player profile and from the group
+ * of favorite tracks of the Track Manager.
+ */
+void FavoriteTrackStatus::removeFavoriteTrack(std::string ident, std::string group)
+{
+ if (m_favorite_tracks[group].find(ident) != m_favorite_tracks[group].end())
+ {
+ m_favorite_tracks[group].erase(ident);
+ }
+} // removeFavoriteTrack
+
+//------------------------------------------------------------------------------
+/** Writes the data for this player to the specified UTFWriter.
+ * \param out The utf writer to write the data to.
+ */
+void FavoriteTrackStatus::save(UTFWriter &out)
+{
+ out << " \n";
+ for (auto it_group = m_favorite_tracks.begin(); it_group != m_favorite_tracks.end(); it_group++)
+ {
+ std::string group_name = it_group->first;
+
+ if (group_name == DEFAULT_FAVORITE_GROUP_NAME)
+ {
+ for (auto it_track = it_group->second.begin(); it_track != it_group->second.end(); it_track++)
+ {
+ out << " \n";
+ }
+ }
+ else
+ {
+ out << " \n";
+ for (auto it_track = it_group->second.begin(); it_track != it_group->second.end(); it_track++)
+ {
+ out << " \n";
+ }
+ out << " \n";
+ }
+ }
+ out << " \n";
+} // save
diff --git a/src/config/favorite_track_status.hpp b/src/config/favorite_track_status.hpp
new file mode 100644
index 00000000000..6defd245dcc
--- /dev/null
+++ b/src/config/favorite_track_status.hpp
@@ -0,0 +1,74 @@
+//
+// SuperTuxKart - a fun racing game with go-kart
+// Copyright (C) 2024 SuperTuxKart-Team
+//
+// 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_FAVORITE_TRACK_STATUS_HPP
+#define HEADER_FAVORITE_TRACK_STATUS_HPP
+
+#include "utils/leak_check.hpp"
+
+#include
+
+#include
+#include
+#include
+
+using namespace irr;
+
+class TrackManager;
+class UTFWriter;
+class XMLNode;
+
+/** Class for managing player profiles (name, usage frequency,
+ * etc.). All PlayerProfiles are managed by the PlayerManager.
+ * A PlayerProfile keeps track of the story mode progress using an instance
+ * of StoryModeStatus, and achievements with AchievementsStatus. All data
+ * is saved in the players.xml file.
+ * This class also defines the interface for handling online data. All of
+ * the online handling is done in the derived class OnlinePlayerProfile,
+ * where the interface is fully implemented.
+ * \ingroup config
+ */
+class FavoriteTrackStatus
+{
+private:
+ LEAK_CHECK()
+
+ /** unordered_map > .*/
+ std::unordered_map > m_favorite_tracks;
+
+public:
+ friend class TrackManager;
+
+ static const std::string DEFAULT_FAVORITE_GROUP_NAME;
+
+ FavoriteTrackStatus(const XMLNode *node);
+
+ virtual ~FavoriteTrackStatus();
+
+ void save(UTFWriter &out);
+
+ bool isFavoriteTrack(std::string ident);
+
+ void addFavoriteTrack(std::string ident, std::string group = DEFAULT_FAVORITE_GROUP_NAME);
+
+ void removeFavoriteTrack(std::string ident, std::string group = DEFAULT_FAVORITE_GROUP_NAME);
+}; // class PlayerProfile
+
+#endif
+
+/*EOF*/
diff --git a/src/config/player_manager.cpp b/src/config/player_manager.cpp
index 55e061b012e..f3637e49fde 100644
--- a/src/config/player_manager.cpp
+++ b/src/config/player_manager.cpp
@@ -241,10 +241,6 @@ void PlayerManager::initRemainingData()
// Sort player by frequency
m_all_players.insertionSort(/*start*/0, /*desc*/true);
-
- // Load favorite tracks for the current player
- if (m_current_player)
- m_current_player->setFavoriteTracks();
} // initRemainingData
// ----------------------------------------------------------------------------
@@ -486,7 +482,6 @@ void PlayerManager::setCurrentPlayer(PlayerProfile *player)
if(m_current_player)
{
m_current_player->computeActive();
- m_current_player->setFavoriteTracks();
}
if (player_has_changed)
diff --git a/src/config/player_profile.cpp b/src/config/player_profile.cpp
index 729450657e7..414338a21ac 100644
--- a/src/config/player_profile.cpp
+++ b/src/config/player_profile.cpp
@@ -79,7 +79,7 @@ PlayerProfile::PlayerProfile(const XMLNode* node)
m_remember_password = false;
m_story_mode_status = NULL;
m_achievements_status = NULL;
- m_favorite_tracks.clear();
+ m_favorite_track_status = NULL;
m_default_kart_color = 0.0f;
m_icon_filename = "";
@@ -106,6 +106,7 @@ PlayerProfile::~PlayerProfile()
{
delete m_story_mode_status;
delete m_achievements_status;
+ delete m_favorite_track_status;
#ifdef DEBUG
m_magic_number = 0xDEADBEEF;
#endif
@@ -118,29 +119,20 @@ PlayerProfile::~PlayerProfile()
*/
void PlayerProfile::loadRemainingData(const XMLNode *node)
{
+ assert(m_story_mode_status == NULL);
const XMLNode *xml_story_mode = node->getNode("story-mode");
- m_story_mode_status =
- unlock_manager->createStoryModeStatus(xml_story_mode);
+ m_story_mode_status = unlock_manager->createStoryModeStatus(xml_story_mode);
+
+ assert(m_achievements_status == NULL);
const XMLNode *xml_achievements = node->getNode("achievements");
m_achievements_status = AchievementsManager::get()
- ->createAchievementsStatus(xml_achievements, m_unique_id == 1);
+ ->createAchievementsStatus(xml_achievements, m_unique_id == 1);
// We first load the list of all favorite tracks
// Some favorites may correspond to uninstalled addons, so we do not sanitize the strings
- // TODO : handle Arena and Soccer fields
+ assert(m_favorite_track_statuss == NULL);
const XMLNode *xml_favorites = node->getNode("favorites");
- std::vector xml_favorite_tracks;
- xml_favorites->getNodes("track", xml_favorite_tracks);
- for (unsigned int i = 0; i < xml_favorite_tracks.size(); i++)
- {
- std::string temp_string;
- xml_favorite_tracks[i]->get("ident", &temp_string);
- m_favorite_tracks.push_back(temp_string);
- }
- // Deduplicate the list just in case.
- std::sort(m_favorite_tracks.begin(), m_favorite_tracks.end());
- auto it = std::unique(m_favorite_tracks.begin(), m_favorite_tracks.end());
- m_favorite_tracks.erase(it, m_favorite_tracks.end());
+ m_favorite_track_status = new FavoriteTrackStatus(xml_favorites);
// Fix up any potentially missing icons.
addIcon();
@@ -155,49 +147,10 @@ void PlayerProfile::initRemainingData()
m_story_mode_status = unlock_manager->createStoryModeStatus();
m_achievements_status =
AchievementsManager::get()->createAchievementsStatus();
+ m_favorite_track_status = new FavoriteTrackStatus(NULL);
addIcon();
} // initRemainingData
-//------------------------------------------------------------------------------
-/** Update the group of favorite tracks handled by the Track Manager.
- * To be used only if this player profile is the current player.
- */
-void PlayerProfile::setFavoriteTracks()
-{
- // Update the group data from the Track Manager
- track_manager->clearFavoriteTracks();
- for (unsigned int i = 0; i < m_favorite_tracks.size(); i++)
- {
- track_manager->addFavoriteTrack(m_favorite_tracks[i]);
- }
-} // setFavoriteTracks
-
-//------------------------------------------------------------------------------
-/** Adds a new favorite track to this player profile and to the group
- * of favorite tracks of the Track Manager.
- * To be used only if this player profile is the current player.
- */
-void PlayerProfile::addFavoriteTrack(std::string ident)
-{
- m_favorite_tracks.push_back(ident);
- track_manager->addFavoriteTrack(ident);
-} // addFavoriteTrack
-
-//------------------------------------------------------------------------------
-/** Removes a favorite track from this player profile and from the group
- * of favorite tracks of the Track Manager.
- * To be used only if this player profile is the current player.
- */
-void PlayerProfile::removeFavoriteTrack(std::string ident)
-{
- auto it = std::find(m_favorite_tracks.begin(), m_favorite_tracks.end(), ident);
- if (it != m_favorite_tracks.end()) // the track to remove has been found
- {
- m_favorite_tracks.erase(it);
- setFavoriteTracks();
- }
-} // removeFavoriteTrack
-
//------------------------------------------------------------------------------
/** Creates an icon for a player if non exist so far. It takes the unique
* player id modulo the number of karts to pick an icon from the karts. It
@@ -282,18 +235,14 @@ void PlayerProfile::save(UTFWriter &out)
if (player != NULL && (getName() == player->getName()))
is_current_player = true;
- if(m_story_mode_status)
+ if (m_story_mode_status)
m_story_mode_status->save(out, is_current_player);
- if(m_achievements_status)
+ if (m_achievements_status)
m_achievements_status->save(out);
-
- out << " \n";
- for (unsigned int i=0; i < m_favorite_tracks.size(); i++)
- {
- out << " \n";
- }
- out << " \n";
+
+ if (m_favorite_track_status)
+ m_favorite_track_status->save(out);
}
out << " \n";
} // save
diff --git a/src/config/player_profile.hpp b/src/config/player_profile.hpp
index 04fcd1f76af..d0ecf5c9811 100644
--- a/src/config/player_profile.hpp
+++ b/src/config/player_profile.hpp
@@ -20,6 +20,7 @@
#define HEADER_PLAYER_PROFILE_HPP
#include "challenges/story_mode_status.hpp"
+#include "config/favorite_track_status.hpp"
#include "network/remote_kart_info.hpp"
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
@@ -114,8 +115,8 @@ class PlayerProfile : public NoCopy
/** The complete achievement data. */
AchievementsStatus *m_achievements_status;
- /** The list of identifiers of favorite tracks .*/
- std::vector m_favorite_tracks;
+ /** The favorite tracks selected by this player. */
+ FavoriteTrackStatus *m_favorite_track_status;
public:
@@ -125,9 +126,6 @@ class PlayerProfile : public NoCopy
void save(UTFWriter &out);
void loadRemainingData(const XMLNode *node);
void initRemainingData();
- void setFavoriteTracks();
- void addFavoriteTrack(std::string ident);
- void removeFavoriteTrack(std::string ident);
void incrementUseFrequency();
int getUseFrequency() const { return m_use_frequency; }
bool operator<(const PlayerProfile &other);
@@ -204,13 +202,6 @@ class PlayerProfile : public NoCopy
return m_story_mode_status->isLocked(feature);
} // isLocked
// ----------------------------------------------------------------------------------------
- /** Returnes if the track is favorite. */
- bool isFavoriteTrack(const std::string &ident) const
- {
- return std::find(m_favorite_tracks.begin(), m_favorite_tracks.end(), ident)
- != m_favorite_tracks.end();
- } // isFavoriteTrack
- // ----------------------------------------------------------------------------------------
/** Returns all active challenges. */
void computeActive() { m_story_mode_status->computeActive(); }
// ----------------------------------------------------------------------------------------
@@ -303,6 +294,23 @@ class PlayerProfile : public NoCopy
// ----------------------------------------------------------------------------------------
StoryModeStatus* getStoryModeStatus() { return m_story_mode_status; }
// ----------------------------------------------------------------------------------------
+ FavoriteTrackStatus* getFavoriteTrackStatus() { return m_favorite_track_status; }
+ // ----------------------------------------------------------------------------------------
+ bool isFavoriteTrack(std::string ident)
+ {
+ return m_favorite_track_status->isFavoriteTrack(ident);
+ } // getNumBestTrophies
+ void addFavoriteTrack(std::string ident, std::string group =
+ FavoriteTrackStatus::DEFAULT_FAVORITE_GROUP_NAME)
+ {
+ m_favorite_track_status->addFavoriteTrack(ident, group);
+ } // getNumBestTrophies
+ void removeFavoriteTrack(std::string ident, std::string group =
+ FavoriteTrackStatus::DEFAULT_FAVORITE_GROUP_NAME)
+ {
+ m_favorite_track_status->removeFavoriteTrack(ident, group);
+ } // getNumBestTrophies
+ // ----------------------------------------------------------------------------------------
/** If a session was saved, return the id of the saved user. */
int getSavedUserId() const
{
diff --git a/src/states_screens/arenas_screen.cpp b/src/states_screens/arenas_screen.cpp
index fff9cc6e050..ec840e1d8eb 100644
--- a/src/states_screens/arenas_screen.cpp
+++ b/src/states_screens/arenas_screen.cpp
@@ -20,6 +20,7 @@
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/widget.hpp"
+#include "guiengine/widgets/check_box_widget.hpp"
#include "guiengine/widgets/dynamic_ribbon_widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "io/file_manager.hpp"
@@ -57,6 +58,12 @@ void ArenasScreen::loadedFromFile()
void ArenasScreen::beforeAddingWidget()
{
+ // Add user-defined group to track groups
+ track_manager->setFavoriteTrackStatus(PlayerManager::getCurrentPlayer()->getFavoriteTrackStatus());
+
+ CheckBoxWidget* favorite_cb = getWidget("favorite");
+ assert( favorite_cb != NULL );
+ favorite_cb->setState(false);
// Dynamically add tabs
RibbonWidget* tabs = this->getWidget("trackgroups");
@@ -79,6 +86,8 @@ void ArenasScreen::beforeAddingWidget()
//I18N: track group name
FOR_GETTEXT_ONLY( _("All") )
//I18N: track group name
+ FOR_GETTEXT_ONLY( _("Favorites") )
+ //I18N: track group name
FOR_GETTEXT_ONLY( _("Standard") )
//I18N: track group name
FOR_GETTEXT_ONLY( _("Add-Ons") )
@@ -204,8 +213,20 @@ void ArenasScreen::eventCallback(Widget* widget, const std::string& name, const
Track* clicked_track = track_manager->getTrack(selection);
if (clicked_track != NULL)
{
- TrackInfoScreen::getInstance()->setTrack(clicked_track);
- TrackInfoScreen::getInstance()->push();
+ if (getWidget("favorite")->getState())
+ {
+ if(PlayerManager::getCurrentPlayer()->isFavoriteTrack(clicked_track->getIdent()))
+ PlayerManager::getCurrentPlayer()->removeFavoriteTrack(clicked_track->getIdent());
+ else
+ PlayerManager::getCurrentPlayer()->addFavoriteTrack(clicked_track->getIdent());
+
+ buildTrackList();
+ }
+ else
+ {
+ TrackInfoScreen::getInstance()->setTrack(clicked_track);
+ TrackInfoScreen::getInstance()->push();
+ }
} // clickedTrack != NULL
} // if random_track
@@ -225,6 +246,9 @@ void ArenasScreen::eventCallback(Widget* widget, const std::string& name, const
void ArenasScreen::buildTrackList()
{
+ // Add user-defined group to track groups
+ track_manager->setFavoriteTrackStatus(PlayerManager::getCurrentPlayer()->getFavoriteTrackStatus());
+
DynamicRibbonWidget* w = this->getWidget("tracks");
assert( w != NULL );
@@ -237,6 +261,7 @@ void ArenasScreen::buildTrackList()
bool soccer_mode = RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_SOCCER;
bool arenas_have_navmesh = false;
+ PtrVector