Compute and display a story mode timer (#4121)

This is the result of my previous work, with a port of the timer version that was developed for a 1.0 mod. It has been used by several players so no major issue should exist, though UI and other elements may require adjustment to smooth some rough edges.

It features both a casual story mode timer storing the total time to complete the story mode (on by default) and a "speedrun" timer (off by default). The casual timer is paused whenever the player exits story mode, and supports play over multiple sessions. It is only displayed in the overworld and during challenges ; while the speedrun timer is permanently displayed.

Fix #2907
This commit is contained in:
Alayan-stk-2 2019-11-01 13:25:27 +01:00 committed by GitHub
parent 48f4088894
commit 770d02b19b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 760 additions and 92 deletions

View File

@ -87,6 +87,23 @@
</div>
<spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="fit">
<checkbox id="story-mode-timer"/>
<spacer width="1%" height="100%" />
<label height="100%" I18N="In the ui settings" text="Enable the story mode timer" word_wrap="true"/>
</div>
<spacer width="5" height="2%"/>
<div layout="horizontal-row" width="100%" height="fit">
<checkbox id="speedrun-timer"/>
<spacer width="1%" height="100%" />
<label id="speedrun-timer-text" height="100%" I18N="In the ui settings" text="Enable the speedrun timer" word_wrap="true"/>
</div>
<spacer width="5" height="2%"/>
</box>
</div>
</div>

View File

@ -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/*")

View File

@ -21,6 +21,7 @@
#include "challenges/challenge_status.hpp"
#include "challenges/challenge_data.hpp"
#include "challenges/story_mode_timer.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
@ -30,20 +31,35 @@
//-----------------------------------------------------------------------------
StoryModeStatus::StoryModeStatus(const XMLNode *node)
{
m_points = 0;
m_points_before = 0;
m_next_unlock_points = 0;
m_first_time = true;
m_easy_challenges = 0;
m_medium_challenges = 0;
m_hard_challenges = 0;
m_best_challenges = 0;
m_current_challenge = NULL;
m_points = 0;
m_points_before = 0;
m_next_unlock_points = 0;
m_first_time = true;
m_easy_challenges = 0;
m_medium_challenges = 0;
m_hard_challenges = 0;
m_best_challenges = 0;
m_current_challenge = NULL;
m_story_mode_finished = false;
m_valid_speedrun_finished = false;
m_story_mode_milliseconds = 0;
m_speedrun_milliseconds = 0;
// If there is saved data, load it
if(node)
{
node->get("first-time", &m_first_time);
// If the timer sub-nodes are missing, don't load junk data
// Disable the timers if story mode has already been started.
if(!node->get("finished", &m_story_mode_finished))
m_story_mode_finished = !m_first_time;
if(!node->get("speedrun-finished", &m_valid_speedrun_finished))
m_valid_speedrun_finished = false;
if(!node->get("story-ms", &m_story_mode_milliseconds))
m_story_mode_milliseconds = -1;
if(!node->get("speedrun-ms", &m_speedrun_milliseconds))
m_speedrun_milliseconds = -1;
} // if node
} // StoryModeStatus
@ -317,9 +333,28 @@ void StoryModeStatus::grandPrixFinished()
/** Writes the data of this StoryModeStatus to the specified stream.
* \param out UTF stream to write to.
*/
void StoryModeStatus::save(UTFWriter &out)
void StoryModeStatus::save(UTFWriter &out, bool current_player)
{
out << " <story-mode first-time=\"" << m_first_time << L"\">\n";
if (story_mode_timer->playerLoaded() && current_player)
{
// Make sure the timer pauses time is correct
if (story_mode_timer->isStoryModePaused())
{
story_mode_timer->unpauseTimer(/*loading*/ false);
story_mode_timer->updateTimer();
story_mode_timer->pauseTimer(/*loading*/ false);
}
m_speedrun_milliseconds = story_mode_timer->getSpeedrunTime();
m_story_mode_milliseconds = story_mode_timer->getStoryModeTime();
}
out << " <story-mode first-time=\"" << m_first_time << "\"";
out << " finished=\"" << m_story_mode_finished << "\"";
out << " speedrun-finished=\"" << m_valid_speedrun_finished << "\"\n";
out << " story-ms=\"" << m_story_mode_milliseconds << "\"";
out << " speedrun-ms=\"" << m_speedrun_milliseconds << "\">\n";
std::map<std::string, ChallengeStatus*>::const_iterator i;
for(i = m_challenges_state.begin();
i != m_challenges_state.end(); i++)

View File

@ -73,6 +73,17 @@ private:
int m_hard_challenges;
int m_best_challenges;
/* Set to true after fort magma */
bool m_story_mode_finished;
/* Set to true after fort magma if there was a valid speedrun active
* This is used to know if m_speedrun_milliseconds contain valid data */
bool m_valid_speedrun_finished;
// It overflows at over 500 hours
int m_story_mode_milliseconds;
int m_speedrun_milliseconds;
public:
StoryModeStatus(const XMLNode *node=NULL);
@ -86,58 +97,83 @@ public:
bool do_save=true);
void raceFinished ();
void grandPrixFinished ();
void save (UTFWriter &out);
void save (UTFWriter &out, bool current_player=false);
void addStatus(ChallengeStatus *cs);
void setCurrentChallenge(const std::string &challenge_id);
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the list of recently unlocked features (e.g. call at the end
* of a race to know if any features were unlocked) */
const std::vector<const ChallengeData*>
getRecentlyCompletedChallenges() {return m_unlocked_features;}
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Clear the list of recently unlocked challenges */
void clearUnlocked () {m_unlocked_features.clear(); }
// ------------------------------------------------------------------------
void clearUnlocked () {m_unlocked_features.clear(); }
// ----------------------------------------------------------------------------------------
/** Returns the number of completed challenges. */
int getNumCompletedChallenges () const { return (m_easy_challenges + m_medium_challenges +
m_hard_challenges + m_best_challenges); }
// ------------------------------------------------------------------------
int getNumCompletedChallenges () const { return (m_easy_challenges + m_medium_challenges +
m_hard_challenges + m_best_challenges); }
// ----------------------------------------------------------------------------------------
/** Returns the number of challenges with the superTux time beaten in a lower difficulty. */
int getNumReqMetInLowerDiff () const;
// ------------------------------------------------------------------------
int getNumReqMetInLowerDiff () const;
// ----------------------------------------------------------------------------------------
/** Returns the number of points accumulated. */
int getPoints () const { return m_points; }
// ------------------------------------------------------------------------
int getPoints () const { return m_points; }
// ----------------------------------------------------------------------------------------
/** Returns the number of points before the previous point increase */
int getPointsBefore () const { return m_points_before; }
// ------------------------------------------------------------------------
int getPointsBefore () const { return m_points_before; }
// ----------------------------------------------------------------------------------------
/** Returns the number of points needed by the next unlockable. 0 if none. */
int getNextUnlockPoints () const { return m_next_unlock_points; }
// ------------------------------------------------------------------------
int getNextUnlockPoints () const { return m_next_unlock_points; }
// ----------------------------------------------------------------------------------------
/** Returns the number of fulfilled challenges at easy level. */
int getNumEasyTrophies () const { return m_easy_challenges; }
// ------------------------------------------------------------------------
int getNumEasyTrophies () const { return m_easy_challenges; }
// ----------------------------------------------------------------------------------------
/* Returns the number of fulfilled challenges at medium level. */
int getNumMediumTrophies() const { return m_medium_challenges; }
// ------------------------------------------------------------------------
int getNumMediumTrophies() const { return m_medium_challenges; }
// ----------------------------------------------------------------------------------------
/** Returns the number of fulfilled challenges at hard level. */
int getNumHardTrophies () const { return m_hard_challenges; }
// ------------------------------------------------------------------------
int getNumHardTrophies () const { return m_hard_challenges; }
// ----------------------------------------------------------------------------------------
/** Returns the number of fulfilled challenges at best level. */
int getNumBestTrophies () const { return m_best_challenges; }
// ------------------------------------------------------------------------
int getNumBestTrophies () const { return m_best_challenges; }
// ----------------------------------------------------------------------------------------
/** Sets if this is the first time the intro is shown. */
void setFirstTime(bool ft) { m_first_time = ft; }
// ------------------------------------------------------------------------
void setFirstTime(bool ft) { m_first_time = ft; }
// ----------------------------------------------------------------------------------------
/** Returns if this is the first time the intro is shown. */
bool isFirstTime() const { return m_first_time; }
// ------------------------------------------------------------------------
const ChallengeStatus *getCurrentChallengeStatus() const
{
return m_current_challenge;
} // getCurrentChallengeStatus
// ------------------------------------------------------------------------
bool isFirstTime() const { return m_first_time; }
// ----------------------------------------------------------------------------------------
/** Sets if the player has beaten Nolock */
void setFinished() { m_story_mode_finished = true; }
// ----------------------------------------------------------------------------------------
/** Returns if the player has beaten Nolock */
bool isFinished() const { return m_story_mode_finished; }
// ----------------------------------------------------------------------------------------
/** Sets if the player has finished a valid speedrun */
void setSpeedrunFinished() { m_valid_speedrun_finished = true; }
// ----------------------------------------------------------------------------------------
/** Returns if the player has finished a valid speedrun */
bool isSpeedrunFinished() const { return m_valid_speedrun_finished; }
// ----------------------------------------------------------------------------------------
/** Sets the story mode timer */
void setStoryModeTimer(int milliseconds) { m_story_mode_milliseconds = milliseconds; }
// ----------------------------------------------------------------------------------------
/** Gets the story mode timer
* This is designed to be used on loading and once story mode is completed ;
* it will return out-of-date values when the timer is running. */
int getStoryModeTimer() { return m_story_mode_milliseconds; }
// ----------------------------------------------------------------------------------------
/** Sets the story mode timer */
void setSpeedrunTimer(int milliseconds) { m_speedrun_milliseconds = milliseconds; }
// ----------------------------------------------------------------------------------------
/** Gets the speedrun timer
* This is designed to be used on loading and once story mode is completed ;
* it will return out-of-date values when the timer is running. */
int getSpeedrunTimer() { return m_speedrun_milliseconds; }
// ----------------------------------------------------------------------------------------
const ChallengeStatus *getCurrentChallengeStatus() const { return m_current_challenge; }
// ----------------------------------------------------------------------------------------
/** Returns a challenge given the challenge id.
*/
const ChallengeStatus* getChallengeStatus(const std::string& id) const

View File

@ -0,0 +1,271 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2019 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 "challenges/story_mode_timer.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
StoryModeTimer *story_mode_timer = 0;
StoryModeTimer::StoryModeTimer()
{
m_player_tested = false;
reset();
} // SpeedrunTimer
void StoryModeTimer::reset()
{
PlayerProfile *player = PlayerManager::getCurrentPlayer();
// May happen when there is no player profile at all
if (player == NULL)
{
m_valid_speedrun_ended = false;
m_story_mode_ended = false;
m_stored_speedrun_milliseconds = 0;
m_stored_story_mode_milliseconds = 0;
}
else
{
m_valid_speedrun_ended = player->isSpeedrunFinished();
m_story_mode_ended = player->isFinished();
m_stored_speedrun_milliseconds = player->getSpeedrunTimer();
m_stored_story_mode_milliseconds = player->getStoryModeTimer();
}
m_valid_speedrun_started = false;
m_story_mode_started = false;
m_speedrun_pause_active = false;
m_story_mode_pause_active = false;
m_loading_pause = false;
m_player_can_speedrun = false;
m_speedrun_milliseconds = 0;
m_story_mode_milliseconds = 0;
std::chrono::time_point<std::chrono::system_clock> now(std::chrono::system_clock::now());
m_speedrun_total_pause_time = now-now;//the zero() function doesn't work
m_story_mode_total_pause_time = now-now;
}
void StoryModeTimer::startTimer()
{
// The speedrun timer runs even if not enabled, as long
// as the conditions match, as the ovrehead is minimal
// and it thus persist if the user disable/reenable it.
if (!m_valid_speedrun_started && m_player_can_speedrun)
{
m_speedrun_start = std::chrono::system_clock::now();
m_valid_speedrun_started = true;
}
// The normal story mode timer runs along the speedrun timer
// so that if speedrun mode is disabled, its value exists
// and is correct
if (!m_story_mode_started)
{
m_story_mode_start = std::chrono::system_clock::now();
m_story_mode_started = true;
}
}
void StoryModeTimer::stopTimer()
{
if (m_valid_speedrun_started)
{
m_speedrun_end = std::chrono::system_clock::now();
m_valid_speedrun_ended = true;
}
if (m_story_mode_started)
{
m_story_mode_end = std::chrono::system_clock::now();
m_story_mode_ended = true;
}
updateTimer();
}
/* Pauses the story mode and speedrun timer, if applicable.
* The speedrun timer is only paused on loading screens,
* while the story mode timer can also be paused when
* quitting the story mode. */
void StoryModeTimer::pauseTimer(bool loading)
{
// Don't change the pause time if there is no run,
// if it is finished, or if it is already set.
if ( m_valid_speedrun_started && !m_speedrun_pause_active &&
!m_valid_speedrun_ended && loading)
{
pauseSpeedrunTimer();
}
if ( m_story_mode_started && !m_story_mode_pause_active &&
!m_story_mode_ended)
{
pauseStoryModeTimer();
m_loading_pause = loading;
}
} // pauseTimer
void StoryModeTimer::pauseSpeedrunTimer()
{
m_speedrun_pause_start = std::chrono::system_clock::now();
m_speedrun_pause_active = true;
}
void StoryModeTimer::pauseStoryModeTimer()
{
m_story_mode_pause_start = std::chrono::system_clock::now();
m_story_mode_pause_active = true;
}
void StoryModeTimer::unpauseTimer(bool loading)
{
//Don't unpause if there is no run or no previous pause
if (m_valid_speedrun_started && m_speedrun_pause_active && !m_valid_speedrun_ended && loading)
{
unpauseSpeedrunTimer();
}
if (m_story_mode_started && m_story_mode_pause_active &&
!m_story_mode_ended && (m_loading_pause || ( !m_loading_pause && !loading)))
{
unpauseStoryModeTimer();
}
} //unpauseTimer
void StoryModeTimer::unpauseSpeedrunTimer()
{
std::chrono::time_point<std::chrono::system_clock> now(std::chrono::system_clock::now());
m_speedrun_total_pause_time += now - m_speedrun_pause_start;
m_speedrun_pause_active = false;
int milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(m_speedrun_total_pause_time).count();
Log::verbose("StoryModeTimer", "Total speedrun pause time : %ims.",milliseconds);
} // unpauseSpeedrunTimer
void StoryModeTimer::unpauseStoryModeTimer()
{
std::chrono::time_point<std::chrono::system_clock> now(std::chrono::system_clock::now());
m_story_mode_total_pause_time += now - m_story_mode_pause_start;
m_story_mode_pause_active = false;
} // unpauseStoryModeTimer
void StoryModeTimer::updateTimer()
{
if (!m_player_tested)
{
reset();
testPlayerRun();
}
updateSpeedrunTimer();
updateStoryModeTimer();
} // updateTimer
void StoryModeTimer::updateSpeedrunTimer()
{
std::chrono::duration<double> elapsed_time;
if (m_valid_speedrun_ended)
{
elapsed_time = m_speedrun_end - m_speedrun_start - m_speedrun_total_pause_time;
}
else
{
std::chrono::time_point<std::chrono::system_clock> now(std::chrono::system_clock::now());
elapsed_time = now - m_speedrun_start - m_speedrun_total_pause_time;
}
m_speedrun_milliseconds = m_stored_speedrun_milliseconds +
std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count();
}
void StoryModeTimer::updateStoryModeTimer()
{
std::chrono::duration<double> elapsed_time;
if (m_story_mode_ended)
{
elapsed_time = m_story_mode_end - m_story_mode_start - m_story_mode_total_pause_time;
}
else
{
std::chrono::time_point<std::chrono::system_clock> now(std::chrono::system_clock::now());
elapsed_time = now - m_story_mode_start - m_story_mode_total_pause_time;
}
m_story_mode_milliseconds = m_stored_story_mode_milliseconds +
std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count();
}
//Check if the current player has already entered story mode or not
void StoryModeTimer::testPlayerRun()
{
PlayerProfile *player = PlayerManager::getCurrentPlayer();
// May happen when there is no player profile at all
// Will be called again until a player profile is created
if (player == NULL)
return;
if (player->isFirstTime())
{
m_player_can_speedrun = true;
}
if(!m_player_can_speedrun && UserConfigParams::m_speedrun_mode)
{
UserConfigParams::m_speedrun_mode = false;
new MessageDialog(_("Speedrun mode disabled. It can only be enabled if the game"
" has not been closed since the launch of the story mode.\n\n"
"Closing the game before the story mode's"
" completion invalidates the timer.\n\n"
"To use the speedrun mode, please use a new profile."),
MessageDialog::MESSAGE_DIALOG_OK,
NULL, false, false, 0.6f, 0.7f);
}
m_player_tested = true;
}
/* When the active player changes, resets and reload timer data */
void StoryModeTimer::playerHasChanged()
{
m_player_tested = false;
} // playerHasChanged
std::string StoryModeTimer::getTimerString()
{
if (UserConfigParams::m_speedrun_mode && !m_valid_speedrun_started && !m_valid_speedrun_ended)
{
return StringUtils::timeToString(/*time in seconds*/ 0,
/*precision*/ 3, true, /* display hours*/ true);
}
if (UserConfigParams::m_speedrun_mode)
return StringUtils::timeToString(/*time in seconds*/ m_speedrun_milliseconds/1000.0f,
/*precision*/ 3, true, /* display hours*/ true);
else
return StringUtils::timeToString(/*time in seconds*/ m_story_mode_milliseconds/1000.0f,
/*precision*/ 0, true, /* display hours*/ true);
} // getStoryModeTimerString
/* EOF */

View File

@ -0,0 +1,90 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2019 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_STORY_MODE_TIMER_HPP
#define HEADER_STORY_MODE_TIMER_HPP
#include <chrono>
#include <iomanip>
class StoryModeTimer
{
private:
bool m_valid_speedrun_started, m_valid_speedrun_ended;
bool m_story_mode_started, m_story_mode_ended;
bool m_speedrun_pause_active, m_story_mode_pause_active;
bool m_loading_pause;
bool m_player_tested;
bool m_player_can_speedrun;
//This stores the number of milliseconds to display with the counter
int m_speedrun_milliseconds;
int m_story_mode_milliseconds;
int m_stored_speedrun_milliseconds;
int m_stored_story_mode_milliseconds;
std::chrono::time_point<std::chrono::system_clock> m_speedrun_start;
std::chrono::time_point<std::chrono::system_clock> m_speedrun_end;
std::chrono::time_point<std::chrono::system_clock> m_speedrun_pause_start;
std::chrono::time_point<std::chrono::system_clock> m_story_mode_start;
std::chrono::time_point<std::chrono::system_clock> m_story_mode_end;
std::chrono::time_point<std::chrono::system_clock> m_story_mode_pause_start;
std::chrono::duration<double> m_speedrun_total_pause_time;
std::chrono::duration<double> m_story_mode_total_pause_time;
void reset();
void testPlayerRun();
void pauseSpeedrunTimer();
void pauseStoryModeTimer();
void unpauseSpeedrunTimer();
void unpauseStoryModeTimer();
void updateSpeedrunTimer();
void updateStoryModeTimer();
public:
StoryModeTimer();
// ------------------------------------------------------------------------
/** Speedrun timer functions. */
void startTimer();
void stopTimer();
void pauseTimer(bool loading);
void unpauseTimer(bool loading);
void updateTimer();
void playerHasChanged();
std::string getTimerString();
bool playerLoaded() const { return m_player_tested; }
bool isStoryModePaused() const { return m_story_mode_pause_active; }
bool playerCanRun() const { return m_player_can_speedrun; }
bool isSpeedrunning() const { return m_valid_speedrun_started; }
bool speedrunIsFinished() const { return m_valid_speedrun_ended; }
int getStoryModeTime() const { return m_story_mode_milliseconds; }
int getSpeedrunTime() const { return m_speedrun_milliseconds; }
}; // StoryModeTimer
extern StoryModeTimer* story_mode_timer;
#endif
/* EOF */

View File

@ -19,6 +19,7 @@
#include "config/player_manager.hpp"
#include "achievements/achievements_manager.hpp"
#include "challenges/story_mode_timer.hpp"
#include "config/player_profile.hpp"
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
@ -461,12 +462,18 @@ PlayerProfile *PlayerManager::getPlayer(const irr::core::stringw &name)
*/
void PlayerManager::setCurrentPlayer(PlayerProfile *player)
{
bool player_has_changed = false;
if (m_current_player != player)
{
player_has_changed = true;
save();
race_manager->clearKartLastPositionOnOverworld();
}
m_current_player = player;
if(m_current_player)
{
m_current_player->computeActive();
}
if (player_has_changed)
story_mode_timer->playerHasChanged();
} // setCurrentPlayer

View File

@ -217,8 +217,14 @@ void PlayerProfile::save(UTFWriter &out)
out << " remember-password=\"" << m_remember_password << "\"\n";
out << " default-kart-color=\"" << m_default_kart_color << "\">\n";
{
bool is_current_player = false;
PlayerProfile *player = PlayerManager::getCurrentPlayer();
if (player != NULL && (getName() == player->getName()))
is_current_player = true;
if(m_story_mode_status)
m_story_mode_status->save(out);
m_story_mode_status->save(out, is_current_player);
if(m_achievements_status)
m_achievements_status->save(out);

View File

@ -145,7 +145,7 @@ public:
virtual void requestSignOut() = 0;
virtual bool isLoggedIn() const { return false; }
const std::string getIconFilename() const;
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Sets the name of this player. */
void setName(const core::stringw& name)
{
@ -155,7 +155,7 @@ public:
m_local_name = name;
} // setName
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the name of this player. */
const core::stringw& getName() const
{
@ -163,7 +163,7 @@ public:
return m_local_name;
} // getName
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns true if this player is a guest account. */
bool isGuestAccount() const
{
@ -172,80 +172,96 @@ public:
#endif
return m_is_guest_account;
} // isGuestAccount
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the last used online name. */
const core::stringw& getLastOnlineName() const
{
return m_last_online_name;
} // getLastOnlineName
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Sets the last used online name. */
void setLastOnlineName(const core::stringw &name)
{
m_last_online_name = name;
} // setLastOnlineName
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the unique id of this player. */
unsigned int getUniqueID() const { return m_unique_id; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returnes if the feature (kart, track) is locked. */
bool isLocked(const std::string &feature) const
{
return m_story_mode_status->isLocked(feature);
} // isLocked
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns all active challenges. */
void computeActive() { m_story_mode_status->computeActive(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the list of recently completed challenges. */
std::vector<const ChallengeData*> getRecentlyCompletedChallenges()
{
return m_story_mode_status->getRecentlyCompletedChallenges();
} // getRecently Completed Challenges
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Sets the currently active challenge. */
void setCurrentChallenge(const std::string &name)
{
m_story_mode_status->setCurrentChallenge(name);
} // setCurrentChallenge
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Callback when a GP is finished (to test if a challenge was
* fulfilled). */
void grandPrixFinished() { m_story_mode_status->grandPrixFinished(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getNumCompletedChallenges() const { return m_story_mode_status->getNumCompletedChallenges(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getPoints() const { return m_story_mode_status->getPoints(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getPointsBefore() const { return m_story_mode_status->getPointsBefore(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getNextUnlockPoints() const { return m_story_mode_status->getNextUnlockPoints(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
void setFirstTime(bool b) { m_story_mode_status->setFirstTime(b); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
bool isFirstTime() const { return m_story_mode_status->isFirstTime(); }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
void setFinished() { m_story_mode_status->setFinished(); }
// ----------------------------------------------------------------------------------------
bool isFinished() const { return m_story_mode_status->isFinished(); }
// ----------------------------------------------------------------------------------------
void setSpeedrunFinished() { m_story_mode_status->setSpeedrunFinished(); }
// ----------------------------------------------------------------------------------------
bool isSpeedrunFinished() { return m_story_mode_status->isSpeedrunFinished(); }
// ----------------------------------------------------------------------------------------
void setStoryModeTimer(int ms) { m_story_mode_status->setStoryModeTimer(ms); }
// ----------------------------------------------------------------------------------------
int getStoryModeTimer() { return m_story_mode_status->getStoryModeTimer(); }
// ----------------------------------------------------------------------------------------
void setSpeedrunTimer(int ms) { m_story_mode_status->setSpeedrunTimer(ms); }
// ----------------------------------------------------------------------------------------
int getSpeedrunTimer() { return m_story_mode_status->getSpeedrunTimer(); }
// ----------------------------------------------------------------------------------------
void clearUnlocked()
{
m_story_mode_status->clearUnlocked();
}
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns the current challenge for this player. */
const ChallengeStatus* getCurrentChallengeStatus() const
{
return m_story_mode_status->getCurrentChallengeStatus();
} // getCurrentChallengeStatus
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
const ChallengeStatus* getChallengeStatus(const std::string &id)
{
return m_story_mode_status->getChallengeStatus(id);
} // getChallengeStatus
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getNumEasyTrophies() const
{
return m_story_mode_status->getNumEasyTrophies();
} // getNumEasyTrophies
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
unsigned int getNumMediumTrophies() const
{
return m_story_mode_status->getNumMediumTrophies();
@ -259,47 +275,47 @@ public:
{
return m_story_mode_status->getNumBestTrophies();
} // getNumBestTrophies
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
AchievementsStatus* getAchievementsStatus()
{
return m_achievements_status;
} // getAchievementsStatus
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns true if a session was saved for this player. */
bool hasSavedSession() const { return m_saved_session; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
StoryModeStatus* getStoryModeStatus() { return m_story_mode_status; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** If a session was saved, return the id of the saved user. */
int getSavedUserId() const
{
assert(m_saved_session);
return m_saved_user_id;
} // getSavedUserId
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** If a session was saved, return the token to use. */
const std::string& getSavedToken() const
{
assert(m_saved_session);
return m_saved_token;
} // getSavedToken
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns if the last time this player was used it was used online or
* offline. */
bool wasOnlineLastTime() const { return m_last_was_online; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Sets if this player was logged in last time it was used. */
void setWasOnlineLastTime(bool b) { m_last_was_online = b; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Returns if the last time this player was used it was used online or
* offline. */
bool rememberPassword() const { return m_remember_password; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
/** Sets if this player was logged in last time it was used. */
void setRememberPassword(bool b) { m_remember_password = b; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
void setDefaultKartColor(float c) { m_default_kart_color = c; }
// ------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
float getDefaultKartColor() const { return m_default_kart_color; }
}; // class PlayerProfile

View File

@ -603,6 +603,12 @@ namespace UserConfigParams
PARAM_PREFIX BoolUserConfigParam m_display_fps
PARAM_DEFAULT( BoolUserConfigParam(false, "show_fps",
&m_video_group, "Display frame per seconds") );
PARAM_PREFIX BoolUserConfigParam m_display_story_mode_timer
PARAM_DEFAULT( BoolUserConfigParam(true, "show_story_mode_timer",
&m_video_group, "Display the story mode timer") );
PARAM_PREFIX BoolUserConfigParam m_speedrun_mode
PARAM_DEFAULT( BoolUserConfigParam(false, "show_speedrun_timer",
&m_video_group, "Display the speedrun timer") );
PARAM_PREFIX IntUserConfigParam m_max_fps
PARAM_DEFAULT( IntUserConfigParam(120, "max_fps",
&m_video_group, "Maximum fps, should be at least 60") );

View File

@ -17,6 +17,8 @@
#include "graphics/irr_driver.hpp"
#include "challenges/story_mode_timer.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "font/font_manager.hpp"
#include "graphics/2dutils.hpp"
@ -1850,6 +1852,48 @@ void IrrDriver::displayFPS()
#endif
} // updateFPS
// ----------------------------------------------------------------------------
/** Displays the timer for Story Mode on the screen.
* This can't be done in race or overworld GUIs as
* the speedrun timer has to be displayed on all screens.
*/
void IrrDriver::displayStoryModeTimer()
{
#ifndef SERVER_ONLY
gui::ScalableFont* font = GUIEngine::getHighresDigitFont();
core::stringw timer_string;
timer_string = story_mode_timer->getTimerString().c_str();
//The normal timer size ; to not write over it
core::dimension2du area = font->getDimension(L"99:99.99");
int regular_timer_width = area.Width;
int additional_height = 0;
if (UserConfigParams::m_speedrun_mode)
area = font->getDimension(L"99:99:99.999");
else
area = font->getDimension(L"99:99:99");
int screen_width = irr_driver->getActualScreenSize().Width;
int screen_height = irr_driver->getActualScreenSize().Height;
int speedrun_string_width = area.Width;
int dist_from_right = speedrun_string_width + regular_timer_width + screen_width*4/100;
core::rect<s32> position(screen_width - dist_from_right, screen_height*2/100,
screen_width , screen_height*6/100);
font->setColoredBorder(irr::video::SColor(255, 0, 32, 80));
if ( (UserConfigParams::m_speedrun_mode && story_mode_timer->speedrunIsFinished()) ||
(!UserConfigParams::m_speedrun_mode && PlayerManager::getCurrentPlayer()->isFinished()) )
font->draw(timer_string.c_str(), position, video::SColor(255, 0, 255, 0), false, false, NULL, true);
else
font->draw(timer_string.c_str(), position, video::SColor(255, 220, 255, 0), false, false, NULL, true);
font->disableColoredBorder();
#endif
} // displayStoryModeTimer
// ----------------------------------------------------------------------------
/** Requess a screenshot from irrlicht, and save it in a file.
*/

View File

@ -215,6 +215,7 @@ public:
scene::IAnimatedMesh *getAnimatedMesh(const std::string &name);
scene::IMesh *getMesh(const std::string &name);
void displayFPS();
void displayStoryModeTimer();
bool OnEvent(const irr::SEvent &event);
void setAmbientLight(const video::SColorf &light,
bool force_SH_computation = true);

View File

@ -658,6 +658,7 @@ namespace GUIEngine
#include "guiengine/engine.hpp"
#include "challenges/story_mode_timer.hpp"
#include "config/user_config.hpp"
#include "font/bold_face.hpp"
#include "font/digit_face.hpp"
@ -1135,7 +1136,7 @@ namespace GUIEngine
{
g_device->getVideoDriver()
->beginScene(true, true, video::SColor(255,100,101,140));
renderLoading();
renderLoading(true, true);
g_device->getVideoDriver()->endScene();
}
} // init
@ -1328,6 +1329,12 @@ namespace GUIEngine
// draw FPS if enabled
if ( UserConfigParams::m_display_fps ) irr_driver->displayFPS();
// draw speedrun timer if enabled
if ( UserConfigParams::m_speedrun_mode ) irr_driver->displayStoryModeTimer();
// Update the story mode and speedrun timer (even if not enabled)
story_mode_timer->unpauseTimer(/* exit loading pause */ true);
story_mode_timer->updateTimer();
g_driver->enableMaterial2D(false);
@ -1349,7 +1356,7 @@ namespace GUIEngine
// -----------------------------------------------------------------------
std::vector<irr::video::ITexture*> g_loading_icons;
void renderLoading(bool clearIcons)
void renderLoading(bool clearIcons, bool launching)
{
#ifndef SERVER_ONLY
if (clearIcons) g_loading_icons.clear();
@ -1421,6 +1428,17 @@ namespace GUIEngine
g_device->setEventReceiver(NULL);
g_device->run();
g_device->setEventReceiver(EventHandler::get());
// If launch is finished, pause & display the story mode timers
if ( !launching)
{
// For speedruns only, display the timer on loading screens
if (UserConfigParams::m_speedrun_mode)
irr_driver->displayStoryModeTimer();
//pause the timer during loading
story_mode_timer->pauseTimer(true);
}
#endif
} // renderLoading
@ -1434,7 +1452,7 @@ namespace GUIEngine
g_device->getVideoDriver()
->beginScene(true, true, video::SColor(255,100,101,140));
renderLoading(false);
renderLoading(false, true);
g_device->getVideoDriver()->endScene();
}
else

View File

@ -232,7 +232,7 @@ namespace GUIEngine
void render(float dt, bool is_loading = false);
/** \brief renders a "loading" screen */
void renderLoading(bool clearIcons = true);
void renderLoading(bool clearIcons = true, bool launching = false);
/** \brief to spice up a bit the loading icon : add icons to the loading screen */
void addLoadingIcon(irr::video::ITexture* icon);

View File

@ -176,6 +176,7 @@
#include "addons/news_manager.hpp"
#include "audio/music_manager.hpp"
#include "audio/sfx_manager.hpp"
#include "challenges/story_mode_timer.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/hardware_stats.hpp"
#include "config/player_manager.hpp"
@ -1791,7 +1792,7 @@ void initRest()
GUIEngine::init(device, driver, StateManager::get());
GUIEngine::renderLoading();
GUIEngine::renderLoading(true, true);
input_manager = new InputManager();
// Get into menu mode initially.
input_manager->setMode(InputManager::MENU);
@ -2360,6 +2361,11 @@ int main(int argc, char *argv[])
race_manager->setupPlayerKartInfo();
race_manager->startNew(false);
}
// Create the story mode timer before going in the main loop
// as it needs to be able to run continuously
story_mode_timer = new StoryModeTimer();
main_loop->run();
} // try
@ -2469,6 +2475,7 @@ static void cleanSuperTuxKart()
GUIEngine::cleanUp();
GUIEngine::clearScreenCache();
if(font_manager) delete font_manager;
if(story_mode_timer) delete story_mode_timer;
// Now finish shutting down objects which a separate thread. The
// RequestManager has been signaled to shut down as early as possible,

View File

@ -21,6 +21,7 @@
#include "audio/music_manager.hpp"
#include "audio/sfx_manager.hpp"
#include "challenges/story_mode_timer.hpp"
#include "config/user_config.hpp"
#include "guiengine/emoji_keyboard.hpp"
#include "guiengine/engine.hpp"
@ -56,9 +57,12 @@ RacePausedDialog::RacePausedDialog(const float percentWidth,
ModalDialog(percentWidth, percentHeight)
{
m_self_destroy = false;
m_from_overworld = false;
if (dynamic_cast<OverWorld*>(World::getWorld()) != NULL)
{
loadFromFile("overworld_dialog.stkgui");
m_from_overworld = true;
}
else if (!NetworkConfig::get()->isNetworking())
{
@ -222,6 +226,11 @@ GUIEngine::EventPropagation
else
{
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
// Pause story mode timer when quitting story mode
if (m_from_overworld)
story_mode_timer->pauseTimer(/*loading screen*/ false);
if (race_manager->raceWasStartedFromOverworld())
{
OverWorld::enterOverWorld();

View File

@ -37,6 +37,7 @@ class RacePausedDialog : public GUIEngine::ModalDialog,
{
private:
bool m_self_destroy;
bool m_from_overworld;
GUIEngine::TextBoxWidget* m_text_box;

View File

@ -20,6 +20,7 @@
#include "states_screens/main_menu_screen.hpp"
#include "addons/news_manager.hpp"
#include "challenges/story_mode_timer.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
@ -475,6 +476,10 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
{
NetworkConfig::get()->unsetNetworking();
PlayerProfile *player = PlayerManager::getCurrentPlayer();
// Start the story mode (and speedrun) timer
story_mode_timer->startTimer();
if (player->isFirstTime())
{
CutsceneWorld::setUseDuration(true);
@ -493,6 +498,9 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name,
}
else
{
// Unpause the story mode timer when entering back the story mode
story_mode_timer->unpauseTimer(/* exit loading pause */ false);
const std::string default_kart = UserConfigParams::m_default_kart;
if (player->isLocked(default_kart))
{

View File

@ -20,6 +20,7 @@
#include "addons/news_manager.hpp"
#include "audio/sfx_manager.hpp"
#include "audio/sfx_base.hpp"
#include "challenges/story_mode_timer.hpp"
#include "config/hardware_stats.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
@ -38,6 +39,7 @@
#include "guiengine/widget.hpp"
#include "io/file_manager.hpp"
#include "online/request_manager.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "states_screens/options/options_screen_audio.hpp"
#include "states_screens/options/options_screen_general.hpp"
@ -242,6 +244,30 @@ void OptionsScreenUI::init()
assert( fps != NULL );
fps->setState( UserConfigParams::m_display_fps );
CheckBoxWidget* story_timer = getWidget<CheckBoxWidget>("story-mode-timer");
assert( story_timer != NULL );
story_timer->setState( UserConfigParams::m_display_story_mode_timer );
CheckBoxWidget* speedrun_timer = getWidget<CheckBoxWidget>("speedrun-timer");
assert( speedrun_timer != NULL );
if (UserConfigParams::m_speedrun_mode)
{
if (!story_mode_timer->playerCanRun())
{
UserConfigParams::m_speedrun_mode = false;
new MessageDialog(_("Speedrun mode disabled. It can only be enabled if the game"
" has not been closed since the launch of the story mode.\n\n"
"Closing the game before the story mode's"
" completion invalidates the timer.\n\n"
"To use the speedrun mode, please use a new profile."),
MessageDialog::MESSAGE_DIALOG_OK,
NULL, false, false, 0.6f, 0.7f);
}
}
speedrun_timer->setState( UserConfigParams::m_speedrun_mode );
speedrun_timer->setVisible( UserConfigParams::m_display_story_mode_timer );
getWidget<LabelWidget>("speedrun-timer-text")
->setVisible(UserConfigParams::m_display_story_mode_timer);
// --- select the right skin in the spinner
bool currSkinFound = false;
const std::string& user_skin = UserConfigParams::m_skin_file;
@ -403,6 +429,46 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con
assert( fps != NULL );
UserConfigParams::m_display_fps = fps->getState();
}
else if (name == "story-mode-timer")
{
CheckBoxWidget* story_timer = getWidget<CheckBoxWidget>("story-mode-timer");
assert( story_timer != NULL );
UserConfigParams::m_display_story_mode_timer = story_timer->getState();
CheckBoxWidget* speedrun_timer = getWidget<CheckBoxWidget>("speedrun-timer");
assert( speedrun_timer != NULL );
speedrun_timer->setActive( UserConfigParams::m_display_story_mode_timer );
getWidget<LabelWidget>("speedrun-timer-text")
->setActive(UserConfigParams::m_display_story_mode_timer);
// Disable speedrun mode if the story mode timer is disabled
if (!UserConfigParams::m_display_story_mode_timer)
{
UserConfigParams::m_speedrun_mode = false;
speedrun_timer->setState(false);
}
}
else if (name == "speedrun-timer")
{
CheckBoxWidget* speedrun_timer = getWidget<CheckBoxWidget>("speedrun-timer");
assert( speedrun_timer != NULL );
if (speedrun_timer->getState())
{
if (!story_mode_timer->playerCanRun())
{
speedrun_timer->setState(false);
new MessageDialog(_("Speedrun mode can only be enabled if the game has not"
" been closed since the launch of the story mode.\n\n"
"Closing the game before the story mode's"
" completion invalidates the timer.\n\n"
"To use the speedrun mode, please use a new profile."),
MessageDialog::MESSAGE_DIALOG_OK,
NULL, false, false, 0.6f, 0.7f);
}
}
UserConfigParams::m_speedrun_mode = speedrun_timer->getState();
}
#endif
} // eventCallback

View File

@ -24,6 +24,7 @@ using namespace irr;
#include <algorithm>
#include <limits>
#include "challenges/story_mode_timer.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/user_config.hpp"
#include "graphics/camera.hpp"
@ -289,6 +290,14 @@ void RaceGUI::renderGlobal(float dt)
if (!m_enabled) return;
// Display the story mode timer if not in speedrun mode
// If in speedrun mode, it is taken care of in GUI engine
// as it must be displayed in all the game's screens
if (UserConfigParams::m_display_story_mode_timer &&
!UserConfigParams::m_speedrun_mode &&
race_manager->raceWasStartedFromOverworld())
irr_driver->displayStoryModeTimer();
// MiniMap is drawn when the players wait for the start countdown to end
drawGlobalMiniMap();

View File

@ -20,6 +20,7 @@
#include "states_screens/race_gui_overworld.hpp"
#include "challenges/challenge_status.hpp"
#include "challenges/story_mode_timer.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
@ -196,6 +197,12 @@ void RaceGUIOverworld::renderGlobal(float dt)
drawTrophyPoints();
}
// Display the story mode timer if not in speedrun mode
// If in speedrun mode, it is taken care of in GUI engine
// as it must be displayed in all the game's screens
if (UserConfigParams::m_display_story_mode_timer && !UserConfigParams::m_speedrun_mode)
irr_driver->displayStoryModeTimer();
drawGlobalMiniMap();
#endif
} // renderGlobal
@ -261,6 +268,9 @@ void RaceGUIOverworld::drawTrophyPoints()
size*2, pos.UpperLeftCorner.Y + size);
core::rect<s32> source(core::position2di(0, 0), m_trophy[3]->getSize());
float place_between_trophies =
PlayerManager::getCurrentPlayer()->isLocked("difficulty_best") ? size*2.0f : size*1.0f;
// Draw trophies icon and the number of trophy obtained by type
for (unsigned int i=0;i<4;i++)
{