From f136c6fe3614a7520936845ee78d35a2e8d27069 Mon Sep 17 00:00:00 2001 From: Richard Qian Date: Thu, 11 Feb 2021 21:12:43 -0600 Subject: [PATCH] In-game high scores management (#4483) * Add in-game high score selection screens, based on the ghost replay screens Its functionality is basic for now, mainly to let players have a central place to view their high scores Other things to improve: * Allow sorting the high score entries by criteria * Allow deleting high score nodes and possibly entries * Use better icons * The string for the high scores title in the track info screen can now be translated * Refine in-game high score selection screens Includes: * High score info dialog now shows track and setup information * A race can be started with the displayed setup, using the current player and kart * Icon to access the screen now placed between the tutorial and achievements buttons * It is possible to delete a specific high score group or all of the high score groups * Change the order of some columns to make them easier to hide for non-linear modes * The list will now filter out enpty high score groups * Replace bomb icon (as used in the help menu) with the full object version from STK 0.8 It has been edited to remove most of the transparency in the object itself * Implement column clicking in the high score selection screen, and minor GUI fixes Note that high score entry sorting is not yet working properly Includes: * Top right buttons replaced by button bar containing them; fixes unreliable clicking * High score manager has some one-line functions moved into its header file * High scores can be sorted by some criteria * Sorting is done before every time high scores are saved * Fix header define names for the high score info dialog * Fix high score sorting, reorganize its associated code * More refinements to the high score selection screen Includes: * Clearing high scores no longer causes memory leaks * The manual refresh button has been removed, as it has been deemed useless * Remove unused header files for the high scores information dialog header * The high scores box in the track information screen no longer has '=' * Fix pressing escape key in the high score information dialog crashing the game Also remove unused widget variables and unnecessary function overrides * Do not write high scores for races that have 0 laps and/or have no real karts * Allow passing a parameter to prevent high scores from temporarily being written This setting lasts only as long as the game runs; it is useful during track and kart animation testing, where unwanted high score entries should not be written * Force update sources.cmake, as new source files are being added for high scores screens * Fix memory leak and strings Co-authored-by: Benau --- .../gui/dialogs/high_score_info_dialog.stkgui | 57 +++ data/gui/icons/bomb_icon.png | Bin 5902 -> 10589 bytes data/gui/screens/high_score_selection.stkgui | 51 +++ data/gui/screens/highscore_info.stkgui | 68 ++++ data/gui/screens/main_menu.stkgui | 4 +- data/gui/screens/track_info.stkgui | 2 +- sources.cmake | 2 +- src/config/user_config.hpp | 5 +- src/main.cpp | 2 + src/modes/world.cpp | 40 +- src/race/highscore_manager.cpp | 16 +- src/race/highscore_manager.hpp | 30 +- src/race/highscores.cpp | 34 +- src/race/highscores.hpp | 42 ++- .../dialogs/high_score_info_dialog.cpp | 249 +++++++++++++ .../dialogs/high_score_info_dialog.hpp | 59 +++ src/states_screens/high_score_selection.cpp | 345 ++++++++++++++++++ src/states_screens/high_score_selection.hpp | 102 ++++++ src/states_screens/main_menu_screen.cpp | 5 + src/states_screens/track_info_screen.cpp | 1 + 20 files changed, 1074 insertions(+), 40 deletions(-) create mode 100644 data/gui/dialogs/high_score_info_dialog.stkgui create mode 100644 data/gui/screens/high_score_selection.stkgui create mode 100644 data/gui/screens/highscore_info.stkgui create mode 100644 src/states_screens/dialogs/high_score_info_dialog.cpp create mode 100644 src/states_screens/dialogs/high_score_info_dialog.hpp create mode 100644 src/states_screens/high_score_selection.cpp create mode 100644 src/states_screens/high_score_selection.hpp diff --git a/data/gui/dialogs/high_score_info_dialog.stkgui b/data/gui/dialogs/high_score_info_dialog.stkgui new file mode 100644 index 000000000..532f1c22d --- /dev/null +++ b/data/gui/dialogs/high_score_info_dialog.stkgui @@ -0,0 +1,57 @@ + + +
+ +
+
+
+ +
+ + + + + + +
+ +
+
+
+ +
+
+ + +
+ +
+ +
+ + + + + +
+ +
+
+
+
diff --git a/data/gui/icons/bomb_icon.png b/data/gui/icons/bomb_icon.png index 6e7238ea1d0949cef71786b3d95c24caeeca95fc..1aa4d3decfe360aebab9e01329aa6304889fa32f 100644 GIT binary patch literal 10589 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 literal 5902 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 diff --git a/data/gui/screens/high_score_selection.stkgui b/data/gui/screens/high_score_selection.stkgui new file mode 100644 index 000000000..61c7c3691 --- /dev/null +++ b/data/gui/screens/high_score_selection.stkgui @@ -0,0 +1,51 @@ + + +
+ + + +
+ +
+
+ + + + + + + + + + + + + + + +
+
diff --git a/data/gui/screens/highscore_info.stkgui b/data/gui/screens/highscore_info.stkgui new file mode 100644 index 000000000..babfa7fc9 --- /dev/null +++ b/data/gui/screens/highscore_info.stkgui @@ -0,0 +1,68 @@ + + + + +
+
+ + +
+ + +
+ +
+ + +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + + + +
+ + + + + + + + +
+
diff --git a/data/gui/screens/main_menu.stkgui b/data/gui/screens/main_menu.stkgui index 42d764b2f..9c14a5da5 100644 --- a/data/gui/screens/main_menu.stkgui +++ b/data/gui/screens/main_menu.stkgui @@ -46,13 +46,15 @@ + raw_text="TEST: Outro" label_location="hover"/> +
- diff --git a/sources.cmake b/sources.cmake index d4f28ae4d..ba4868d71 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 3c4cf01e7..7c9ef79c3 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -724,6 +724,9 @@ namespace UserConfigParams &m_recording_group, "Specify the fps of recording video")); // ---- Debug - not saved to config file + /** If high scores will not be saved. For repeated testing on tracks. */ + PARAM_PREFIX bool m_no_high_scores PARAM_DEFAULT(false); + /** If gamepad debugging is enabled. */ PARAM_PREFIX bool m_unit_testing PARAM_DEFAULT(false); @@ -736,7 +739,7 @@ namespace UserConfigParams /** Wiimote debugging. */ PARAM_PREFIX bool m_wiimote_debug PARAM_DEFAULT( false ); - /** Debug gamepads by visualising their values. */ + /** Debug gamepads by visualising their values. */ PARAM_PREFIX bool m_gamepad_visualisation PARAM_DEFAULT( false ); /** If material debugging (printing terrain specific slowdown) diff --git a/src/main.cpp b/src/main.cpp index c05d4dd6f..35331eef8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -937,6 +937,8 @@ int handleCmdLine(bool has_server_config, bool has_parent_process) if (CommandLine::has("--unit-testing")) UserConfigParams::m_unit_testing = true; + if (CommandLine::has("--no-high-scores")) + UserConfigParams::m_no_high_scores=true; if (CommandLine::has("--gamepad-debug")) UserConfigParams::m_gamepad_debug=true; if (CommandLine::has("--keyboard-debug")) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index bee2a8b2b..734591b34 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -285,9 +285,9 @@ void World::init() if (Camera::getNumCameras() == 0) { auto cl = LobbyProtocol::get(); - if ( (NetworkConfig::get()->isServer() && - !GUIEngine::isNoGraphics() ) || - RaceManager::get()->isWatchingReplay() || + if ((NetworkConfig::get()->isServer() && + !GUIEngine::isNoGraphics()) || + RaceManager::get()->isWatchingReplay() || (cl && cl->isSpectator())) { // In case that the server is running with gui, watching replay or @@ -581,8 +581,8 @@ Controller* World::loadAIController(AbstractKart* kart) { case 0: // If requested, start the test ai - if( (AIBaseController::getTestAI()!=0 ) && - ( (kart->getWorldKartId()+1) % AIBaseController::getTestAI() )==0) + if((AIBaseController::getTestAI()!=0) && + ((kart->getWorldKartId()+1) % AIBaseController::getTestAI()) == 0) controller = new TestAI(kart); else controller = new SkiddingAI(kart); @@ -748,11 +748,20 @@ void World::terminateRace() } } // igetNumNonGhostKarts() > 0 && + RaceManager::get()->getNumLaps() > 0 && + !(UserConfigParams::m_no_high_scores)) { updateHighscores(&best_highscore_rank); } @@ -870,7 +879,7 @@ void World::resetAllKarts() (*i)->getMaterial() && (*i)->getMaterial()->hasGravity() ? (*i)->getNormal() * -g : Vec3(0, -g, 0)); } - for(int i=0; igetPhysicsFPS(); i++) + for(int i=0; igetPhysicsFPS(); i++) Physics::get()->update(1); for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++) @@ -1168,7 +1177,7 @@ void World::update(int ticks) PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); // Update all the karts. This in turn will also update the controller, - // which causes all AI steering commands set. So in the following + // which causes all AI steering commands set. So in the following // physics update the new steering is taken into account. const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) @@ -1289,10 +1298,10 @@ void World::updateHighscores(int* best_highscore_rank) // Only record times for local player karts and only if // they finished the race - if(!m_karts[index[pos]]->getController()->isLocalPlayerController()) + if (!m_karts[index[pos]]->getController()->isLocalPlayerController() || + !m_karts[index[pos]]->hasFinishedRace() || + m_karts[index[pos]]->isEliminated()) continue; - if (!m_karts[index[pos]]->hasFinishedRace()) continue; - if (m_karts[index[pos]]->isEliminated()) continue; assert(index[pos] < m_karts.size()); Kart *k = (Kart*)m_karts[index[pos]].get(); @@ -1313,6 +1322,9 @@ void World::updateHighscores(int* best_highscore_rank) *best_highscore_rank = highscore_rank; } + Highscores::setSortOrder(Highscores::SO_DEFAULT); + highscore_manager->sortHighscores(false); + highscore_manager->saveHighscores(); } } // next position @@ -1618,7 +1630,7 @@ void World::setAITeam() { m_red_ai = RaceManager::get()->getNumberOfRedAIKarts(); m_blue_ai = RaceManager::get()->getNumberOfBlueAIKarts(); - + for (int i = 0; i < (int)RaceManager::get()->getNumLocalPlayers(); i++) { KartTeam team = RaceManager::get()->getKartInfo(i).getKartTeam(); diff --git a/src/race/highscore_manager.cpp b/src/race/highscore_manager.cpp index 50409004f..59b158aab 100644 --- a/src/race/highscore_manager.cpp +++ b/src/race/highscore_manager.cpp @@ -44,9 +44,8 @@ HighscoreManager::HighscoreManager() HighscoreManager::~HighscoreManager() { saveHighscores(); - for(type_all_scores::iterator i = m_all_scores.begin(); - i != m_all_scores.end(); i++) - delete *i; + clearHighscores(); + } // ~HighscoreManager // ----------------------------------------------------------------------------- @@ -124,7 +123,7 @@ void HighscoreManager::loadHighscores() Log::error("Highscore Manager", "Invalid highscore entry will be skipped : %s\n", e.what()); continue; } - m_all_scores.push_back(highscores); + m_all_scores.push_back(std::unique_ptr(highscores)); } // next entry if(UserConfigParams::logMisc()) @@ -186,20 +185,19 @@ Highscores* HighscoreManager::getHighscores(const Highscores::HighscoreType &hig Highscores *highscores = 0; // See if we already have a record for this type - for(type_all_scores::iterator i = m_all_scores.begin(); - i != m_all_scores.end(); i++) + for (auto& hs : m_all_scores) { - if((*i)->matches(highscore_type, num_karts, difficulty, trackName, + if (hs->matches(highscore_type, num_karts, difficulty, trackName, number_of_laps, reverse) ) { // we found one entry for this kind of race, return it - return (*i); + return hs.get(); } } // for i in m_all_scores // we don't have an entry for such a race currently. Create one. highscores = new Highscores(highscore_type, num_karts, difficulty, trackName, number_of_laps, reverse); - m_all_scores.push_back(highscores); + m_all_scores.push_back(std::unique_ptr(highscores)); return highscores; } // getHighscores diff --git a/src/race/highscore_manager.hpp b/src/race/highscore_manager.hpp index 898716479..c305cd892 100644 --- a/src/race/highscore_manager.hpp +++ b/src/race/highscore_manager.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "race/highscores.hpp" @@ -36,25 +37,46 @@ class HighscoreManager public: private: static const unsigned int CURRENT_HSCORE_FILE_VERSION; - typedef std::vector type_all_scores; - type_all_scores m_all_scores; + std::vector > m_all_scores; std::string m_filename; bool m_can_write; - void loadHighscores(); - void setFilename(); + void setFilename(); public: HighscoreManager(); ~HighscoreManager(); + // ------------------------------------------------------------------------ + void loadHighscores(); + // ------------------------------------------------------------------------ void saveHighscores(); + // ------------------------------------------------------------------------ Highscores *getHighscores(const Highscores::HighscoreType &highscore_type, int num_karts, const RaceManager::Difficulty difficulty, const std::string &trackName, const int number_of_laps, const bool reverse); + // ------------------------------------------------------------------------ + void deleteHighscores(int i) { m_all_scores.erase + (m_all_scores.begin() + i); } + // ------------------------------------------------------------------------ + void clearHighscores() { m_all_scores.clear(); } + // ------------------------------------------------------------------------ + bool highscoresEmpty() { return m_all_scores.empty(); } + // ------------------------------------------------------------------------ + Highscores* getHighscoresAt(int i) { return m_all_scores.at(i).get(); } + // ------------------------------------------------------------------------ + int highscoresSize() { return m_all_scores.size(); } + // ------------------------------------------------------------------------ + void sortHighscores(bool reverse) + { + (reverse ? std::stable_sort(m_all_scores.rbegin(), + m_all_scores.rend(), Highscores::compare) : + std::stable_sort(m_all_scores.begin(), + m_all_scores.end(), Highscores::compare)); + } }; // HighscoreManager extern HighscoreManager* highscore_manager; diff --git a/src/race/highscores.cpp b/src/race/highscores.cpp index 4fd7e3283..5bffef6c1 100644 --- a/src/race/highscores.cpp +++ b/src/race/highscores.cpp @@ -21,11 +21,15 @@ #include "io/utf_writer.hpp" #include "io/xml_node.hpp" #include "race/race_manager.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" #include "utils/log.hpp" #include #include +Highscores::SortOrder Highscores::m_sort_order = Highscores::SO_DEFAULT; + // ----------------------------------------------------------------------------- Highscores::Highscores(const HighscoreType &highscore_type, int num_karts, @@ -53,9 +57,9 @@ Highscores::Highscores(const XMLNode &node) { m_track = ""; m_highscore_type = "HST_UNDEFINED"; - m_number_of_karts = -1; + m_number_of_karts = 0; m_difficulty = -1; - m_number_of_laps = -1; + m_number_of_laps = 0; m_reverse = false; for(int i=0; igetTrack(m_track); + Track* b = track_manager->getTrack(hi.m_track); + std::wstring sort_name_a, sort_name_b; + if (a) + sort_name_a = a->getSortName().c_str(); + if (b) + sort_name_b = b->getSortName().c_str(); + return sort_name_a > sort_name_b; + } + case SO_KART_NUM: + return m_number_of_karts < hi.m_number_of_karts; + case SO_DIFF: + return m_difficulty < hi.m_difficulty; + case SO_LAPS: + return m_number_of_laps < hi.m_number_of_laps; + case SO_REV: + return m_reverse < hi.m_reverse; + } // switch + return true; +} // operator < diff --git a/src/race/highscores.hpp b/src/race/highscores.hpp index 892f52f3c..ac1337f36 100644 --- a/src/race/highscores.hpp +++ b/src/race/highscores.hpp @@ -18,28 +18,41 @@ #ifndef HEADER_HIGHSCORES_HPP #define HEADER_HIGHSCORES_HPP +#include #include #include #include #include "race/race_manager.hpp" -#include +#include "irrString.h" + +using namespace irr::core; class XMLNode; class UTFWriter; /** - * Represents one highscore entry, i.e. the (atm up to three) highscores + * Represents one highscore entry, i.e. the (atm up to five) highscores * for a particular setting (track, #karts, difficulty etc). * \ingroup race */ class Highscores { public: + /** Order of sort for Highscores */ + enum SortOrder + { + SO_DEFAULT, + SO_TRACK = SO_DEFAULT, // Sorted by internal track name + SO_KART_NUM, // Sorted by amount of karts used + SO_DIFF, // Sorted by difficulty level + SO_LAPS, // Sorted by number of laps + SO_REV // Sorted by if using reverse mode + }; + typedef std::string HighscoreType; -private: enum {HIGHSCORE_LEN = 5}; // It's a top 5 list std::string m_track; HighscoreType m_highscore_type; @@ -47,10 +60,18 @@ private: int m_difficulty; int m_number_of_laps; bool m_reverse; - std::string m_kart_name[HIGHSCORE_LEN]; - irr::core::stringw m_name[HIGHSCORE_LEN]; - float m_time[HIGHSCORE_LEN]; + +private: + std::array m_kart_name; + std::array m_name; + std::array m_time; + + static SortOrder m_sort_order; + public: + bool operator < (const Highscores& hi) const; + + static bool compare(const std::unique_ptr& a, const std::unique_ptr& b) { return (*a < *b); } /** Creates a new entry */ Highscores (const Highscores::HighscoreType &highscore_type, @@ -60,18 +81,25 @@ public: /** Creates an entry from a file */ Highscores (const XMLNode &node); - + // ------------------------------------------------------------------------ void readEntry (const XMLNode &node); + // ------------------------------------------------------------------------ void writeEntry(UTFWriter &writer); + // ------------------------------------------------------------------------ int matches (const HighscoreType &highscore_type, int num_karts, const RaceManager::Difficulty &difficulty, const std::string &track, const int number_of_laps, const bool reverse); + // ------------------------------------------------------------------------ int addData (const std::string& kart_name, const irr::core::stringw& name, const float time); + // ------------------------------------------------------------------------ int getNumberEntries() const; + // ------------------------------------------------------------------------ void getEntry (int number, std::string &kart_name, irr::core::stringw &name, float *const time) const; + // ------------------------------------------------------------------------ + static void setSortOrder(SortOrder so) { m_sort_order = so; } }; // Highscores #endif diff --git a/src/states_screens/dialogs/high_score_info_dialog.cpp b/src/states_screens/dialogs/high_score_info_dialog.cpp new file mode 100644 index 000000000..9d58d1ce5 --- /dev/null +++ b/src/states_screens/dialogs/high_score_info_dialog.cpp @@ -0,0 +1,249 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 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 "states_screens/dialogs/high_score_info_dialog.hpp" + +#include "config/player_manager.hpp" +#include "config/user_config.hpp" +#include "guiengine/CGUISpriteBank.hpp" +#include "graphics/stk_tex_manager.hpp" +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "karts/kart_properties.hpp" +#include "karts/kart_properties_manager.hpp" +#include "race/highscores.hpp" +#include "race/highscore_manager.hpp" +#include "race/race_manager.hpp" +#include "states_screens/high_score_selection.hpp" +#include "states_screens/state_manager.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include "utils/string_utils.hpp" +#include "utils/translation.hpp" + +using namespace GUIEngine; +using namespace irr::core; + +// ----------------------------------------------------------------------------- +HighScoreInfoDialog::HighScoreInfoDialog(Highscores* highscore, bool is_linear) + : ModalDialog(0.75f,0.75f) +{ + m_hs = highscore; + + loadFromFile("high_score_info_dialog.stkgui"); + + Track* track = track_manager->getTrack(m_hs->m_track); + + m_track_screenshot_widget = getWidget("track_screenshot"); + m_track_screenshot_widget->setFocusable(false); + m_track_screenshot_widget->m_tab_stop = false; + + // temporary icon, will replace it just after (but it will be shown if the given icon is not found) + m_track_screenshot_widget->m_properties[PROP_ICON] = "gui/icons/main_help.png"; + + irr::video::ITexture* image = STKTexManager::getInstance() + ->getTexture(track->getScreenshotFile(), + "While loading screenshot for track '%s':", track->getFilename()); + if(!image) + { + image = STKTexManager::getInstance()->getTexture("main_help.png", + "While loading screenshot for track '%s':", track->getFilename()); + } + if (image != NULL) + m_track_screenshot_widget->setImage(image); + + // TODO : small refinement, add the possibility to tab stops for lists + // to make this unselectable by keyboard/mouse + m_high_score_list = getWidget("high_score_list"); + assert(m_high_score_list != NULL); + + /* Used to display kart icons for the entries */ + irr::gui::STKModifiedSpriteBank *icon_bank = HighScoreSelection::getInstance()->getIconBank(); + int icon_height = GUIEngine::getFontHeight() * 3 / 2; + + icon_bank->setScale(icon_height/128.0f); + icon_bank->setTargetIconSize(128, 128); + m_high_score_list->setIcons(icon_bank, (int)icon_height); + + updateHighscoreEntries(); + + //Setup static text labels + m_high_score_label = getWidget("name"); + m_high_score_label->setText(_("Top %d High Scores", m_hs->HIGHSCORE_LEN), true); + m_track_name_label = getWidget("track-name"); + m_track_name_label->setText(_("Track: %s", + track_manager->getTrack(m_hs->m_track)->getName()), true); + m_difficulty_label = getWidget("difficulty"); + m_difficulty_label->setText(_("Difficulty: %s", RaceManager::get()-> + getDifficultyName((RaceManager::Difficulty) + m_hs->m_difficulty)), true); + m_num_karts_label = getWidget("num-karts"); + m_reverse_label = getWidget("reverse"); + m_num_laps_label = getWidget("num-laps"); + + if (is_linear) + { + m_num_karts_label->setVisible(true); + m_num_karts_label->setText(_("Number of karts: %d", m_hs->m_number_of_karts), true); + + m_num_laps_label->setVisible(true); + m_num_laps_label->setText(_("Laps: %d", m_hs->m_number_of_laps), true); + + stringw is_reverse = m_hs->m_reverse ? _("Yes") : _("No"); + m_reverse_label->setVisible(true); + m_reverse_label->setText(_("Reverse: %s", is_reverse), true); + } + else + { + m_num_karts_label->setVisible(false); + m_num_laps_label->setVisible(false); + m_reverse_label->setVisible(false); + } + + m_action_widget = getWidget("actions"); + + m_action_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + m_action_widget->select("back", PLAYER_ID_GAME_MASTER); +} // HighScoreInfoDialog +// ----------------------------------------------------------------------------- +HighScoreInfoDialog::~HighScoreInfoDialog() +{ +} // ~HighScoreInfoDialog + +// ----------------------------------------------------------------------------- +void HighScoreInfoDialog::updateHighscoreEntries() +{ + m_high_score_list->clear(); + + const int amount = m_hs->getNumberEntries(); + + std::string kart_name; + core::stringw name; + float time; + + int time_precision = RaceManager::get()->currentModeTimePrecision(); + + // Fill highscore entries + for (int n = 0; n < m_hs->HIGHSCORE_LEN; n++) + { + irr::core::stringw line; + int icon = -1; + + // Check if this entry is filled or still empty + if (n < amount) + { + m_hs->getEntry(n, kart_name, name, &time); + + std::string time_string = StringUtils::timeToString(time, time_precision); + + for(unsigned int i=0; igetNumberOfKarts(); i++) + { + const KartProperties* prop = kart_properties_manager->getKartById(i); + if (kart_name == prop->getIdent()) + { + icon = i; + break; + } + } + + line = name + " " + core::stringw(time_string.c_str()); + } + else + { + //I18N: for empty highscores entries + line = _("(Empty)"); + } + + if (icon == -1) + { + icon = HighScoreSelection::getInstance()->getUnknownKartIcon(); + } + + std::vector row; + row.push_back(GUIEngine::ListWidget::ListCell(line.c_str(), icon, 5, false)); + m_high_score_list->addItem(StringUtils::toString(n), row); + } +} // updateHighscoreEntries + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation + HighScoreInfoDialog::processEvent(const std::string& event_source) +{ + if (event_source == "actions") + { + const std::string& selection = + m_action_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (selection == "start") + { + // Use the last used device + InputDevice* device = input_manager->getDeviceManager()->getLatestUsedDevice(); + + // Create player and associate player with device + StateManager::get()->createActivePlayer(PlayerManager::getCurrentPlayer(), device); + + RaceManager::get()->setMinorMode(HighScoreSelection::getInstance()->getActiveMode()); + + bool reverse = m_hs->m_reverse; + std::string track_name = m_hs->m_track; + int laps = m_hs->m_number_of_laps; + + RaceManager::get()->setDifficulty((RaceManager::Difficulty) m_hs->m_difficulty); + + RaceManager::get()->setNumKarts(m_hs->m_number_of_karts); + RaceManager::get()->setNumPlayers(1); + + if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) + { + Log::warn("HighScoreInfoDialog", "Cannot find kart '%s', will revert to default", + UserConfigParams::m_default_kart.c_str()); + UserConfigParams::m_default_kart.revertToDefaults(); + } + RaceManager::get()->setPlayerKart(0, UserConfigParams::m_default_kart); + + // Disable accidentally unlocking of a challenge + PlayerManager::getCurrentPlayer()->setCurrentChallenge(""); + + RaceManager::get()->setReverseTrack(reverse); + + // ASSIGN should make sure that only input from assigned devices is read + input_manager->getDeviceManager()->setAssignMode(ASSIGN); + input_manager->getDeviceManager() + ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); + + ModalDialog::dismiss(); + + RaceManager::get()->startSingleRace(track_name, laps, false); + return GUIEngine::EVENT_BLOCK; + } + else if (selection == "remove") + { + ModalDialog::dismiss(); + + dynamic_cast(GUIEngine::getCurrentScreen()) + ->onDeleteHighscores(); + return GUIEngine::EVENT_BLOCK; + } + else if (selection == "back") + { + ModalDialog::dismiss(); + return GUIEngine::EVENT_BLOCK; + } + } + return GUIEngine::EVENT_LET; +} // processEvent diff --git a/src/states_screens/dialogs/high_score_info_dialog.hpp b/src/states_screens/dialogs/high_score_info_dialog.hpp new file mode 100644 index 000000000..dfffcae03 --- /dev/null +++ b/src/states_screens/dialogs/high_score_info_dialog.hpp @@ -0,0 +1,59 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 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_HIGH_SCORE_INFO_DIALOG_HPP +#define HEADER_HIGH_SCORE_INFO_DIALOG_HPP + +#include "guiengine/modaldialog.hpp" +#include "guiengine/widgets.hpp" +#include "race/highscores.hpp" + +/** \brief Dialog that allows a user to manage a high score + * \ingroup states_screens + */ +class HighScoreInfoDialog : public GUIEngine::ModalDialog +{ + +private: + + bool m_self_destroy; + + Highscores* m_hs; + + GUIEngine::RibbonWidget* m_action_widget; + + GUIEngine::LabelWidget* m_high_score_label; + GUIEngine::LabelWidget* m_track_name_label; + GUIEngine::LabelWidget* m_num_karts_label; + GUIEngine::LabelWidget* m_difficulty_label; + GUIEngine::LabelWidget* m_reverse_label; + GUIEngine::LabelWidget* m_num_laps_label; + + GUIEngine::ListWidget* m_high_score_list; + GUIEngine::IconButtonWidget* m_track_screenshot_widget; + + void updateHighscoreEntries(); + +public: + HighScoreInfoDialog(Highscores* highscore, bool is_linear); + ~HighScoreInfoDialog(); + + GUIEngine::EventPropagation processEvent(const std::string& eventSource); +}; // class HighScoreInfoDialog + +#endif diff --git a/src/states_screens/high_score_selection.cpp b/src/states_screens/high_score_selection.cpp new file mode 100644 index 000000000..94c80ae8c --- /dev/null +++ b/src/states_screens/high_score_selection.cpp @@ -0,0 +1,345 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 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 "states_screens/high_score_selection.hpp" + +#include "config/player_manager.hpp" +#include "config/user_config.hpp" +#include "graphics/material.hpp" +#include "guiengine/CGUISpriteBank.hpp" +#include "karts/kart_properties.hpp" +#include "karts/kart_properties_manager.hpp" +#include "race/highscores.hpp" +#include "race/highscore_manager.hpp" +#include "states_screens/dialogs/high_score_info_dialog.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/online/tracks_screen.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include "utils/string_utils.hpp" +#include "utils/translation.hpp" + +using namespace GUIEngine; + +// ---------------------------------------------------------------------------- +/** Constructor, which loads the stkgui file. + */ +HighScoreSelection::HighScoreSelection() : Screen("high_score_selection.stkgui") +{ + m_selected_index = -1; +} // HighScoreSelection + +// ---------------------------------------------------------------------------- +/** Destructor. + */ +HighScoreSelection::~HighScoreSelection() +{ +} // HighScoreSelection + +// ---------------------------------------------------------------------------- +void HighScoreSelection::tearDown() +{ + m_high_scores_list_widget->setIcons(NULL); +} + +// ---------------------------------------------------------------------------- +void HighScoreSelection::unloaded() +{ + delete m_icon_bank; + m_icon_bank = NULL; +} // unloaded + +// ---------------------------------------------------------------------------- +/** Triggers a refresh of the high score list. + */ +void HighScoreSelection::refresh(bool forced_update, bool update_columns) +{ + m_selected_index = -1; + + if (highscore_manager->highscoresEmpty() || forced_update) + { + if (!highscore_manager->highscoresEmpty()) + { + highscore_manager->clearHighscores(); + } + highscore_manager->loadHighscores(); + } + defaultSort(); + + loadList(); + + if (update_columns) + { + m_high_scores_list_widget->clearColumns(); + beforeAddingWidget();//Reload the columns used + } +} // refresh + +// ---------------------------------------------------------------------------- +/** Set pointers to the various widgets. + */ +void HighScoreSelection::loadedFromFile() +{ + m_high_scores_list_widget = getWidget("high_scores_list"); + assert(m_high_scores_list_widget != NULL); + m_high_scores_list_widget->setColumnListener(this); + + m_mode_tabs = getWidget("race_mode"); + m_active_mode = RaceManager::MINOR_MODE_NORMAL_RACE; + m_active_mode_is_linear = true; + + m_icon_bank = new irr::gui::STKModifiedSpriteBank( GUIEngine::getGUIEnv()); + + for(unsigned int i=0; igetNumberOfKarts(); i++) + { + const KartProperties* prop = kart_properties_manager->getKartById(i); + m_icon_bank->addTextureAsSprite(prop->getIconMaterial()->getTexture()); + } + + video::ITexture* kart_not_found = irr_driver->getTexture( + file_manager->getAsset(FileManager::GUI_ICON, "random_kart.png")); + + m_icon_unknown_kart = m_icon_bank->addTextureAsSprite(kart_not_found); + + video::ITexture* lock = irr_driver->getTexture( file_manager->getAsset( + FileManager::GUI_ICON, "gui_lock.png")); + + m_icon_lock = m_icon_bank->addTextureAsSprite(lock); +} // loadedFromFile + +// ---------------------------------------------------------------------------- +/** Clear the high score entry list, which will be reloaded. + */ +void HighScoreSelection::beforeAddingWidget() +{ + m_high_scores_list_widget->addColumn(_C("column_name", "Track"), 7); + m_high_scores_list_widget->addColumn(_C("column_name", "Difficulty"), 4); + if (m_active_mode_is_linear) + { + m_high_scores_list_widget->addColumn(_C("column_name", "Number of karts"), 4); + m_high_scores_list_widget->addColumn(_C("column_name", "Laps"), 3); + m_high_scores_list_widget->addColumn(_C("column_name", "Reverse"), 3); + } + + m_high_scores_list_widget->createHeader(); +} // beforeAddingWidget + +// ---------------------------------------------------------------------------- +void HighScoreSelection::init() +{ + Screen::init(); + + int icon_height = GUIEngine::getFontHeight(); + int row_height = GUIEngine::getFontHeight() * 5 / 4; + + // 128 is the height of the image file + m_icon_bank->setScale(icon_height/128.0f); + m_icon_bank->setTargetIconSize(128, 128); + m_high_scores_list_widget->setIcons(m_icon_bank, (int)row_height); + + refresh(/*reload high score list*/ false, /* update columns */ true); +} // init + +// ---------------------------------------------------------------------------- +/** Loads the list of all high score entries. The gui element will be + * updated. + */ +void HighScoreSelection::loadList() +{ + m_high_scores_list_widget->clear(); + + if (highscore_manager->highscoresEmpty()) + return; + + for (int i = 0; i < highscore_manager->highscoresSize(); i++) + { + Highscores* hs = highscore_manager->getHighscoresAt(i); + + if (m_active_mode == RaceManager::MINOR_MODE_NORMAL_RACE && + hs->m_highscore_type != "HST_STANDARD") + continue; + else if (m_active_mode == RaceManager::MINOR_MODE_TIME_TRIAL && + hs->m_highscore_type != "HST_STD_TIMETRIAL") + continue; + else if (m_active_mode == RaceManager::MINOR_MODE_EASTER_EGG && + hs->m_highscore_type != "HST_EASTER_EGG_HUNT") + continue; + + Track* track = track_manager->getTrack(hs->m_track); + + if (track == NULL || hs->getNumberEntries() < 1) + continue; + + std::vector row; + //The third argument should match the numbers used in beforeAddingWidget + row.push_back(GUIEngine::ListWidget::ListCell(track->getName() , -1, 7)); + + bool display_lock = false; + if ((RaceManager::Difficulty)hs->m_difficulty == RaceManager::DIFFICULTY_BEST && + PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) + display_lock = true; + + row.push_back(GUIEngine::ListWidget::ListCell(RaceManager::get()-> + getDifficultyName((RaceManager::Difficulty) hs->m_difficulty), + display_lock ? m_icon_lock : -1, 4, true)); + + if (m_active_mode_is_linear) + { + row.push_back(GUIEngine::ListWidget::ListCell + (StringUtils::toWString(hs->m_number_of_karts), -1, 4, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (StringUtils::toWString(hs->m_number_of_laps), -1, 3, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (hs->m_reverse ? _("Yes") : _("No"), -1, 3, true)); + } + m_high_scores_list_widget->addItem(StringUtils::toString(i), row); + } +} // loadList + +// ---------------------------------------------------------------------------- +void HighScoreSelection::eventCallback(GUIEngine::Widget* widget, + const std::string& name, + const int playerID) +{ + if (name == "back") + { + StateManager::get()->escapePressed(); + } + else if (name == "remove-all") + { + onClearHighscores(); + } + else if (name == m_high_scores_list_widget->m_properties[GUIEngine::PROP_ID]) + { + m_selected_index = -1; + const bool success = StringUtils::fromString(m_high_scores_list_widget + ->getSelectionInternalName(), m_selected_index); + // This can happen e.g. when the list is empty and the user + // clicks somewhere. + if (m_selected_index >= (signed)highscore_manager->highscoresSize() || + m_selected_index < 0 || !success) + { + return; + } + if (PlayerManager::getCurrentPlayer()->isLocked("difficulty_best")) + { + Highscores* hs = highscore_manager->getHighscoresAt(m_selected_index); + if((RaceManager::Difficulty)hs->m_difficulty == RaceManager::DIFFICULTY_BEST) + return; + } + + new HighScoreInfoDialog(highscore_manager->getHighscoresAt(m_selected_index), m_active_mode_is_linear); + } // click on high score entry + else if (name == "race_mode") + { + std::string selection = ((RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (selection == "tab_normal_race") + m_active_mode = RaceManager::MINOR_MODE_NORMAL_RACE; + else if (selection == "tab_time_trial") + m_active_mode = RaceManager::MINOR_MODE_TIME_TRIAL; + else if (selection == "tab_egg_hunt") + m_active_mode = RaceManager::MINOR_MODE_EASTER_EGG; + + m_active_mode_is_linear = RaceManager::get()->isLinearRaceMode(m_active_mode); + refresh(/*keep high score list*/ false, /* update columns */ true); + } +} // eventCallback + +// ---------------------------------------------------------------------------- +void HighScoreSelection::onDeleteHighscores() +{ + new MessageDialog( _("Are you sure you want to remove this high score entry?"), + MessageDialog::MESSAGE_DIALOG_CONFIRM, this, false); +} // onDeleteHighscores + +// ---------------------------------------------------------------------------- +void HighScoreSelection::onClearHighscores() +{ + m_selected_index = -1; + new MessageDialog( _("Are you sure you want to remove all of your high scores?"), + MessageDialog::MESSAGE_DIALOG_CONFIRM, this, false); +} // onClearHighscores + +// ---------------------------------------------------------------------------- +void HighScoreSelection::onConfirm() +{ + if (m_selected_index < 0) + { + highscore_manager->clearHighscores(); + } + else + { + highscore_manager->deleteHighscores(m_selected_index); + } + defaultSort(); + + highscore_manager->saveHighscores(); + + // Restore the previously used sort direction + highscore_manager->sortHighscores(m_reverse_sort); + + ModalDialog::dismiss(); + HighScoreSelection::getInstance()->refresh(); +} // onConfirm + +// ---------------------------------------------------------------------------- +/** Change the sort order if a column was clicked. + * \param column_id ID of the column that was clicked. + */ + +void HighScoreSelection::onColumnClicked(int column_id, bool sort_desc, bool sort_default) +{ + // Begin by resorting the list to default + defaultSort(); + + if (sort_default) + { + loadList(); + return; + } + + if (column_id == 0) + Highscores::setSortOrder(Highscores::SO_TRACK); + else if (column_id == 1) + Highscores::setSortOrder(Highscores::SO_DIFF); + else if (column_id == 2) + Highscores::setSortOrder(Highscores::SO_KART_NUM); + else if (column_id == 3) + Highscores::setSortOrder(Highscores::SO_LAPS); + else if (column_id == 4) + Highscores::setSortOrder(Highscores::SO_REV); + else + assert(0); + + m_reverse_sort = sort_desc; + highscore_manager->sortHighscores(sort_desc); + + loadList(); +} // onColumnClicked + +// ---------------------------------------------------------------------------- +/** Apply the default sorting to the high score list + */ + +void HighScoreSelection::defaultSort() +{ + m_reverse_sort = false; + Highscores::setSortOrder(Highscores::SO_DEFAULT); + highscore_manager->sortHighscores(false); +} // defaultSort diff --git a/src/states_screens/high_score_selection.hpp b/src/states_screens/high_score_selection.hpp new file mode 100644 index 000000000..4f583483b --- /dev/null +++ b/src/states_screens/high_score_selection.hpp @@ -0,0 +1,102 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 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_HIGH_SCORE_SELECTION_HPP +#define HEADER_HIGH_SCORE_SELECTION_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets.hpp" +#include "race/race_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" + +namespace GUIEngine { class Widget; } + +/** + * \brief HighScoreSelection + * \ingroup states_screens + */ +class HighScoreSelection : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton, + public GUIEngine::IListWidgetHeaderListener, + public MessageDialog::IConfirmDialogListener + +{ + friend class GUIEngine::ScreenSingleton; + +private: + HighScoreSelection(); + ~HighScoreSelection(); + + GUIEngine::ListWidget* m_high_scores_list_widget; + GUIEngine::RibbonWidget* m_mode_tabs; + bool m_active_mode_is_linear; + bool m_reverse_sort; + RaceManager::MinorRaceModeType m_active_mode; + int m_selected_index; + + irr::gui::STKModifiedSpriteBank *m_icon_bank; + + /** Icon for unknown karts */ + int m_icon_unknown_kart; + /** Icon for locked replays */ + int m_icon_lock; + + void defaultSort(); + +public: + irr::gui::STKModifiedSpriteBank* getIconBank() { return m_icon_bank; } + + int getUnknownKartIcon() { return m_icon_unknown_kart; } + + void refresh(bool forced_update = true, bool update_columns = false); + + /** Load the addons into the main list.*/ + void loadList(); + + void onDeleteHighscores(); + + void onClearHighscores(); + + const RaceManager::MinorRaceModeType getActiveMode() { return m_active_mode; } + + const bool isActiveModeLinear() { return m_active_mode_is_linear; } + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) OVERRIDE; + + virtual void init() OVERRIDE; + + virtual void tearDown() OVERRIDE; + + virtual void unloaded() OVERRIDE; + + /** \brief Implement IConfirmDialogListener callback */ + virtual void onConfirm() OVERRIDE; + +}; // HighScoreSelection + +#endif diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 45d8f5c79..1268032dd 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -46,6 +46,7 @@ #include "states_screens/cutscene_general.hpp" #include "states_screens/grand_prix_editor_screen.hpp" #include "states_screens/help_screen_1.hpp" +#include "states_screens/high_score_selection.hpp" #include "states_screens/offline_kart_selection.hpp" #include "states_screens/online/online_profile_achievements.hpp" #include "states_screens/online/online_profile_servers.hpp" @@ -594,6 +595,10 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, { OnlineProfileAchievements::getInstance()->push(); } + else if (selection == "highscores") + { + HighScoreSelection::getInstance()->push(); + } #endif } // eventCallback diff --git a/src/states_screens/track_info_screen.cpp b/src/states_screens/track_info_screen.cpp index 1683970f5..ba1690f58 100644 --- a/src/states_screens/track_info_screen.cpp +++ b/src/states_screens/track_info_screen.cpp @@ -329,6 +329,7 @@ void TrackInfoScreen::init() m_ai_kart_spinner->setActive(true); // ---- High Scores + m_highscore_label->setText(_("High Scores"), false); m_highscore_label->setVisible(has_highscores); if (has_highscores)