Add Ghost replay GUI

This commit is contained in:
Benau 2016-02-13 01:34:00 +08:00
parent 5cd27f8f99
commit 8a121ed32b
19 changed files with 583 additions and 142 deletions

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="5%" y="5%" width="90%" height="90%" layout="vertical-row">
<div x="5%" y="0%" width="90%" proportion="6" layout="horizontal-row">
<div width="40%" height="100%" layout="vertical-row">
<icon id="icon" align="center" proportion="8" width="100%" icon="gui/loading.png" />
<spacer proportion="1" />
</div>
<spacer proportion="1" />
<div width="60%" height="50%" layout="vertical-row">
<label id="name" width="100%" text_align="left"/>
<spacer height="10"/>
</div>
</div>
<div width="80%" proportion="5" align="center">
<buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center">
<icon-button id="start" width="128" height="128"
icon="gui/green_check.png"
I18N="Ghost replay info screen action" text="Start Race" />
<icon-button id="remove" width="128" height="128"
icon="gui/remove.png"
I18N="Ghost replay info action" text="Remove" />
<icon-button id="back" width="128" height="128"
icon="gui/back.png"
I18N="Ghost replay info action" text="Back" />
</buttonbar>
</div>
</div>
</stkgui>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="0%" y="1%" width="100%" height="98%" layout="vertical-row" >
<div x="0" y="0" width="100%" layout="horizontal-row" height="8%">
<icon-button id="back" height="100%" icon="gui/back.png"/>
<header text_align="center" proportion="1" text="Ghost Replay Selection" align="center"/>
<icon-button id="reload" height="90%" icon="gui/restart.png"/>
</div>
<box proportion="1" width="98%" align="center" layout="vertical-row" padding="6">
<list id="replay_list" x="0" y="0" width="100%" height="100%"/>
</box>
</div>
</stkgui>

View File

@ -25,6 +25,8 @@
<!-- Populated dynamically at runtime -->
<tabs width="100%" height="25" id="trackgroups"> </tabs>
<spacer width="100%" height="2%" />
<spacer width="100%" height="3%" />
<button id="ghost" I18N="In the track selection screen" text="Load Ghost Replay" />
</div>
</stkgui>

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

@ -526,7 +526,6 @@ void cmdLineHelp()
" spaces are allowed in the track names.\n"
" --demo-laps=n Number of laps in a demo.\n"
" --demo-karts=n Number of karts to use in a demo.\n"
" --ghost Replay ghost data together with one player kart.\n"
// " --history Replay history file 'history.dat'.\n"
// " --history=n Replay history file 'history.dat' using:\n"
// " n=1: recorded positions\n"
@ -998,9 +997,6 @@ int handleCmdLine()
}
} // --with-profile
if(CommandLine::has("--ghost"))
ReplayPlay::create();
if(CommandLine::has("--history", &n))
{
history->doReplayHistory( (History::HistoryReplayMode)n);
@ -1151,6 +1147,7 @@ void initRest()
// The order here can be important, e.g. KartPropertiesManager needs
// defaultKartProperties, which are defined in stk_config.
history = new History ();
ReplayPlay::create();
ReplayRecorder::create();
material_manager = new MaterialManager ();
track_manager = new TrackManager ();
@ -1575,7 +1572,6 @@ static void cleanSuperTuxKart()
irr_driver->updateConfigIfRelevant();
AchievementsManager::destroy();
Referee::cleanup();
if(ReplayPlay::get()) ReplayPlay::destroy();
if(race_manager) delete race_manager;
if(grand_prix_manager) delete grand_prix_manager;
if(highscore_manager) delete highscore_manager;
@ -1587,6 +1583,7 @@ static void cleanSuperTuxKart()
if(track_manager) delete track_manager;
if(material_manager) delete material_manager;
if(history) delete history;
ReplayPlay::destroy();
ReplayRecorder::destroy();
delete ParticleKindManager::get();
PlayerManager::destroy();

View File

@ -147,7 +147,7 @@ void World::init()
m_eliminated_players = 0;
m_num_players = 0;
unsigned int gk = 0;
if (ReplayPlay::get())
if (race_manager->hasGhostKarts())
gk = ReplayPlay::get()->getNumGhostKart();
// Create the race gui before anything else is attached to the scene node
@ -257,7 +257,7 @@ void World::reset()
Camera::getCamera(i)->reset();
}
if(ReplayPlay::get())
if(race_manager->hasGhostKarts())
ReplayPlay::get()->reset();
resetAllKarts();
@ -420,7 +420,7 @@ World::~World()
delete m_karts[i];
}
if(ReplayPlay::get())
if(race_manager->hasGhostKarts())
{
// Destroy the old replay object, which also stored the ghost
// karts, and create a new one (which means that in further
@ -429,6 +429,7 @@ World::~World()
ReplayPlay::create();
}
m_karts.clear();
race_manager->setRaceGhostKarts(false);
Camera::removeAllCameras();

View File

@ -77,6 +77,7 @@ RaceManager::RaceManager()
m_have_kart_last_position_on_overworld = false;
setReverseTrack(false);
setRecordRace(false);
setRaceGhostKarts(false);
setTrack("jungle");
m_default_ai_list.clear();
setNumPlayers(0);
@ -311,11 +312,8 @@ void RaceManager::computeRandomKartList()
void RaceManager::startNew(bool from_overworld)
{
unsigned int gk = 0;
if (ReplayPlay::get())
{
ReplayPlay::get()->loadBasicInfo();
if (m_has_ghost_karts)
gk = ReplayPlay::get()->getNumGhostKart();
}
m_started_from_overworld = from_overworld;
m_saved_gp = NULL; // There will be checks for this being NULL done later

View File

@ -351,6 +351,8 @@ private:
bool m_continue_saved_gp;
bool m_will_record_race;
bool m_has_ghost_karts;
public:
RaceManager();
~RaceManager();
@ -704,10 +706,20 @@ public:
m_will_record_race = record;
} // setRecordRace
// ------------------------------------------------------------------------
void setRaceGhostKarts(bool ghost)
{
m_has_ghost_karts = ghost;
} // setRaceGhostKarts
// ------------------------------------------------------------------------
bool willRecordRace() const
{
return m_will_record_race;
} // willRecordRace
// ------------------------------------------------------------------------
bool hasGhostKarts() const
{
return m_has_ghost_karts;
} // hasGhostKarts
}; // RaceManager

View File

@ -23,19 +23,16 @@
// -----------------------------------------------------------------------------
ReplayBase::ReplayBase()
{
m_filename = "";
} // ReplayBaese
// -----------------------------------------------------------------------------
/** Opens a replay file (depending on the track name, which is taken from
* the race manager).
/** Opens a replay file which is determined by sub classes.
* \param writeable True if the file should be opened for writing.
* \return A FILE *, or NULL if the file could not be opened.
*/
FILE* ReplayBase::openReplayFile(bool writeable)
{
m_filename = file_manager->getReplayDir() +
race_manager->getTrackName() + ".replay";
FILE *fd = fopen(m_filename.c_str(), writeable ? "w" : "r");
FILE *fd = fopen((file_manager->getReplayDir() +
getReplayFilename()).c_str(), writeable ? "w" : "r");
if (!fd)
{
return NULL;

View File

@ -24,6 +24,7 @@
#include <stdio.h>
#include <string>
#include <vector>
/**
* \ingroup race
@ -32,10 +33,20 @@ class ReplayBase : public NoCopy
{
// Needs access to KartReplayEvent
friend class GhostKart;
private:
/** The filename of the replay file. Only defined after calling
* openReplayFile. */
std::string m_filename;
public:
class ReplayData
{
public:
std::string m_filename;
bool m_reverse;
std::vector<std::string> m_kart_list;
unsigned int m_difficulty;
std::string m_track_name;
unsigned int m_laps;
float m_min_time;
}; // ReplayData
protected:
/** Stores a transform event, i.e. a position and rotation of a kart
* at a certain time. */
@ -69,16 +80,19 @@ protected:
}; // KartReplayEvent
// ------------------------------------------------------------------------
ReplayBase();
FILE *openReplayFile(bool writeable);
// ----------------------------------------------------------------------
// ------------------------------------------------------------------------
/** Returns the filename that was opened. */
const std::string &getReplayFilename() const { return m_filename;}
// ----------------------------------------------------------------------
virtual const std::string& getReplayFilename() const = 0;
// ------------------------------------------------------------------------
/** Returns the version number of the replay file. This is used to check
* that a loaded replay file can still be understood by this
* executable. */
unsigned int getReplayVersion() const { return 1; }
public:
ReplayBase();
virtual ~ReplayBase() {};
}; // ReplayBase
#endif

View File

@ -37,8 +37,7 @@ ReplayPlay *ReplayPlay::m_replay_play = NULL;
*/
ReplayPlay::ReplayPlay()
{
m_next = 0;
m_ghost_karts_list.clear();
m_current_replay_file = 0;
} // ReplayPlay
//-----------------------------------------------------------------------------
@ -52,8 +51,6 @@ ReplayPlay::~ReplayPlay()
*/
void ReplayPlay::reset()
{
m_next = 0;
m_ghost_karts_list.clear();
for(unsigned int i=0; i<(unsigned int)m_ghost_karts.size(); i++)
{
m_ghost_karts[i].reset();
@ -61,11 +58,78 @@ void ReplayPlay::reset()
} // reset
//-----------------------------------------------------------------------------
/** Loads the basic (ghost karts, reverse track) info in the replay file,
* required by race manager.
*/
void ReplayPlay::loadBasicInfo()
void ReplayPlay::loadAllReplayFile()
{
m_replay_file_list.clear();
std::set<std::string> files;
file_manager->listFiles(files, file_manager->getReplayDir(),
/*is_full_path*/ false);
char s[1024], s1[1024];
for (std::set<std::string>::iterator i = files.begin();
i != files.end(); ++i)
{
if (StringUtils::getExtension(*i) != "replay") continue;
FILE *fd = fopen((file_manager->getReplayDir() + (*i)).c_str(), "r");
if (fd == NULL) continue;
ReplayData rd;
rd.m_filename = *i;
int reverse = 0;
fgets(s, 1023, fd);
if(sscanf(s, "reverse: %d", &reverse) != 1)
Log::fatal("Replay", "Reverse info found in replay file.");
rd.m_reverse = (bool)reverse;
while(true)
{
fgets(s, 1023, fd);
core::stringc is_end(s);
is_end.trim();
if (is_end == "kart_list_end") break;
char s1[1024];
if (sscanf(s,"kart: %s", s1) != 1)
Log::fatal("Replay", "Could not read ghost karts info!");
rd.m_kart_list.push_back(std::string(s1));
}
fgets(s, 1023, fd);
unsigned int version;
if (sscanf(s,"version: %u", &version) != 1)
Log::fatal("Replay", "No Version information found in replay file (bogus replay file).");
if (version != getReplayVersion())
{
Log::warn("Replay", "Replay is version '%d'",version);
Log::warn("Replay", "STK version is '%d'",getReplayVersion());
Log::warn("Replay", "We try to proceed, but it may fail.");
}
fgets(s, 1023, fd);
if (sscanf(s, "difficulty: %u", &rd.m_difficulty) != 1)
Log::fatal("Replay", " No difficulty found in replay file.");
fgets(s, 1023, fd);
if (sscanf(s, "track: %s", s1) != 1)
Log::fatal("Replay", "Track not found in replay file.");
rd.m_track_name = std::string(s1);
fgets(s, 1023, fd);
if(sscanf(s, "laps: %u", &rd.m_laps) != 1)
Log::fatal("Replay", "No number of laps found in replay file.");
rd.m_min_time = 0.0f;
fclose(fd);
m_replay_file_list.push_back(rd);
}
}
//-----------------------------------------------------------------------------
void ReplayPlay::load()
{
m_ghost_karts.clearAndDeleteAll();
char s[1024];
FILE *fd = openReplayFile(/*writeable*/false);
@ -77,100 +141,11 @@ void ReplayPlay::loadBasicInfo()
return;
}
int reverse;
fgets(s, 1023, fd);
if(sscanf(s, "reverse: %d", &reverse) != 1)
Log::fatal("Replay", "Reverse info found in replay file.");
race_manager->setReverseTrack((bool)reverse);
Log::info("Replay", "Reading ghost karts info");
while(true)
{
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
core::stringc is_end(s);
is_end.trim();
if (is_end == "kart_list_end") break;
char s1[1024];
if (sscanf(s,"kart: %s", s1) != 1)
Log::fatal("Replay", "Could not read ghost karts info!");
m_ghost_karts_list.push_back(std::string(s1));
}
fclose(fd);
} // loadBasicInfo
//-----------------------------------------------------------------------------
/** Loads a replay data from file called 'trackname'.replay.
*/
void ReplayPlay::load()
{
m_ghost_karts.clearAndDeleteAll();
char s[1024], s1[1024];
FILE *fd = openReplayFile(/*writeable*/false);
if(!fd)
{
Log::error("Replay", "Can't read '%s', ghost replay disabled.",
getReplayFilename().c_str());
destroy();
return;
}
Log::info("Replay", "Reading replay file '%s'.", getReplayFilename().c_str());
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
// Skip reverse info which is already read.
for (unsigned int i = 0; i < m_ghost_karts_list.size(); i++)
{
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
// Skip kart info which is already read.
}
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
// Skip kart_list_end
unsigned int version;
if (sscanf(s,"version: %u", &version) != 1)
Log::fatal("Replay", "No Version information found in replay file (bogus replay file).");
if (version != getReplayVersion())
{
Log::warn("Replay", "Replay is version '%d'",version);
Log::warn("Replay", "STK version is '%d'",getReplayVersion());
Log::warn("Replay", "We try to proceed, but it may fail.");
}
if (fgets(s, 1023, fd) == NULL)
Log::fatal("Replay", "Could not read '%s'.", getReplayFilename().c_str());
int n;
if(sscanf(s, "difficulty: %d", &n) != 1)
Log::fatal("Replay", " No difficulty found in replay file.");
if(race_manager->getDifficulty()!=(RaceManager::Difficulty)n)
Log::warn("Replay", "Difficulty of replay is '%d', "
"while '%d' is selected.",
race_manager->getDifficulty(), n);
fgets(s, 1023, fd);
if(sscanf(s, "track: %s", s1) != 1)
Log::warn("Replay", "Track not found in replay file.");
assert(std::string(s1)==race_manager->getTrackName());
race_manager->setTrack(s1);
unsigned int num_laps;
fgets(s, 1023, fd);
if(sscanf(s, "laps: %u", &num_laps) != 1)
Log::fatal("Replay", "No number of laps found in replay file.");
race_manager->setNumLaps(num_laps);
const unsigned int line_skipped = getNumGhostKart() + 6;
for (unsigned int i = 0; i < line_skipped; i++)
fgets(s, 1023, fd);
// eof actually doesn't trigger here, since it requires first to try
// reading behind eof, but still it's clearer this way.
@ -193,7 +168,8 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line)
{
char s[1024];
const unsigned int kart_num = m_ghost_karts.size();
m_ghost_karts.push_back(new GhostKart(m_ghost_karts_list.at(kart_num),
m_ghost_karts.push_back(new GhostKart(m_replay_file_list
[m_current_replay_file].m_kart_list.at(kart_num),
kart_num, kart_num + 1));
m_ghost_karts[kart_num].init(RaceManager::KT_GHOST);
Controller* controller = new GhostController(getGhostKart(kart_num));

View File

@ -34,15 +34,14 @@ class GhostKart;
class ReplayPlay : public ReplayBase
{
private:
static ReplayPlay *m_replay_play;
static ReplayPlay *m_replay_play;
/** Points to the next free entry. */
unsigned int m_next;
unsigned int m_current_replay_file;
std::vector<std::string> m_ghost_karts_list;
std::vector<ReplayData> m_replay_file_list;
/** All ghost karts. */
PtrVector<GhostKart> m_ghost_karts;
PtrVector<GhostKart> m_ghost_karts;
ReplayPlay();
~ReplayPlay();
@ -50,16 +49,30 @@ private:
public:
void reset();
void load();
void loadBasicInfo();
void loadAllReplayFile();
// ------------------------------------------------------------------------
void setReplayFile(unsigned int n)
{ m_current_replay_file = n; }
// ------------------------------------------------------------------------
const ReplayData& getReplayData(unsigned int n) const
{ return m_replay_file_list.at(n); }
// ------------------------------------------------------------------------
const unsigned int getNumReplayFile() const
{ return m_replay_file_list.size(); }
// ------------------------------------------------------------------------
GhostKart* getGhostKart(int n) { return m_ghost_karts.get(n); }
// ------------------------------------------------------------------------
const unsigned int getNumGhostKart() const
{ return m_ghost_karts_list.size(); }
{
assert(m_replay_file_list.size() > 0);
return m_replay_file_list.at(m_current_replay_file).m_kart_list.size();
}
// ------------------------------------------------------------------------
const std::string& getGhostKartName(int n) const
{ return m_ghost_karts_list.at(n); }
{
assert(m_replay_file_list.size() > 0);
return m_replay_file_list.at(m_current_replay_file).m_kart_list.at(n);
}
// ------------------------------------------------------------------------
/** Creates a new instance of the replay object. */
static void create() { m_replay_play = new ReplayPlay(); }
@ -71,6 +84,13 @@ public:
static void destroy()
{ delete m_replay_play; m_replay_play = NULL; }
// ------------------------------------------------------------------------
/** Returns the filename that was opened. */
virtual const std::string& getReplayFilename() const
{
assert(m_replay_file_list.size() > 0);
return m_replay_file_list.at(m_current_replay_file).m_filename;
}
// ------------------------------------------------------------------------
}; // Replay
#endif

View File

@ -39,6 +39,7 @@ ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL;
*/
ReplayRecorder::ReplayRecorder()
{
m_filename = "TODO.replay";
} // ReplayRecorder
//-----------------------------------------------------------------------------
@ -97,9 +98,6 @@ void ReplayRecorder::update(float dt)
unsigned int num_karts = world->getNumKarts();
float time = world->getTime();
// Once we use interpolate results, we don't have to increase
// m_next by num_karts, so count how often to increase
for(unsigned int i=0; i<num_karts; i++)
{
const AbstractKart *kart = world->getKart(i);

View File

@ -30,6 +30,7 @@
class ReplayRecorder : public ReplayBase
{
private:
std::string m_filename;
/** A separate vector of Replay Events for all transforms. */
std::vector< std::vector<TransformEvent> > m_transform_events;
@ -85,6 +86,10 @@ public:
// ------------------------------------------------------------------------
/** Delete the instance of the replay object. */
static void destroy() { delete m_replay_recorder; m_replay_recorder=NULL; }
// ------------------------------------------------------------------------
/** Returns the filename that was opened. */
virtual const std::string& getReplayFilename() const { return m_filename; }
// ------------------------------------------------------------------------
}; // ReplayRecorder
#endif

View File

@ -0,0 +1,109 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/dialogs/ghost_replay_info_dialog.hpp"
#include "config/player_manager.hpp"
#include "replay/replay_play.hpp"
#include "states_screens/ghost_replay_selection.hpp"
#include "states_screens/state_manager.hpp"
using namespace GUIEngine;
using namespace irr::core;
// -----------------------------------------------------------------------------
GhostReplayInfoDialog::GhostReplayInfoDialog(unsigned int replay_id)
: ModalDialog(0.5f,0.5f), m_replay_id(replay_id)
{
m_self_destroy = false;
m_rd = ReplayPlay::get()->getReplayData(m_replay_id);
loadFromFile("ghost_replay_info_dialog.stkgui");
LabelWidget *name = getWidget<LabelWidget>("name");
assert(name);
name->setText(stringw((m_rd.m_filename).c_str()), false);
m_back_widget = getWidget<IconButtonWidget>("back");
m_back_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
m_action_widget = getWidget<RibbonWidget>("actions");
} // GhostReplayInfoDialog
// -----------------------------------------------------------------------------
GhostReplayInfoDialog::~GhostReplayInfoDialog()
{
} // ~GhostReplayInfoDialog
// -----------------------------------------------------------------------------
GUIEngine::EventPropagation
GhostReplayInfoDialog::processEvent(const std::string& event_source)
{
if (event_source == "actions")
{
const std::string& selection =
m_action_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if(selection == "start")
{
ModalDialog::dismiss();
ReplayPlay::get()->setReplayFile(m_replay_id);
race_manager->setRaceGhostKarts(true);
race_manager->setNumKarts(race_manager->getNumLocalPlayers());
// Disable accidentally unlocking of a challenge
PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
race_manager->setReverseTrack(m_rd.m_reverse);
race_manager->startSingleRace(m_rd.m_track_name, m_rd.m_laps, false);
return GUIEngine::EVENT_BLOCK;
}
else if(selection == "remove")
{
m_self_destroy = true;
if (!file_manager
->removeFile(file_manager->getReplayDir() + m_rd.m_filename))
Log::warn("GhostReplayInfoDialog", "Failed to delete file.");
return GUIEngine::EVENT_BLOCK;
}
else if (selection == "back")
{
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
}
return GUIEngine::EVENT_LET;
} // processEvent
// -----------------------------------------------------------------------------
bool GhostReplayInfoDialog::onEscapePressed()
{
if (m_back_widget->isActivated())
m_self_destroy = true;
return false;
} // onEscapePressed
// -----------------------------------------------------------------------------
void GhostReplayInfoDialog::onUpdate(float dt)
{
if (m_self_destroy)
{
ModalDialog::dismiss();
GhostReplaySelection::getInstance()->refresh();
return;
}
} // onUpdate

View File

@ -0,0 +1,55 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_GHOST_REPLAY_INFO_DIALOG_HPP
#define HEADER_GHOST_REPLAY_INFO_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "replay/replay_base.hpp"
#include <irrString.h>
/** \brief Dialog that allows a user to do action with ghost replay file
* \ingroup states_screens
*/
class GhostReplayInfoDialog : public GUIEngine::ModalDialog
{
private:
bool m_self_destroy;
const unsigned int m_replay_id;
ReplayBase::ReplayData m_rd;
GUIEngine::RibbonWidget* m_action_widget;
GUIEngine::IconButtonWidget* m_back_widget;
public:
GhostReplayInfoDialog(unsigned int replay_id);
~GhostReplayInfoDialog();
GUIEngine::EventPropagation processEvent(const std::string& eventSource);
virtual bool onEscapePressed();
virtual void onUpdate(float dt);
}; // class ServerInfoDialog
#endif

View File

@ -0,0 +1,130 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "states_screens/ghost_replay_selection.hpp"
#include "replay/replay_play.hpp"
#include "states_screens/dialogs/ghost_replay_info_dialog.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/translation.hpp"
DEFINE_SCREEN_SINGLETON( GhostReplaySelection );
// ----------------------------------------------------------------------------
/** Constructor, which loads the stkgui file.
*/
GhostReplaySelection::GhostReplaySelection() : Screen("ghost_replay_selection.stkgui")
{
} // GhostReplaySelection
// ----------------------------------------------------------------------------
/** Destructor.
*/
GhostReplaySelection::~GhostReplaySelection()
{
} // GhostReplaySelection
// ----------------------------------------------------------------------------
/** Triggers a refresh of the replay file list.
*/
void GhostReplaySelection::refresh()
{
ReplayPlay::get()->loadAllReplayFile();
loadList();
} // refresh
// ----------------------------------------------------------------------------
/** Set pointers to the various widgets.
*/
void GhostReplaySelection::loadedFromFile()
{
m_replay_list_widget = getWidget<GUIEngine::ListWidget>("replay_list");
assert(m_replay_list_widget != NULL);
m_replay_list_widget->setColumnListener(this);
} // loadedFromFile
// ----------------------------------------------------------------------------
/** Clear the replay file list, which will be reloaded.
*/
void GhostReplaySelection::beforeAddingWidget()
{
m_replay_list_widget->clearColumns();
m_replay_list_widget->addColumn( _("Track"), 3 );
m_replay_list_widget->addColumn( _("Laps"), 1);
} // beforeAddingWidget
// ----------------------------------------------------------------------------
void GhostReplaySelection::init()
{
Screen::init();
refresh();
} // init
// ----------------------------------------------------------------------------
/** Loads the list of all replay files. The gui element will be
* updated.
*/
void GhostReplaySelection::loadList()
{
m_replay_list_widget->clear();
for (unsigned int i = 0; i < ReplayPlay::get()->getNumReplayFile() ; i++)
{
const ReplayBase::ReplayData rd = ReplayPlay::get()->getReplayData(i);
std::vector<GUIEngine::ListWidget::ListCell> row;
Track* t = track_manager->getTrack(rd.m_track_name);
row.push_back(GUIEngine::ListWidget::ListCell
(translations->fribidize(t->getName()) , -1, 3));
row.push_back(GUIEngine::ListWidget::ListCell
(StringUtils::toWString(rd.m_laps), -1, 1, true));
m_replay_list_widget->addItem("replay", row);
}
} // loadList
// ----------------------------------------------------------------------------
void GhostReplaySelection::eventCallback( GUIEngine::Widget* widget,
const std::string& name,
const int playerID)
{
if (name == "back")
{
race_manager->setRaceGhostKarts(false);
StateManager::get()->escapePressed();
}
else if (name == "reload")
{
refresh();
}
else if (name == m_replay_list_widget->m_properties[GUIEngine::PROP_ID])
{
unsigned int selected_index = m_replay_list_widget->getSelectionID();
// This can happen e.g. when the list is empty and the user
// clicks somewhere.
if(selected_index >= ReplayPlay::get()->getNumReplayFile() ||
selected_index < 0 )
{
return;
}
new GhostReplayInfoDialog(selected_index);
} // click on replay file
} // eventCallback

View File

@ -0,0 +1,72 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_GHOST_REPLAY_SELECTION_HPP
#define HEADER_GHOST_REPLAY_SELECTION_HPP
#include "guiengine/screen.hpp"
#include "guiengine/widgets.hpp"
namespace GUIEngine { class Widget; }
/**
* \brief GhostReplaySelection
* \ingroup states_screens
*/
class GhostReplaySelection : public GUIEngine::Screen,
public GUIEngine::ScreenSingleton<GhostReplaySelection>,
public GUIEngine::IListWidgetHeaderListener
{
friend class GUIEngine::ScreenSingleton<GhostReplaySelection>;
private:
GhostReplaySelection();
~GhostReplaySelection();
GUIEngine::ListWidget * m_replay_list_widget;
public:
void refresh();
/** Load the addons into the main list.*/
void loadList();
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void loadedFromFile() OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name,
const int playerID) OVERRIDE;
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void beforeAddingWidget() OVERRIDE;
virtual void onColumnClicked(int columnId) {};
virtual void init() OVERRIDE;
virtual void tearDown() OVERRIDE {};
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void onUpdate(float dt) OVERRIDE {};
}; // ServerSelection
#endif

View File

@ -22,6 +22,7 @@
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/widget.hpp"
#include "guiengine/widgets/button_widget.hpp"
#include "guiengine/widgets/dynamic_ribbon_widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "io/file_manager.hpp"
@ -30,9 +31,10 @@
#include "network/stk_host.hpp"
#include "race/grand_prix_data.hpp"
#include "race/grand_prix_manager.hpp"
#include "states_screens/ghost_replay_selection.hpp"
#include "states_screens/gp_info_screen.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/track_info_screen.hpp"
#include "states_screens/gp_info_screen.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/translation.hpp"
@ -137,10 +139,15 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name,
UserConfigParams::m_last_used_track_group = tabs->getSelectionIDString(0);
buildTrackList();
}
else if (name == "ghost")
{
GhostReplaySelection::getInstance()->push();
}
else if (name == "back")
{
StateManager::get()->escapePressed();
}
} // eventCallback
// -----------------------------------------------------------------------------
@ -254,6 +261,9 @@ void TracksScreen::init()
tracks_widget->setSelection(0, PLAYER_ID_GAME_MASTER, true);
}
irr_driver->unsetTextureErrorMessage();
const bool ghost_available = race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL;
getWidget<ButtonWidget>("ghost")->setVisible(ghost_available);
} // init
// -----------------------------------------------------------------------------