stk-code_catmod/src/states_screens/race_result_gui.cpp

1701 lines
66 KiB
C++
Raw Normal View History

//
// SuperTuxKart - a fun racing game with go-kart
2015-03-29 20:31:42 -04:00
// Copyright (C) 2010-2015 Joerg Henrichs
//
// 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/race_result_gui.hpp"
#include "audio/music_manager.hpp"
2014-09-17 07:38:21 -04:00
#include "audio/sfx_manager.hpp"
#include "audio/sfx_base.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
2014-10-18 12:16:50 -04:00
#include "graphics/2dutils.hpp"
#include "graphics/material.hpp"
#include "guiengine/engine.hpp"
2018-04-21 04:00:14 -04:00
#include "guiengine/message_queue.hpp"
#include "guiengine/modaldialog.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/controller/local_player_controller.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/cutscene_world.hpp"
#include "modes/demo_world.hpp"
2018-08-17 00:54:38 -04:00
#include "modes/capture_the_flag.hpp"
#include "modes/overworld.hpp"
#include "modes/soccer_world.hpp"
2018-03-09 08:17:31 -05:00
#include "network/network_config.hpp"
2018-03-08 00:19:24 -05:00
#include "network/stk_host.hpp"
#include "network/protocols/client_lobby.hpp"
#include "race/highscores.hpp"
Numerous improvements related to ghost replays (#3244) * Update GUI files for replay improvements * Updated replay capabilities * Improve timer formatting possibilities Mainly, the ability to choose to display or not minutes, and to choose how many digits are shown after seconds (from 0 : second as smallest timestep - to 3 : ms as smallest timestep) Also displays "mm:ss.ms" rather than "mm:ss:ms". * Some new list widget possibilities Like the ability to update the header while the list is displayed (useful to add or remove columns) * Update ghost kart for the new replay data * Also update the ghost controller * Live differences with ghost replays in linear worlds * Replay-related UI changes Big changes to the replay selection screen, and small change to the race UI (add the live timer in ghost races) and the race result UI (add the option to directly race against a newly saved ghost). * Improves the replay action modal dialog * Fix time-to-ticks regression * Several requested improvements * Improved ghost icon Also updates the license * GUI changes and improvements to accomodate multi-mode support * Additional recorded data * More functions to get the current race state * Update replay variables to match what is used * Updated replay-related config values * Add ghost kart support to easter egg hunt mode * Transparent attachments for ghost karts * Use new stored data (color, item type) Also : - Interpolate speed for smoother display in watch-only mode - Coding style improvements * Fix coding style issues and add UI support for modes * Fix coding style issues & support for easter egg mode * Remove leftover prints * Use getDifficultyName to remove hardcoded values * Fix attach_ticks and coding style fixes * Make the position of the timers fully relative Otherwise, they would tend to touch each other in some resolutions Also add a comment about the 59.9995f * Make the list filling code clearer Also use getDifficultyName Most of the line changes shown by git correspond to moving around some bits or adjusting indentation. * Remove a TODO as requested * Fix dialog being dismissed too soon * Remove a fixme * Small clean up * Fix logging * Partial #3249 fix
2018-05-13 20:51:52 -04:00
#include "replay/replay_play.hpp"
#include "replay/replay_recorder.hpp"
#include "scriptengine/property_animator.hpp"
#include "states_screens/feature_unlocked.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "states_screens/online/networking_lobby.hpp"
#include "states_screens/race_setup_screen.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/string_utils.hpp"
#include <algorithm>
/** Constructor, initialises internal data structures.
*/
RaceResultGUI::RaceResultGUI() : Screen("race_result.stkgui",
/*pause race*/ false)
{
} // RaceResultGUI
//-----------------------------------------------------------------------------
/** Besides calling init in the base class this makes all buttons of this
* screen invisible. The buttons will only displayed once the animation is
* over.
*/
void RaceResultGUI::init()
{
Screen::init();
determineTableLayout();
m_animation_state = RR_INIT;
m_timer = 0;
getWidget("top")->setVisible(false);
getWidget("middle")->setVisible(false);
getWidget("bottom")->setVisible(false);
music_manager->stopMusic();
bool human_win = true;
unsigned int num_karts = race_manager->getNumberOfKarts();
for (unsigned int kart_id = 0; kart_id < num_karts; kart_id++)
{
const AbstractKart *kart = World::getWorld()->getKart(kart_id);
2018-04-21 04:00:14 -04:00
if (kart->getController()->isLocalPlayerController())
human_win = human_win && kart->getRaceResult();
}
m_finish_sound = SFXManager::get()->quickSound(
2016-06-24 20:22:48 -04:00
human_win ? "race_finish_victory" : "race_finish");
//std::string path = (human_win ? Different result music too later
// file_manager->getAsset(FileManager::MUSIC, "race_summary.music") :
// file_manager->getAsset(FileManager::MUSIC, "race_summary.music"));
std::string path = file_manager->getAsset(FileManager::MUSIC, "race_summary.music");
m_race_over_music = music_manager->getMusicInformation(path);
if (!m_finish_sound)
{
// If there is no finish sound (because sfx are disabled), start
// the race over music here (since the race over music is only started
// when the finish sound has been played).
music_manager->startMusic(m_race_over_music);
}
// Calculate how many track screenshots can fit into the "result-table" widget
GUIEngine::Widget* result_table = getWidget("result-table");
assert(result_table != NULL);
m_sshot_height = (int)(UserConfigParams::m_height*0.1275);
m_max_tracks = std::max(1, ((result_table->m_h - getFontHeight() * 5) /
(m_sshot_height + SSHOT_SEPARATION))); //Show at least one
// Calculate screenshot scrolling parameters
const std::vector<std::string> tracks =
race_manager->getGrandPrix().getTrackNames();
int n_tracks = (int)tracks.size();
int currentTrack = race_manager->getTrackNumber();
m_start_track = currentTrack;
if (n_tracks > m_max_tracks)
{
m_start_track = std::min(currentTrack, n_tracks - m_max_tracks);
m_end_track = std::min(currentTrack + m_max_tracks, n_tracks);
}
else
{
m_start_track = 0;
m_end_track = (int)tracks.size();
}
} // init
//-----------------------------------------------------------------------------
void RaceResultGUI::tearDown()
{
Screen::tearDown();
2016-07-18 11:00:40 -04:00
//m_font->setMonospaceDigits(m_was_monospace);
if (m_finish_sound != NULL &&
2014-10-13 17:05:04 -04:00
m_finish_sound->getStatus() == SFXBase::SFX_PLAYING)
{
m_finish_sound->stop();
}
} // tearDown
//-----------------------------------------------------------------------------
/** Makes the correct buttons visible again, and gives them the right label.
* 1) If something was unlocked, only a 'next' button is displayed.
*/
void RaceResultGUI::enableAllButtons()
{
GUIEngine::Widget *top = getWidget("top");
GUIEngine::Widget *middle = getWidget("middle");
GUIEngine::Widget *bottom = getWidget("bottom");
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
enableGPProgress();
}
// If we're in a network world, change the buttons text
if (World::getWorld()->isNetworkWorld())
{
top->setVisible(false);
2018-03-08 00:19:24 -05:00
middle->setText(_("Continue"));
middle->setVisible(true);
middle->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
2018-03-08 00:19:24 -05:00
bottom->setText(_("Quit the server"));
bottom->setVisible(true);
return;
}
// If something was unlocked
// -------------------------
int n = (int)PlayerManager::getCurrentPlayer()->getRecentlyCompletedChallenges().size();
if (n > 0)
{
top->setText(n == 1 ? _("You completed a challenge!")
: _("You completed challenges!"));
top->setVisible(true);
top->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
}
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
// In case of a GP:
// ----------------
top->setVisible(false);
top->setFocusable(false);
middle->setText(_("Continue"));
middle->setVisible(true);
if (race_manager->getTrackNumber() + 1 < race_manager->getNumOfTracks())
{
bottom->setText(_("Abort Grand Prix"));
bottom->setVisible(true);
bottom->setFocusable(true);
}
else
{
bottom->setFocusable(false);
}
middle->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
}
else
{
// Normal race
// -----------
middle->setText(_("Restart"));
middle->setVisible(true);
2017-10-29 13:34:40 -04:00
middle->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
if (race_manager->raceWasStartedFromOverworld())
{
top->setVisible(false);
bottom->setText(_("Back to challenge selection"));
}
else
{
Numerous improvements related to ghost replays (#3244) * Update GUI files for replay improvements * Updated replay capabilities * Improve timer formatting possibilities Mainly, the ability to choose to display or not minutes, and to choose how many digits are shown after seconds (from 0 : second as smallest timestep - to 3 : ms as smallest timestep) Also displays "mm:ss.ms" rather than "mm:ss:ms". * Some new list widget possibilities Like the ability to update the header while the list is displayed (useful to add or remove columns) * Update ghost kart for the new replay data * Also update the ghost controller * Live differences with ghost replays in linear worlds * Replay-related UI changes Big changes to the replay selection screen, and small change to the race UI (add the live timer in ghost races) and the race result UI (add the option to directly race against a newly saved ghost). * Improves the replay action modal dialog * Fix time-to-ticks regression * Several requested improvements * Improved ghost icon Also updates the license * GUI changes and improvements to accomodate multi-mode support * Additional recorded data * More functions to get the current race state * Update replay variables to match what is used * Updated replay-related config values * Add ghost kart support to easter egg hunt mode * Transparent attachments for ghost karts * Use new stored data (color, item type) Also : - Interpolate speed for smoother display in watch-only mode - Coding style improvements * Fix coding style issues and add UI support for modes * Fix coding style issues & support for easter egg mode * Remove leftover prints * Use getDifficultyName to remove hardcoded values * Fix attach_ticks and coding style fixes * Make the position of the timers fully relative Otherwise, they would tend to touch each other in some resolutions Also add a comment about the 59.9995f * Make the list filling code clearer Also use getDifficultyName Most of the line changes shown by git correspond to moving around some bits or adjusting indentation. * Remove a TODO as requested * Fix dialog being dismissed too soon * Remove a fixme * Small clean up * Fix logging * Partial #3249 fix
2018-05-13 20:51:52 -04:00
if (race_manager->isRecordingRace())
top->setText(_("Race against the new ghost replay"));
else
top->setText(_("Setup New Race"));
top->setVisible(true);
bottom->setText(_("Back to the menu"));
}
bottom->setVisible(true);
}
} // enableAllButtons
//-----------------------------------------------------------------------------
void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
const std::string& name, const int playerID)
{
int n_tracks = race_manager->getGrandPrix().getNumberOfTracks();
if (name == "up_button" && n_tracks > m_max_tracks && m_start_track > 0)
{
m_start_track--;
m_end_track--;
displayScreenShots();
}
else if (name == "down_button" && n_tracks > m_max_tracks &&
m_start_track < (n_tracks - m_max_tracks))
{
m_start_track++;
m_end_track++;
displayScreenShots();
}
2018-04-21 04:00:14 -04:00
// If we're playing online :
if (World::getWorld()->isNetworkWorld())
{
if (name == "middle") // Continue button (return to server lobby)
{
// Signal to the server that this client is back in the lobby now.
auto cl = LobbyProtocol::get<ClientLobby>();
if (cl)
cl->doneWithResults();
getWidget("middle")->setText(_("Waiting for others"));
}
if (name == "bottom") // Quit server (return to online lan / wan menu)
{
race_manager->clearNetworkGrandPrixResult();
if (STKHost::existHost())
{
STKHost::get()->shutdown();
}
race_manager->exitRace();
race_manager->setAIKartOverride("");
StateManager::get()->resetAndSetStack(
NetworkConfig::get()->getResetScreens().data());
NetworkConfig::get()->unsetNetworking();
}
return;
}
// If something was unlocked, the 'continue' button was
// actually used to display "Show unlocked feature(s)" text.
// ---------------------------------------------------------
int n = (int)PlayerManager::getCurrentPlayer()
->getRecentlyCompletedChallenges().size();
if (n>0)
{
if (name == "top")
{
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
cleanupGPProgress();
}
std::vector<const ChallengeData*> unlocked =
PlayerManager::getCurrentPlayer()->getRecentlyCompletedChallenges();
bool gameCompleted = false;
for (unsigned int n = 0; n < unlocked.size(); n++)
{
SuperTux in Story Mode (and other improvements) (#3207) * Add SuperTux difficulty & update number of karts Also make the expert challenge slightly easier to match more the difficulty of other challenges. * Add SuperTux difficulty & update number of karts & points required Also give some more time margin in easier difficulties, as it is a hard challenge compared to most. * Add SuperTux difficulty & update number of karts & points required Also change the lap count to 4 as it is a very short track (sub 30s) * Add SuperTux difficulty Also tweak the expert challenge to have a more appropriate difficulty * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also correct the requirement position, since this is not a FTL race anymore. * Add SuperTux difficulty & update number of karts & points required Also slight balancing improvements for the usual difficulties. * Add SuperTux difficulty & update number of karts & points required Also adds a position requirement in expert * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 5, as this is a very short track. The time requirements for easier difficulties have been kept proportionally similar to before. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4. * Add SuperTux difficulty & update number of karts & points required Also add a position requirement to expert and intermediate. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4, as a lap often is 30s or less in expert/supertux * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Rename islandtrack.challenge to gran_paradiso.challenge * Rename challenge file * Add SuperTux difficulty & update number of karts & points required Also makes the time limit in expert less easy and tweak position requirement. * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Doesn't unlock the SuperTux difficulty anymore - it's managed elsewhere. * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add new unlock challenges, for difficulty and karts * Add a lap to oliver's math class * Replace Northern Resort by Volcano Island * Replace Volcano Island by Candela City Candela City was in no (official) GP before this. Also sets Green Valley to 3 laps. * Add Northern Resort and remove Fort Magma In 0.9.3, this GP has only 4 races in Story Mode (5 for the other GPs) because Fort Magma is locked. Of all the tracks outside this GP before, Northern Resort is one of the hardest, the AI being rather good there. * Temporary cup for SuperTux challenges Recolored version of the gold cup * Update challenge selection UI for the SuperTux challenges * GUI used before SuperTux difficulty unlocking This is the old select_challenge.stkgui * Swap the two sara * Replace Kiki by another kart to unlock on Benau's demand * Update for improved Story Mode * Update for improved Story Mode * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Update for SuperTux ; also adds the ability to unlock a challenge by points * Update for unlocking by points * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Minor changes to function calls * Update for SuperTux challenges * Add support for SuperTux challenges * Update for Story Mode GP changes * Allows to display the correct number of points for GP challenges * Set the unlock of the 1st bonus kart to correct non-test value * Add support for SuperTux challenges Including a bigger challenge selection diaolg * Add default value * Icon to indicate that there is an unlockable The number of points needed to unlock it are displayed next to it. * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Function for unlock by points UI * Add default for unlock list node and use requirements node for all * Make unlockByPoints simpler and more flexible Now the code will iterate in StoryModeStatus and send the unlock_list challenges for treatment here. The question of getting the right challenge statuses beings solved, it allows for a great simplification and much more flexibility * Update unlockByPoints declaration * Adds support for next unlockable UI * Improve call of unlockByPoints Also calculations for displaying in the UI how many point the next unlockable by points requires. * Add icon for next unlockable * Displays icon/number to make the player aware of the next unlockable Also displays the number below the icon rather than on the side, for more clarity. * Changes to display karts in the unlock scene * Update unlock functions declarations * New function to clarify code and more logical recently unlocked list management In the previous version, everything was added to the recently unlocked list at some point, necessitating a clearing at the end of computeActive, which also removed from the list the non-race challenges. Checking if the feature is newly unlocked to add it to the list remove the need of that clearing. * Declaration for unlockFeatureByList * Display newly unlocked karts * Display newly unlocked karts * Clear the list of recently unlocked features at the end * Update testing code * Update unlocks finding function call * Improve UI scaling * Fixes indentation * Update the number of points before checking for unlock by points * Add const to declarations * Remove const_cast * Remove a const_cast There are other const_cast in the menu debug items (but they are unrelated to this PR) * Fix menu being bolder
2018-04-29 18:27:03 -04:00
if (unlocked[n]->getChallengeId() == "fortmagma")
{
gameCompleted = true;
break;
}
}
if (gameCompleted)
{
// clear the race
// kart will no longer be available during cutscene, drop reference
StateManager::get()->getActivePlayer(playerID)->setKart(NULL);
PropertyAnimator::get()->clear();
World::deleteWorld();
2014-11-05 18:13:47 -05:00
CutsceneWorld::setUseDuration(true);
StateManager::get()->enterGameState();
race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
race_manager->setNumKarts(0);
race_manager->setNumPlayers(0);
race_manager->startSingleRace("endcutscene", 999, false);
std::vector<std::string> parts;
parts.push_back("endcutscene");
((CutsceneWorld*)World::getWorld())->setParts(parts);
}
else
{
2014-06-07 19:51:34 -04:00
StateManager::get()->popMenu();
PropertyAnimator::get()->clear();
2014-06-07 19:51:34 -04:00
World::deleteWorld();
CutsceneWorld::setUseDuration(false);
2014-06-07 19:51:34 -04:00
StateManager::get()->enterGameState();
race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
race_manager->setNumKarts(0);
race_manager->setNumPlayers(0);
race_manager->startSingleRace("featunlocked", 999, race_manager->raceWasStartedFromOverworld());
2014-06-07 19:51:34 -04:00
FeatureUnlockedCutScene* scene =
FeatureUnlockedCutScene::getInstance();
SuperTux in Story Mode (and other improvements) (#3207) * Add SuperTux difficulty & update number of karts Also make the expert challenge slightly easier to match more the difficulty of other challenges. * Add SuperTux difficulty & update number of karts & points required Also give some more time margin in easier difficulties, as it is a hard challenge compared to most. * Add SuperTux difficulty & update number of karts & points required Also change the lap count to 4 as it is a very short track (sub 30s) * Add SuperTux difficulty Also tweak the expert challenge to have a more appropriate difficulty * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also correct the requirement position, since this is not a FTL race anymore. * Add SuperTux difficulty & update number of karts & points required Also slight balancing improvements for the usual difficulties. * Add SuperTux difficulty & update number of karts & points required Also adds a position requirement in expert * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 5, as this is a very short track. The time requirements for easier difficulties have been kept proportionally similar to before. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4. * Add SuperTux difficulty & update number of karts & points required Also add a position requirement to expert and intermediate. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4, as a lap often is 30s or less in expert/supertux * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Rename islandtrack.challenge to gran_paradiso.challenge * Rename challenge file * Add SuperTux difficulty & update number of karts & points required Also makes the time limit in expert less easy and tweak position requirement. * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Doesn't unlock the SuperTux difficulty anymore - it's managed elsewhere. * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add new unlock challenges, for difficulty and karts * Add a lap to oliver's math class * Replace Northern Resort by Volcano Island * Replace Volcano Island by Candela City Candela City was in no (official) GP before this. Also sets Green Valley to 3 laps. * Add Northern Resort and remove Fort Magma In 0.9.3, this GP has only 4 races in Story Mode (5 for the other GPs) because Fort Magma is locked. Of all the tracks outside this GP before, Northern Resort is one of the hardest, the AI being rather good there. * Temporary cup for SuperTux challenges Recolored version of the gold cup * Update challenge selection UI for the SuperTux challenges * GUI used before SuperTux difficulty unlocking This is the old select_challenge.stkgui * Swap the two sara * Replace Kiki by another kart to unlock on Benau's demand * Update for improved Story Mode * Update for improved Story Mode * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Update for SuperTux ; also adds the ability to unlock a challenge by points * Update for unlocking by points * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Minor changes to function calls * Update for SuperTux challenges * Add support for SuperTux challenges * Update for Story Mode GP changes * Allows to display the correct number of points for GP challenges * Set the unlock of the 1st bonus kart to correct non-test value * Add support for SuperTux challenges Including a bigger challenge selection diaolg * Add default value * Icon to indicate that there is an unlockable The number of points needed to unlock it are displayed next to it. * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Function for unlock by points UI * Add default for unlock list node and use requirements node for all * Make unlockByPoints simpler and more flexible Now the code will iterate in StoryModeStatus and send the unlock_list challenges for treatment here. The question of getting the right challenge statuses beings solved, it allows for a great simplification and much more flexibility * Update unlockByPoints declaration * Adds support for next unlockable UI * Improve call of unlockByPoints Also calculations for displaying in the UI how many point the next unlockable by points requires. * Add icon for next unlockable * Displays icon/number to make the player aware of the next unlockable Also displays the number below the icon rather than on the side, for more clarity. * Changes to display karts in the unlock scene * Update unlock functions declarations * New function to clarify code and more logical recently unlocked list management In the previous version, everything was added to the recently unlocked list at some point, necessitating a clearing at the end of computeActive, which also removed from the list the non-race challenges. Checking if the feature is newly unlocked to add it to the list remove the need of that clearing. * Declaration for unlockFeatureByList * Display newly unlocked karts * Display newly unlocked karts * Clear the list of recently unlocked features at the end * Update testing code * Update unlocks finding function call * Improve UI scaling * Fixes indentation * Update the number of points before checking for unlock by points * Add const to declarations * Remove const_cast * Remove a const_cast There are other const_cast in the menu debug items (but they are unrelated to this PR) * Fix menu being bolder
2018-04-29 18:27:03 -04:00
scene->addTrophy(race_manager->getDifficulty(),false);
scene->findWhatWasUnlocked(race_manager->getDifficulty(),unlocked);
scene->push();
race_manager->setAIKartOverride("");
2014-06-07 19:51:34 -04:00
std::vector<std::string> parts;
parts.push_back("featunlocked");
((CutsceneWorld*)World::getWorld())->setParts(parts);
}
SuperTux in Story Mode (and other improvements) (#3207) * Add SuperTux difficulty & update number of karts Also make the expert challenge slightly easier to match more the difficulty of other challenges. * Add SuperTux difficulty & update number of karts & points required Also give some more time margin in easier difficulties, as it is a hard challenge compared to most. * Add SuperTux difficulty & update number of karts & points required Also change the lap count to 4 as it is a very short track (sub 30s) * Add SuperTux difficulty Also tweak the expert challenge to have a more appropriate difficulty * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also correct the requirement position, since this is not a FTL race anymore. * Add SuperTux difficulty & update number of karts & points required Also slight balancing improvements for the usual difficulties. * Add SuperTux difficulty & update number of karts & points required Also adds a position requirement in expert * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 5, as this is a very short track. The time requirements for easier difficulties have been kept proportionally similar to before. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4. * Add SuperTux difficulty & update number of karts & points required Also add a position requirement to expert and intermediate. * Add SuperTux difficulty & update number of karts & points required Also change the number of laps to 4, as a lap often is 30s or less in expert/supertux * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Rename islandtrack.challenge to gran_paradiso.challenge * Rename challenge file * Add SuperTux difficulty & update number of karts & points required Also makes the time limit in expert less easy and tweak position requirement. * Add SuperTux difficulty & update number of karts * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required Doesn't unlock the SuperTux difficulty anymore - it's managed elsewhere. * Add SuperTux difficulty & update number of karts & points required * Add SuperTux difficulty & update number of karts & points required * Add new unlock challenges, for difficulty and karts * Add a lap to oliver's math class * Replace Northern Resort by Volcano Island * Replace Volcano Island by Candela City Candela City was in no (official) GP before this. Also sets Green Valley to 3 laps. * Add Northern Resort and remove Fort Magma In 0.9.3, this GP has only 4 races in Story Mode (5 for the other GPs) because Fort Magma is locked. Of all the tracks outside this GP before, Northern Resort is one of the hardest, the AI being rather good there. * Temporary cup for SuperTux challenges Recolored version of the gold cup * Update challenge selection UI for the SuperTux challenges * GUI used before SuperTux difficulty unlocking This is the old select_challenge.stkgui * Swap the two sara * Replace Kiki by another kart to unlock on Benau's demand * Update for improved Story Mode * Update for improved Story Mode * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Update for SuperTux ; also adds the ability to unlock a challenge by points * Update for unlocking by points * Add support for SuperTux challenges * Add support for SuperTux challenges * Add support for SuperTux challenges * Minor changes to function calls * Update for SuperTux challenges * Add support for SuperTux challenges * Update for Story Mode GP changes * Allows to display the correct number of points for GP challenges * Set the unlock of the 1st bonus kart to correct non-test value * Add support for SuperTux challenges Including a bigger challenge selection diaolg * Add default value * Icon to indicate that there is an unlockable The number of points needed to unlock it are displayed next to it. * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Changed format : the point requirements is now specified in the file * Function for unlock by points UI * Add default for unlock list node and use requirements node for all * Make unlockByPoints simpler and more flexible Now the code will iterate in StoryModeStatus and send the unlock_list challenges for treatment here. The question of getting the right challenge statuses beings solved, it allows for a great simplification and much more flexibility * Update unlockByPoints declaration * Adds support for next unlockable UI * Improve call of unlockByPoints Also calculations for displaying in the UI how many point the next unlockable by points requires. * Add icon for next unlockable * Displays icon/number to make the player aware of the next unlockable Also displays the number below the icon rather than on the side, for more clarity. * Changes to display karts in the unlock scene * Update unlock functions declarations * New function to clarify code and more logical recently unlocked list management In the previous version, everything was added to the recently unlocked list at some point, necessitating a clearing at the end of computeActive, which also removed from the list the non-race challenges. Checking if the feature is newly unlocked to add it to the list remove the need of that clearing. * Declaration for unlockFeatureByList * Display newly unlocked karts * Display newly unlocked karts * Clear the list of recently unlocked features at the end * Update testing code * Update unlocks finding function call * Improve UI scaling * Fixes indentation * Update the number of points before checking for unlock by points * Add const to declarations * Remove const_cast * Remove a const_cast There are other const_cast in the menu debug items (but they are unrelated to this PR) * Fix menu being bolder
2018-04-29 18:27:03 -04:00
PlayerManager::getCurrentPlayer()->clearUnlocked();
return;
}
2017-10-25 20:16:36 -04:00
Log::warn("RaceResultGUI", "Incorrect event '%s' when things are unlocked.",
name.c_str());
}
// Next check for GP
// -----------------
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
if (name == "middle") // Next GP
{
cleanupGPProgress();
StateManager::get()->popMenu();
race_manager->next();
}
else if (name == "bottom") // Abort
{
new MessageDialog(_("Do you really want to abort the Grand Prix?"),
MessageDialog::MESSAGE_DIALOG_CONFIRM, this, false);
}
else if (!getWidget(name.c_str())->isVisible())
{
Log::warn("RaceResultGUI", "Incorrect event '%s' when things are unlocked.",
name.c_str());
}
return;
}
StateManager::get()->popMenu();
if (name == "top") // Setup new race
{
Numerous improvements related to ghost replays (#3244) * Update GUI files for replay improvements * Updated replay capabilities * Improve timer formatting possibilities Mainly, the ability to choose to display or not minutes, and to choose how many digits are shown after seconds (from 0 : second as smallest timestep - to 3 : ms as smallest timestep) Also displays "mm:ss.ms" rather than "mm:ss:ms". * Some new list widget possibilities Like the ability to update the header while the list is displayed (useful to add or remove columns) * Update ghost kart for the new replay data * Also update the ghost controller * Live differences with ghost replays in linear worlds * Replay-related UI changes Big changes to the replay selection screen, and small change to the race UI (add the live timer in ghost races) and the race result UI (add the option to directly race against a newly saved ghost). * Improves the replay action modal dialog * Fix time-to-ticks regression * Several requested improvements * Improved ghost icon Also updates the license * GUI changes and improvements to accomodate multi-mode support * Additional recorded data * More functions to get the current race state * Update replay variables to match what is used * Updated replay-related config values * Add ghost kart support to easter egg hunt mode * Transparent attachments for ghost karts * Use new stored data (color, item type) Also : - Interpolate speed for smoother display in watch-only mode - Coding style improvements * Fix coding style issues and add UI support for modes * Fix coding style issues & support for easter egg mode * Remove leftover prints * Use getDifficultyName to remove hardcoded values * Fix attach_ticks and coding style fixes * Make the position of the timers fully relative Otherwise, they would tend to touch each other in some resolutions Also add a comment about the 59.9995f * Make the list filling code clearer Also use getDifficultyName Most of the line changes shown by git correspond to moving around some bits or adjusting indentation. * Remove a TODO as requested * Fix dialog being dismissed too soon * Remove a fixme * Small clean up * Fix logging * Partial #3249 fix
2018-05-13 20:51:52 -04:00
// Save current race data for race against new ghost
std::string track_name = race_manager->getTrackName();
int laps = race_manager->getNumLaps();
bool reverse = race_manager->getReverseTrack();
bool new_ghost_race = race_manager->isRecordingRace();
race_manager->exitRace();
race_manager->setAIKartOverride("");
//If pressing continue quickly in a losing challenge
if (race_manager->raceWasStartedFromOverworld())
{
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
OverWorld::enterOverWorld();
}
Numerous improvements related to ghost replays (#3244) * Update GUI files for replay improvements * Updated replay capabilities * Improve timer formatting possibilities Mainly, the ability to choose to display or not minutes, and to choose how many digits are shown after seconds (from 0 : second as smallest timestep - to 3 : ms as smallest timestep) Also displays "mm:ss.ms" rather than "mm:ss:ms". * Some new list widget possibilities Like the ability to update the header while the list is displayed (useful to add or remove columns) * Update ghost kart for the new replay data * Also update the ghost controller * Live differences with ghost replays in linear worlds * Replay-related UI changes Big changes to the replay selection screen, and small change to the race UI (add the live timer in ghost races) and the race result UI (add the option to directly race against a newly saved ghost). * Improves the replay action modal dialog * Fix time-to-ticks regression * Several requested improvements * Improved ghost icon Also updates the license * GUI changes and improvements to accomodate multi-mode support * Additional recorded data * More functions to get the current race state * Update replay variables to match what is used * Updated replay-related config values * Add ghost kart support to easter egg hunt mode * Transparent attachments for ghost karts * Use new stored data (color, item type) Also : - Interpolate speed for smoother display in watch-only mode - Coding style improvements * Fix coding style issues and add UI support for modes * Fix coding style issues & support for easter egg mode * Remove leftover prints * Use getDifficultyName to remove hardcoded values * Fix attach_ticks and coding style fixes * Make the position of the timers fully relative Otherwise, they would tend to touch each other in some resolutions Also add a comment about the 59.9995f * Make the list filling code clearer Also use getDifficultyName Most of the line changes shown by git correspond to moving around some bits or adjusting indentation. * Remove a TODO as requested * Fix dialog being dismissed too soon * Remove a fixme * Small clean up * Fix logging * Partial #3249 fix
2018-05-13 20:51:52 -04:00
// Special case : race against a newly saved ghost
else if (new_ghost_race)
{
ReplayPlay::get()->loadAllReplayFile();
unsigned long long int last_uid = ReplayRecorder::get()->getLastUID();
ReplayPlay::get()->setReplayFileByUID(last_uid);
race_manager->setRecordRace(true);
race_manager->setRaceGhostKarts(true);
race_manager->setNumKarts(race_manager->getNumLocalPlayers());
// Disable accidentally unlocking of a challenge
PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
race_manager->setReverseTrack(reverse);
race_manager->startSingleRace(track_name, laps, false);
}
else
{
Screen* newStack[] = { MainMenuScreen::getInstance(),
RaceSetupScreen::getInstance(),
NULL };
StateManager::get()->resetAndSetStack(newStack);
}
}
else if (name == "middle") // Restart
{
race_manager->rerunRace();
}
else if (name == "bottom") // Back to main
{
race_manager->exitRace();
race_manager->setAIKartOverride("");
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
if (race_manager->raceWasStartedFromOverworld())
{
OverWorld::enterOverWorld();
}
}
else
2017-10-25 20:19:46 -04:00
Log::warn("RaceResultGUI", "Incorrect event '%s' for normal race.",
name.c_str());
return;
} // eventCallback
//-----------------------------------------------------------------------------
/** Sets up the gui to go back to the lobby. Can only be called in case of a
* networked game.
*/
void RaceResultGUI::backToLobby()
{
2018-04-21 04:00:14 -04:00
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX &&
race_manager->getTrackNumber() == race_manager->getNumOfTracks() - 1)
{
core::stringw msg = _("Network grand prix has been finished.");
MessageQueue::add(MessageQueue::MT_ACHIEVEMENT, msg);
}
race_manager->clearNetworkGrandPrixResult();
race_manager->exitRace();
race_manager->setAIKartOverride("");
GUIEngine::ModalDialog::dismiss();
2018-04-21 04:00:14 -04:00
cleanupGPProgress();
StateManager::get()->resetAndSetStack(
NetworkConfig::get()->getResetScreens(true/*lobby*/).data());
NetworkingLobby::getInstance()->addMoreServerInfo(L"--------------------");
} // backToLobby
2018-08-17 00:54:38 -04:00
//-----------------------------------------------------------------------------
void RaceResultGUI::displayCTFResults()
{
#ifndef SERVER_ONLY
//Draw win text
core::stringw result_text;
video::SColor color = video::SColor(255, 255, 255, 255);
video::SColor red_color = video::SColor(255, 255, 0, 0);
gui::IGUIFont* font = GUIEngine::getTitleFont();
int current_x = UserConfigParams::m_width / 2;
RowInfo *ri = &(m_all_row_infos[0]);
int current_y = (int)ri->m_y_pos;
CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>(World::getWorld());
const int red_score = ctf->getRedScore();
const int blue_score = ctf->getBlueScore();
GUIEngine::Widget *table_area = getWidget("result-table");
int height = table_area->m_h + table_area->m_y;
if (red_score > blue_score)
result_text = _("Red Team Wins");
else if (blue_score > red_score)
result_text = _("Blue Team Wins");
else
result_text = _("It's a draw");
core::rect<s32> pos(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, true);
core::dimension2du rect = font->getDimension(result_text.c_str());
//Draw team scores:
current_y += rect.Height;
current_x /= 2;
irr::video::ITexture* red_icon = irr_driver->getTexture(FileManager::GUI_ICON,
2018-08-17 00:54:38 -04:00
"red_flag.png");
irr::video::ITexture* blue_icon = irr_driver->getTexture(FileManager::GUI_ICON,
2018-08-17 00:54:38 -04:00
"blue_flag.png");
core::recti source_rect(core::vector2di(0, 0), red_icon->getSize());
core::recti dest_rect(current_x, current_y,
current_x + red_icon->getSize().Width / 2,
current_y + red_icon->getSize().Height / 2);
draw2DImage(red_icon, dest_rect, source_rect,
NULL, NULL, true);
current_x += UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
dest_rect = core::recti(current_x, current_y,
current_x + red_icon->getSize().Width / 2,
current_y + red_icon->getSize().Height / 2);
draw2DImage(blue_icon, dest_rect, source_rect,
NULL, NULL, true);
result_text = StringUtils::toWString(blue_score);
rect = font->getDimension(result_text.c_str());
current_x += red_icon->getSize().Width / 4;
current_y += red_icon->getSize().Height / 2 + rect.Height / 4;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, false);
current_x -= UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
result_text = StringUtils::toWString(red_score);
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, false);
int center_x = UserConfigParams::m_width / 2;
pos = core::rect<s32>(center_x, current_y, center_x, current_y);
font->draw("-", pos, color, true, false);
// The red team player scores:
current_y += rect.Height / 2 + rect.Height / 4;
font = GUIEngine::getSmallFont();
irr::video::ITexture* kart_icon;
int prev_y = current_y;
const unsigned num_karts = ctf->getNumKarts();
for (unsigned int i = 0; i < num_karts; i++)
{
AbstractKart* kart = ctf->getKartAtPosition(i + 1);
unsigned kart_id = kart->getWorldKartId();
if (ctf->getKartTeam(kart_id) != KART_TEAM_RED)
continue;
result_text = kart->getController()->getName();
result_text.append(" ");
if (kart->isEliminated())
{
result_text.append(_("Eliminated"));
}
else
{
result_text.append(
StringUtils::toWString(ctf->getKartScore(kart_id)));
}
rect = font->getDimension(result_text.c_str());
current_y += rect.Height;
if (current_y > height) break;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text, pos,
kart->getController()->isLocalPlayerController() ?
red_color : color, true, false);
kart_icon = kart->getKartProperties()->getIconMaterial()->getTexture();
source_rect = core::recti(core::vector2di(0, 0), kart_icon->getSize());
irr::u32 offset_x =
(irr::u32)(font->getDimension(result_text.c_str()).Width / 1.5f);
dest_rect = core::recti(current_x - offset_x - 30, current_y,
current_x - offset_x, current_y + 30);
draw2DImage(kart_icon, dest_rect, source_rect, NULL, NULL, true);
}
// The blue team player scores:
current_y = prev_y;
current_x += UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
for (unsigned int i = 0; i < num_karts; i++)
{
AbstractKart* kart = ctf->getKartAtPosition(i + 1);
unsigned kart_id = kart->getWorldKartId();
if (ctf->getKartTeam(kart_id) != KART_TEAM_BLUE)
continue;
result_text = kart->getController()->getName();
result_text.append(" ");
if (kart->isEliminated())
{
result_text.append(_("Eliminated"));
}
else
{
result_text.append(
StringUtils::toWString(ctf->getKartScore(kart_id)));
}
rect = font->getDimension(result_text.c_str());
current_y += rect.Height;
if (current_y > height) break;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text, pos,
kart->getController()->isLocalPlayerController() ?
red_color : color, true, false);
kart_icon = kart->getKartProperties()->getIconMaterial()->getTexture();
source_rect = core::recti(core::vector2di(0, 0), kart_icon->getSize());
irr::u32 offset_x = (irr::u32)
(font->getDimension(result_text.c_str()).Width / 1.5f);
dest_rect = core::recti(current_x - offset_x - 30, current_y,
current_x - offset_x, current_y + 30);
draw2DImage(kart_icon, dest_rect, source_rect, NULL, NULL, true);
}
#endif
}
//-----------------------------------------------------------------------------
void RaceResultGUI::onConfirm()
{
//race_manager->saveGP(); // Save the aborted GP
GUIEngine::ModalDialog::dismiss();
cleanupGPProgress();
StateManager::get()->popMenu();
race_manager->exitRace();
race_manager->setAIKartOverride("");
StateManager::get()->resetAndGoToScreen(
MainMenuScreen::getInstance());
if (race_manager->raceWasStartedFromOverworld())
{
OverWorld::enterOverWorld();
}
}
//-----------------------------------------------------------------------------
/** This determines the layout, i.e. the size of all columns, font size etc.
*/
void RaceResultGUI::determineTableLayout()
{
GUIEngine::Widget *table_area = getWidget("result-table");
m_font = GUIEngine::getFont();
assert(m_font);
2016-07-18 11:00:40 -04:00
//m_was_monospace = m_font->getMonospaceDigits();
//m_font->setMonospaceDigits(true);
WorldWithRank *rank_world = (WorldWithRank*)World::getWorld();
unsigned int first_position = 1;
unsigned int sta = race_manager->getNumSpareTireKarts();
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
first_position = 2;
// Use only the karts that are supposed to be displayed (and
// ignore e.g. the leader in a FTL race).
2016-10-07 02:39:39 -04:00
unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1 - sta;
// In FTL races the leader kart is not displayed
m_all_row_infos.resize(num_karts);
// Determine the kart to display in the right order,
// and the maximum width for the kart name column
// -------------------------------------------------
m_width_kart_name = 0;
float max_finish_time = 0;
2018-08-12 12:59:20 -04:00
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
int time_precision = race_manager->currentModeTimePrecision();
for (unsigned int position = first_position;
2016-10-07 02:39:39 -04:00
position <= race_manager->getNumberOfKarts() - sta; position++)
{
const AbstractKart *kart = rank_world->getKartAtPosition(position);
// Save a pointer to the current row_info entry
RowInfo *ri = &(m_all_row_infos[position - first_position]);
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_kart_name = kart->getController()->getName();
video::ITexture *icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
ri->m_kart_icon = icon;
// FTL karts will get a time assigned, they are not shown as eliminated
if (kart->isEliminated() &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
ri->m_finish_time_string = core::stringw(_("Eliminated"));
}
2018-08-12 12:59:20 -04:00
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL ||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
{
assert(ffa);
ri->m_finish_time_string =
StringUtils::toWString(ffa->getKartScore(kart->getWorldKartId()));
}
else
{
const float time = kart->getFinishTime();
if (time > max_finish_time) max_finish_time = time;
std::string time_string = StringUtils::timeToString(time, time_precision);
ri->m_finish_time_string = time_string.c_str();
}
core::dimension2du rect =
m_font->getDimension(ri->m_kart_name.c_str());
if (rect.Width > m_width_kart_name)
m_width_kart_name = rect.Width;
} // for position
std::string max_time = StringUtils::timeToString(max_finish_time, time_precision);
core::stringw string_max_time(max_time.c_str());
core::dimension2du r = m_font->getDimension(string_max_time.c_str());
m_width_finish_time = r.Width;
// Top pixel where to display text
m_top = table_area->m_y;
// Height of the result display
unsigned int height = table_area->m_h;
// Setup different timing information for the different phases
// -----------------------------------------------------------
// How much time between consecutive rows
m_time_between_rows = 0.1f;
// How long it takes for one line to scroll from right to left
m_time_single_scroll = 0.2f;
// Time to rotate the entries to the proper GP position.
m_time_rotation = 1.0f;
// The time the first phase is being displayed: add the start time
// of the last kart to the duration of the scroll plus some time
// of rest before the next phase starts
m_time_overall_scroll = (num_karts - 1)*m_time_between_rows
+ m_time_single_scroll + 2.0f;
// The time to increase the number of points.
m_time_for_points = 1.0f;
// Determine text height
r = m_font->getDimension(L"Y");
m_distance_between_rows = (int)(1.5f*r.Height);
// If there are too many karts, reduce size between rows
if (m_distance_between_rows * num_karts > height)
m_distance_between_rows = height / num_karts;
m_width_icon = table_area->m_h / 18;
m_width_column_space = 10;
// Determine width of new points column
2016-07-18 11:00:40 -04:00
//m_font->setMonospaceDigits(true);
core::dimension2du r_new_p = m_font->getDimension(L"+99");
m_width_new_points = r_new_p.Width;
// Determine width of overall points column
core::dimension2du r_all_p = m_font->getDimension(L"999");
2016-07-18 11:00:40 -04:00
//m_font->setMonospaceDigits(false);
m_width_all_points = r_all_p.Width;
m_table_width = m_width_icon + m_width_column_space
+ m_width_kart_name;
if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
m_table_width += m_width_finish_time + m_width_column_space;
// Only in GP mode are the points displayed.
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
m_table_width += m_width_new_points + m_width_all_points
+ 2 * m_width_column_space;
m_leftmost_column = table_area->m_x;
} // determineTableLayout
//-----------------------------------------------------------------------------
/** This function is called when one of the player presses 'fire'. The next
* phase of the animation will be displayed. E.g.
* in a GP: pressing fire while/after showing the latest race result will
* start the animation for the current GP result
* in a normal race: when pressing fire while an animation is played,
* start the menu showing 'rerun, new race, back to main' etc.
*/
void RaceResultGUI::nextPhase()
{
// This will trigger the next phase in the next render call.
m_timer = 9999;
} // nextPhase
//-----------------------------------------------------------------------------
/** If escape is pressed, don't do the default option (close the screen), but
* advance to the next animation phase.
*/
bool RaceResultGUI::onEscapePressed()
{
nextPhase();
return false; // indicates 'do not close'
} // onEscapePressed
//-----------------------------------------------------------------------------
/** This is called before an event is sent to a widget. Since in this case
* no widget is active, the event would be lost, so we act on fire events
* here and trigger the next phase.
*/
GUIEngine::EventPropagation RaceResultGUI::filterActions(PlayerAction action,
int deviceID,
const unsigned int value,
Input::InputType type,
int playerId)
{
if (action != PA_FIRE) return GUIEngine::EVENT_LET;
// If the buttons are already visible, let the event go through since
// it will be triggering eventCallback where this is handles.
if (m_animation_state == RR_WAIT_TILL_END) return GUIEngine::EVENT_LET;
nextPhase();
return GUIEngine::EVENT_BLOCK;
} // filterActions
//-----------------------------------------------------------------------------
/** Called once a frame, this now triggers the rendering of the actual
* race result gui.
*/
void RaceResultGUI::onUpdate(float dt)
{
renderGlobal(dt);
// When the finish sound has been played, start the race over music.
if (m_finish_sound && m_finish_sound->getStatus() != SFXBase::SFX_PLAYING)
{
try
{
// This call is done once each frame, but startMusic() is cheap
// if the music is already playing.
music_manager->startMusic(m_race_over_music);
}
catch (std::exception& e)
{
Log::error("RaceResultGUI", "Exception caught when "
"trying to load music: %s", e.what());
}
}
} // onUpdate
//-----------------------------------------------------------------------------
/** Render all global parts of the race gui, i.e. things that are only
* displayed once even in splitscreen.
* \param dt Timestep sized.
*/
void RaceResultGUI::renderGlobal(float dt)
{
#ifndef SERVER_ONLY
m_timer += dt;
assert(World::getWorld()->getPhase() == WorldStatus::RESULT_DISPLAY_PHASE);
unsigned int num_karts = (unsigned int)m_all_row_infos.size();
// First: Update the finite state machine
// ======================================
switch (m_animation_state)
{
case RR_INIT:
for (unsigned int i = 0; i < num_karts; i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
ri->m_start_at = m_time_between_rows * i;
ri->m_x_pos = (float)UserConfigParams::m_width;
ri->m_y_pos = (float)(m_top + i*m_distance_between_rows);
}
m_animation_state = RR_RACE_RESULT;
break;
case RR_RACE_RESULT:
if (m_timer > m_time_overall_scroll)
{
// Make sure that all lines are aligned to the left
// (in case that the animation was skipped).
for (unsigned int i = 0; i < num_karts; i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
ri->m_x_pos = (float)m_leftmost_column;
}
if (race_manager->getMajorMode() !=
RaceManager::MAJOR_MODE_GRAND_PRIX)
{
m_animation_state = RR_WAIT_TILL_END;
enableAllButtons();
break;
}
determineGPLayout();
m_animation_state = RR_OLD_GP_RESULTS;
m_timer = 0;
}
break;
case RR_OLD_GP_RESULTS:
if (m_timer > m_time_overall_scroll)
{
m_animation_state = RR_INCREASE_POINTS;
m_timer = 0;
for (unsigned int i = 0; i < num_karts; i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
ri->m_x_pos = (float)m_leftmost_column;
}
}
break;
case RR_INCREASE_POINTS:
// Have one second delay before the resorting starts.
if (m_timer > 1 + m_time_for_points)
{
m_animation_state = RR_RESORT_TABLE;
if (m_gp_position_was_changed)
m_timer = 0;
else
// This causes the phase to go to RESORT_TABLE once, and then
// immediately wait till end. This has the advantage that any
// phase change settings will be processed properly.
m_timer = m_time_rotation + 1;
// Make the new row permanent; necessary in case
// that the animation is skipped.
for (unsigned int i = 0; i < num_karts; i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
ri->m_new_points = 0;
ri->m_current_displayed_points =
(float)ri->m_new_overall_points;
}
}
break;
case RR_RESORT_TABLE:
if (m_timer > m_time_rotation)
{
m_animation_state = RR_WAIT_TILL_END;
// Make the new row permanent.
for (unsigned int i = 0; i < num_karts; i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
ri->m_y_pos = ri->m_centre_point - ri->m_radius;
}
enableAllButtons();
}
break;
case RR_WAIT_TILL_END:
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
displayGPProgress();
if (m_timer - m_time_rotation > 1.0f &&
dynamic_cast<DemoWorld*>(World::getWorld()))
{
race_manager->exitRace();
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
}
break;
} // switch
// Second phase: update X and Y positions for the various animations
// =================================================================
float v = 0.9f*UserConfigParams::m_width / m_time_single_scroll;
2018-08-17 00:54:38 -04:00
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
{
displaySoccerResults();
}
else if (race_manager->getMajorMode() ==
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
{
displayCTFResults();
}
else
{
for (unsigned int i = 0; i < m_all_row_infos.size(); i++)
{
RowInfo *ri = &(m_all_row_infos[i]);
float x = ri->m_x_pos;
float y = ri->m_y_pos;
switch (m_animation_state)
{
// Both states use the same scrolling:
case RR_INIT: break; // Remove compiler warning
case RR_RACE_RESULT:
case RR_OLD_GP_RESULTS:
if (m_timer > ri->m_start_at)
{ // if active
ri->m_x_pos -= dt*v;
if (ri->m_x_pos < m_leftmost_column)
ri->m_x_pos = (float)m_leftmost_column;
x = ri->m_x_pos;
}
break;
case RR_INCREASE_POINTS:
{
WorldWithRank *wwr = dynamic_cast<WorldWithRank*>(World::getWorld());
assert(wwr);
int most_points;
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
most_points = wwr->getScoreForPosition(2);
else
most_points = wwr->getScoreForPosition(1);
ri->m_current_displayed_points +=
dt*most_points / m_time_for_points;
if (ri->m_current_displayed_points > ri->m_new_overall_points)
{
ri->m_current_displayed_points =
(float)ri->m_new_overall_points;
}
ri->m_new_points -=
dt*most_points / m_time_for_points;
if (ri->m_new_points < 0)
ri->m_new_points = 0;
break;
}
case RR_RESORT_TABLE:
x = ri->m_x_pos
- ri->m_radius*sin(m_timer / m_time_rotation*M_PI);
y = ri->m_centre_point
+ ri->m_radius*cos(m_timer / m_time_rotation*M_PI);
break;
case RR_WAIT_TILL_END:
break;
} // switch
displayOneEntry((unsigned int)x, (unsigned int)y, i, true);
} // for i
}
// Display highscores
if (race_manager->getMajorMode() != RaceManager::MAJOR_MODE_GRAND_PRIX ||
m_animation_state == RR_RACE_RESULT)
{
displayPostRaceInfo();
}
#endif
} // renderGlobal
//-----------------------------------------------------------------------------
/** Determine the layout and fields for the GP table based on the previous
* GP results.
*/
void RaceResultGUI::determineGPLayout()
{
#ifndef SERVER_ONLY
unsigned int num_karts = race_manager->getNumberOfKarts();
std::vector<int> old_rank(num_karts, 0);
2018-08-20 20:51:04 -04:00
// Update the kart GP ranks
// This is useful, e.g., when continuing a saved GP.
race_manager->computeGPRanks();
int time_precision = race_manager->currentModeTimePrecision();
for (unsigned int kart_id = 0; kart_id < num_karts; kart_id++)
{
int rank = race_manager->getKartGPRank(kart_id);
// In case of FTL mode: ignore the leader
if (rank < 0) continue;
old_rank[kart_id] = rank;
const AbstractKart *kart = World::getWorld()->getKart(kart_id);
RowInfo *ri = &(m_all_row_infos[rank]);
ri->m_kart_icon =
kart->getKartProperties()->getIconMaterial()->getTexture();
ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_kart_name = kart->getController()->getName();
// In FTL karts do have a time, which is shown even when the kart
// is eliminated
if (kart->isEliminated() &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
ri->m_finish_time_string = core::stringw(_("Eliminated"));
}
else
{
float time = race_manager->getOverallTime(kart_id);
ri->m_finish_time_string
= StringUtils::timeToString(time, time_precision).c_str();
}
ri->m_start_at = m_time_between_rows * rank;
ri->m_x_pos = (float)UserConfigParams::m_width;
ri->m_y_pos = (float)(m_top + rank*m_distance_between_rows);
int p = race_manager->getKartPrevScore(kart_id);
ri->m_current_displayed_points = (float)p;
if (kart->isEliminated() &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
ri->m_new_points = 0;
}
else
{
WorldWithRank *wwr = dynamic_cast<WorldWithRank*>(World::getWorld());
assert(wwr);
ri->m_new_points =
(float)wwr->getScoreForPosition(kart->getPosition());
}
}
// Now update the GP ranks, and determine the new position
// -------------------------------------------------------
race_manager->computeGPRanks();
m_gp_position_was_changed = false;
for (unsigned int i = 0; i < num_karts; i++)
{
int j = old_rank[i];
int gp_position = race_manager->getKartGPRank(i);
m_gp_position_was_changed |= j != gp_position;
RowInfo *ri = &(m_all_row_infos[j]);
ri->m_radius = (j - gp_position)*(int)m_distance_between_rows*0.5f;
ri->m_centre_point = m_top + (gp_position + j)*m_distance_between_rows*0.5f;
int p = race_manager->getKartScore(i);
ri->m_new_overall_points = p;
} // i < num_karts
#endif
} // determineGPLayout
//-----------------------------------------------------------------------------
/** Displays the race results for a single kart.
* \param n Index of the kart to be displayed.
* \param display_points True if GP points should be displayed, too
*/
void RaceResultGUI::displayOneEntry(unsigned int x, unsigned int y,
unsigned int n, bool display_points)
{
#ifndef SERVER_ONLY
RowInfo *ri = &(m_all_row_infos[n]);
video::SColor color = ri->m_is_player_kart
? video::SColor(255, 255, 0, 0)
: video::SColor(255, 255, 255, 255);
unsigned int current_x = x;
// First draw the icon
// -------------------
if (ri->m_kart_icon)
{
core::recti source_rect(core::vector2di(0, 0),
ri->m_kart_icon->getSize());
core::recti dest_rect(current_x, y,
current_x + m_width_icon, y + m_width_icon);
draw2DImage(ri->m_kart_icon, dest_rect,
source_rect, NULL, NULL,
true);
}
current_x += m_width_icon + m_width_column_space;
// Draw the name
// -------------
core::recti pos_name(current_x, y,
UserConfigParams::m_width, y + m_distance_between_rows);
m_font->draw(ri->m_kart_name, pos_name, color, false, false, NULL,
true /* ignoreRTL */);
current_x += m_width_kart_name + m_width_column_space;
core::recti dest_rect = core::recti(current_x, y, current_x + 100, y + 10);
m_font->draw(ri->m_finish_time_string, dest_rect, color, false, false,
NULL, true /* ignoreRTL */);
current_x += m_width_finish_time + m_width_column_space;
// Only display points in GP mode and when the GP results are displayed.
// =====================================================================
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX &&
m_animation_state != RR_RACE_RESULT)
{
// Draw the new points
// -------------------
if (ri->m_new_points > 0)
{
core::recti dest_rect = core::recti(current_x, y,
current_x + 100, y + 10);
core::stringw point_string = core::stringw("+")
+ core::stringw((int)ri->m_new_points);
// With mono-space digits space has the same width as each digit,
// so we can simply fill up the string with spaces to get the
// right aligned.
while (point_string.size() < 3)
point_string = core::stringw(" ") + point_string;
m_font->draw(point_string, dest_rect, color, false, false, NULL,
true /* ignoreRTL */);
}
current_x += m_width_new_points + m_width_column_space;
// Draw the old_points plus increase value
// ---------------------------------------
core::recti dest_rect = core::recti(current_x, y, current_x + 100, y + 10);
core::stringw point_inc_string =
core::stringw((int)(ri->m_current_displayed_points));
while (point_inc_string.size() < 3)
point_inc_string = core::stringw(" ") + point_inc_string;
m_font->draw(point_inc_string, dest_rect, color, false, false, NULL,
true /* ignoreRTL */);
}
#endif
} // displayOneEntry
//-----------------------------------------------------------------------------
void RaceResultGUI::displaySoccerResults()
{
#ifndef SERVER_ONLY
//Draw win text
core::stringw result_text;
static video::SColor color = video::SColor(255, 255, 255, 255);
gui::IGUIFont* font = GUIEngine::getTitleFont();
int current_x = UserConfigParams::m_width / 2;
RowInfo *ri = &(m_all_row_infos[0]);
int current_y = (int)ri->m_y_pos;
SoccerWorld* sw = (SoccerWorld*)World::getWorld();
const int red_score = sw->getScore(KART_TEAM_RED);
const int blue_score = sw->getScore(KART_TEAM_BLUE);
GUIEngine::Widget *table_area = getWidget("result-table");
int height = table_area->m_h + table_area->m_y;
if (red_score > blue_score)
{
result_text = _("Red Team Wins");
}
else if (blue_score > red_score)
{
result_text = _("Blue Team Wins");
}
else
{
//Cannot really happen now. Only in time limited matches.
result_text = _("It's a draw");
}
core::rect<s32> pos(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, true);
core::dimension2du rect = font->getDimension(result_text.c_str());
//Draw team scores:
current_y += rect.Height;
current_x /= 2;
irr::video::ITexture* red_icon = irr_driver->getTexture(FileManager::GUI_ICON,
"soccer_ball_red.png");
irr::video::ITexture* blue_icon = irr_driver->getTexture(FileManager::GUI_ICON,
"soccer_ball_blue.png");
core::recti source_rect(core::vector2di(0, 0), red_icon->getSize());
core::recti dest_rect(current_x, current_y, current_x + red_icon->getSize().Width / 2,
current_y + red_icon->getSize().Height / 2);
draw2DImage(red_icon, dest_rect, source_rect,
NULL, NULL, true);
current_x += UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
dest_rect = core::recti(current_x, current_y, current_x + red_icon->getSize().Width / 2,
current_y + red_icon->getSize().Height / 2);
draw2DImage(blue_icon, dest_rect, source_rect,
NULL, NULL, true);
result_text = StringUtils::toWString(blue_score);
rect = font->getDimension(result_text.c_str());
current_x += red_icon->getSize().Width / 4;
current_y += red_icon->getSize().Height / 2 + rect.Height / 4;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, false);
current_x -= UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
result_text = StringUtils::toWString(red_score);
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text.c_str(), pos, color, true, false);
int center_x = UserConfigParams::m_width / 2;
pos = core::rect<s32>(center_x, current_y, center_x, current_y);
font->draw("-", pos, color, true, false);
//Draw goal scorers:
//The red scorers:
current_y += rect.Height / 2 + rect.Height / 4;
font = GUIEngine::getSmallFont();
std::vector<SoccerWorld::ScorerData> scorers = sw->getScorers(KART_TEAM_RED);
2016-12-30 19:59:24 -05:00
while (scorers.size() > 10)
{
scorers.erase(scorers.begin());
}
std::vector<float> score_times = sw->getScoreTimes(KART_TEAM_RED);
2016-12-30 20:03:42 -05:00
while (score_times.size() > 10)
{
score_times.erase(score_times.begin());
}
irr::video::ITexture* scorer_icon;
int prev_y = current_y;
for (unsigned int i = 0; i < scorers.size(); i++)
{
const bool own_goal = !(scorers.at(i).m_correct_goal);
const int kart_id = scorers.at(i).m_id;
2018-08-17 00:54:38 -04:00
result_text = sw->getKart(kart_id)->getController()->getName();
if (own_goal)
{
result_text.append(" ");
2016-05-05 20:37:05 -04:00
//I18N: indicates a player that scored in their own goal in result screen
result_text.append(_("(Own Goal)"));
}
result_text.append(" ");
result_text.append(StringUtils::timeToString(score_times.at(i)).c_str());
rect = font->getDimension(result_text.c_str());
if (height - prev_y < ((short)scorers.size() + 1)*(short)rect.Height)
current_y += (height - prev_y) / ((short)scorers.size() + 1);
else
current_y += rect.Height;
if (current_y > height) break;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text, pos, (own_goal ?
video::SColor(255, 255, 0, 0) : color), true, false);
scorer_icon = sw->getKart(scorers.at(i).m_id)
->getKartProperties()->getIconMaterial()->getTexture();
source_rect = core::recti(core::vector2di(0, 0), scorer_icon->getSize());
irr::u32 offset_x = (irr::u32)(font->getDimension(result_text.c_str()).Width / 1.5f);
dest_rect = core::recti(current_x - offset_x - 30, current_y, current_x - offset_x, current_y + 30);
draw2DImage(scorer_icon, dest_rect, source_rect,
NULL, NULL, true);
}
//The blue scorers:
current_y = prev_y;
current_x += UserConfigParams::m_width / 2 - red_icon->getSize().Width / 2;
scorers = sw->getScorers(KART_TEAM_BLUE);
2016-12-30 19:59:24 -05:00
while (scorers.size() > 10)
{
scorers.erase(scorers.begin());
}
score_times = sw->getScoreTimes(KART_TEAM_BLUE);
2016-12-30 20:03:42 -05:00
while (score_times.size() > 10)
{
score_times.erase(score_times.begin());
}
for (unsigned int i = 0; i < scorers.size(); i++)
{
const bool own_goal = !(scorers.at(i).m_correct_goal);
const int kart_id = scorers.at(i).m_id;
2018-08-17 00:54:38 -04:00
result_text = sw->getKart(kart_id)->getController()->getName();
if (own_goal)
{
result_text.append(" ");
result_text.append(_("(Own Goal)"));
}
result_text.append(" ");
result_text.append(StringUtils::timeToString(score_times.at(i)).c_str());
rect = font->getDimension(result_text.c_str());
if (height - prev_y < ((short)scorers.size() + 1)*(short)rect.Height)
current_y += (height - prev_y) / ((short)scorers.size() + 1);
else
current_y += rect.Height;
if (current_y > height) break;
pos = core::rect<s32>(current_x, current_y, current_x, current_y);
font->draw(result_text, pos, (own_goal ?
video::SColor(255, 255, 0, 0) : color), true, false);
scorer_icon = sw->getKart(scorers.at(i).m_id)->
getKartProperties()->getIconMaterial()->getTexture();
source_rect = core::recti(core::vector2di(0, 0), scorer_icon->getSize());
irr::u32 offset_x = (irr::u32)(font->getDimension(result_text.c_str()).Width / 1.5f);
dest_rect = core::recti(current_x - offset_x - 30, current_y, current_x - offset_x, current_y + 30);
draw2DImage(scorer_icon, dest_rect, source_rect,
NULL, NULL, true);
}
#endif
}
//-----------------------------------------------------------------------------
void RaceResultGUI::clearHighscores()
{
m_highscore_rank = 0;
} // clearHighscores
//-----------------------------------------------------------------------------
void RaceResultGUI::setHighscore(int rank)
{
m_highscore_rank = rank;
} // setHighscore
// ----------------------------------------------------------------------------
void RaceResultGUI::enableGPProgress()
{
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
GUIEngine::Widget* result_table = getWidget("result-table");
assert(result_table != NULL);
int currentTrack = race_manager->getTrackNumber();
int font_height = getFontHeight();
int w = (int)(UserConfigParams::m_width*0.17);
int x = (int)(result_table->m_x + result_table->m_w - w - 15);
int y = (m_top + font_height + 5);
//Current progress
GUIEngine::LabelWidget* status_label = new GUIEngine::LabelWidget();
status_label->m_properties[GUIEngine::PROP_ID] = "status_label";
status_label->m_properties[GUIEngine::PROP_TEXT_ALIGN] = "center";
status_label->m_x = x;
status_label->m_y = y;
status_label->m_w = w;
status_label->m_h = font_height;
status_label->add();
status_label->setText(_("Track %i/%i", currentTrack + 1,
race_manager->getGrandPrix().getNumberOfTracks()), true);
addGPProgressWidget(status_label);
y = (status_label->m_y + status_label->m_h + 5);
//Scroll up button
GUIEngine::IconButtonWidget* up_button = new GUIEngine::IconButtonWidget(
GUIEngine::IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
false, false, GUIEngine::IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
up_button->m_properties[GUIEngine::PROP_ID] = "up_button";
up_button->m_x = x;
up_button->m_y = y;
up_button->m_w = w;
up_button->m_h = font_height;
up_button->add();
up_button->setImage(file_manager->getAsset(FileManager::GUI_ICON, "scroll_up.png"));
addGPProgressWidget(up_button);
y = (up_button->m_y + up_button->m_h + SSHOT_SEPARATION);
//Track screenshots and labels
int n_sshot = 1;
for (int i = m_start_track; i < m_end_track; i++)
{
//Screenshot
GUIEngine::IconButtonWidget* screenshot_widget =
new GUIEngine::IconButtonWidget(
GUIEngine::IconButtonWidget::
SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
false, false,
GUIEngine::IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
screenshot_widget->setCustomAspectRatio(4.0f / 3.0f);
screenshot_widget->m_x = x;
screenshot_widget->m_y = y;
screenshot_widget->m_w = w;
screenshot_widget->m_h = m_sshot_height;
screenshot_widget->m_properties[GUIEngine::PROP_ID] =
("sshot_" + StringUtils::toString(n_sshot));
screenshot_widget->add();
addGPProgressWidget(screenshot_widget);
//Label
GUIEngine::LabelWidget* sshot_label = new GUIEngine::LabelWidget();
sshot_label->m_properties[GUIEngine::PROP_ID] =
("sshot_label_" + StringUtils::toString(n_sshot));
sshot_label->m_properties[GUIEngine::PROP_TEXT_ALIGN] = "left";
sshot_label->m_x = (x + w + 5);
sshot_label->m_y = (y + (m_sshot_height / 2) - (font_height / 2));
sshot_label->m_w = (w / 2);
sshot_label->m_h = font_height;
sshot_label->add();
addGPProgressWidget(sshot_label);
y += (m_sshot_height + SSHOT_SEPARATION);
n_sshot++;
} // for
displayScreenShots();
//Scroll down button
GUIEngine::IconButtonWidget* down_button = new GUIEngine::IconButtonWidget(
GUIEngine::IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
false, false, GUIEngine::IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
down_button->m_properties[GUIEngine::PROP_ID] = "down_button";
down_button->m_x = x;
down_button->m_y = y;
down_button->m_w = w;
down_button->m_h = font_height;
down_button->add();
down_button->setImage(file_manager->getAsset(FileManager::GUI_ICON, "scroll_down.png"));
addGPProgressWidget(down_button);
} // if MAJOR_MODE_GRAND_PRIX)
} // enableGPProgress
// ----------------------------------------------------------------------------
void RaceResultGUI::addGPProgressWidget(GUIEngine::Widget* widget)
{
m_widgets.push_back(widget);
m_gp_progress_widgets.push_back(widget);
}
// ----------------------------------------------------------------------------
void RaceResultGUI::displayGPProgress()
{
core::stringw msg = _("Grand Prix progress:");
GUIEngine::Widget* result_table = getWidget("result-table");
assert(result_table != NULL);
video::SColor color = video::SColor(255, 255, 0, 0);
core::recti dest_rect(
result_table->m_x + result_table->m_w - m_font->getDimension(msg.c_str()).Width - 5,
m_top, 0, 0);
m_font->draw(msg, dest_rect, color, false, false, NULL, true);
} // displayGPProgress
// ----------------------------------------------------------------------------
void RaceResultGUI::cleanupGPProgress()
{
for (unsigned int i = 0; i < m_gp_progress_widgets.size(); i++)
m_widgets.remove(m_gp_progress_widgets.get(i));
m_gp_progress_widgets.clearAndDeleteAll();
} // cleanupGPProgress
// ----------------------------------------------------------------------------
void RaceResultGUI::displayPostRaceInfo()
{
#ifndef SERVER_ONLY
// This happens in demo world
if (!World::getWorld())
return;
Highscores* scores = World::getWorld()->getHighscores();
video::SColor white_color = video::SColor(255, 255, 255, 255);
int x = (int)(UserConfigParams::m_width*0.65f);
int y = m_top;
int current_y = y;
int time_precision = race_manager->currentModeTimePrecision();
// In some case for exemple FTL they will be no highscores
if (scores != NULL)
{
// First draw title
GUIEngine::getFont()->draw(_("Highscores"),
core::recti(x, y, 0, 0),
white_color,
false, false, NULL, true /* ignoreRTL */);
std::string kart_name;
irr::core::stringw player_name;
// prevent excessive long name
unsigned int max_characters = 15;
unsigned int max_width = (UserConfigParams::m_width / 2 - 200) / 10;
if (max_width < 15)
max_characters = max_width;
float time;
for (int i = 0; i < scores->getNumberEntries(); i++)
{
scores->getEntry(i, kart_name, player_name, &time);
if (player_name.size() > max_characters)
{
int begin = (int(m_timer / 0.4f)) % (player_name.size() - max_characters);
player_name = player_name.subString(begin, max_characters, false);
}
video::SColor text_color = white_color;
if (m_highscore_rank - 1 == i)
{
text_color = video::SColor(255, 255, 0, 0);
}
int current_x = x;
2018-09-21 19:55:28 -04:00
current_y = y + (int)((i + 1) * m_distance_between_rows);
const KartProperties* prop = kart_properties_manager->getKart(kart_name);
if (prop != NULL)
{
const std::string &icon_path = prop->getAbsoluteIconFile();
video::ITexture* kart_icon_texture = irr_driver->getTexture(icon_path);
if (kart_icon_texture != NULL)
{
core::recti source_rect(core::vector2di(0, 0),
kart_icon_texture->getSize());
core::recti dest_rect(current_x, current_y,
current_x + m_width_icon, current_y + m_width_icon);
draw2DImage(
kart_icon_texture, dest_rect,
source_rect, NULL, NULL,
true);
current_x += m_width_icon + m_width_column_space;
}
}
// draw the player name
GUIEngine::getSmallFont()->draw(player_name.c_str(),
core::recti(current_x, current_y, current_x + 150, current_y + 10),
text_color,
false, false, NULL, true /* ignoreRTL */);
current_x = (int)(UserConfigParams::m_width * 0.85f);
// Finally draw the time
std::string time_string = StringUtils::timeToString(time, time_precision);
GUIEngine::getSmallFont()->draw(time_string.c_str(),
core::recti(current_x, current_y, current_x + 100, current_y + 10),
text_color,
false, false, NULL, true /* ignoreRTL */);
}
}
if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER)
{
// display lap count
if (race_manager->modeHasLaps())
{
core::stringw laps = _("Laps: %i", race_manager->getNumLaps());
2016-07-26 17:54:54 -04:00
current_y += int(m_distance_between_rows * 0.8f * 2);
GUIEngine::getFont()->draw(laps, core::recti(x, current_y, 0, 0),
white_color, false, false, nullptr, true);
}
// display difficulty
const core::stringw& difficulty_name =
race_manager->getDifficultyName(race_manager->getDifficulty());
core::stringw difficulty_string = _("Difficulty: %s", difficulty_name);
2016-07-26 17:54:54 -04:00
current_y += int(m_distance_between_rows * 0.8f);
GUIEngine::getFont()->draw(difficulty_string, core::recti(x, current_y, 0, 0),
white_color, false, false, nullptr, true);
// show fastest lap
if (race_manager->modeHasLaps())
{
float best_lap_time = static_cast<LinearWorld*>(World::getWorld())->getFastestLap();
core::stringw best_lap_string = _("Best lap time: %s",
StringUtils::timeToString(best_lap_time, time_precision).c_str());
2016-07-26 17:54:54 -04:00
current_y += int(m_distance_between_rows * 0.8f);
GUIEngine::getFont()->draw(best_lap_string,
core::recti(x, current_y, 0, 0), white_color, false, false,
nullptr, true);
} // if mode has laps
} // if not soccer mode
#endif
}
// ----------------------------------------------------------------------------
void RaceResultGUI::displayScreenShots()
{
const std::vector<std::string> tracks =
race_manager->getGrandPrix().getTrackNames();
int currentTrack = race_manager->getTrackNumber();
int n_sshot = 1;
for (int i = m_start_track; i < m_end_track; i++)
{
Track* track = track_manager->getTrack(tracks[i]);
GUIEngine::IconButtonWidget* sshot = getWidget<GUIEngine::IconButtonWidget>(
("sshot_" + StringUtils::toString(n_sshot)).c_str());
GUIEngine::LabelWidget* label = getWidget<GUIEngine::LabelWidget>(
("sshot_label_" + StringUtils::toString(n_sshot)).c_str());
2018-04-21 04:00:14 -04:00
assert(sshot != NULL && label != NULL);
2018-04-21 04:32:43 -04:00
// Network grand prix chooses each track 1 by 1
2018-04-21 04:00:14 -04:00
if (track == NULL)
{
sshot->setImage(file_manager->getAsset(FileManager::GUI_ICON,
2018-04-21 04:00:14 -04:00
"main_help.png"));
}
else
sshot->setImage(track->getScreenshotFile());
if (i <= currentTrack)
sshot->setBadge(GUIEngine::OK_BADGE);
else
sshot->resetAllBadges();
label->setText(StringUtils::toWString(i + 1), true);
n_sshot++;
}
}
// ----------------------------------------------------------------------------
int RaceResultGUI::getFontHeight() const
{
assert(m_font != NULL);
return m_font->getDimension(L"A").Height; //Could be any capital letter
}