Added first version of (uncompressed) replays - atm only

kart positions and rotations are saved.
To save a replay, press ctrl-F10 which will create a file
in the stk config dir 'trackname'.replay .
To replay, use --ghost command line, and select the
track with the same name (at this stage the replay is
loaded even if number of laps and difficulty is different).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10897 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-02-27 05:52:17 +00:00
parent 272e99b7f4
commit bce6113fd7
20 changed files with 691 additions and 41 deletions

View File

@ -221,6 +221,8 @@ supertuxkart_SOURCES = \
karts/controller/player_controller.hpp \
karts/emergency_animation.cpp \
karts/emergency_animation.hpp \
karts/ghost_kart.cpp \
karts/ghost_lart.hpp \
karts/kart.cpp \
karts/kart.hpp \
karts/kart_gfx.cpp \
@ -317,6 +319,8 @@ supertuxkart_SOURCES = \
race/history.hpp \
race/race_manager.cpp \
race/race_manager.hpp \
replay/replay.cpp \
replay/replay.hpp \
replay/replay_base.cpp \
replay/replay_base.hpp \
replay/replay_buffer_tpl.hpp \

View File

@ -134,9 +134,7 @@ public:
float getFadeAwayStart() const { return m_fade_away_start; }
float getFadeAwayEnd () const { return m_fade_away_end; }
void setBoxSizeX (float newVal) { m_box_x = newVal; }
void setBoxSizeY (float newVal) { m_box_y = newVal; }
void setBoxSizeZ (float newVal) { m_box_z = newVal; }
void setBoxSizeXZ (float x, float z) { m_box_x = x; m_box_z = z; }
int getEmissionDecayRate() const { return m_emission_decay_rate; }

View File

@ -832,6 +832,10 @@
RelativePath="..\..\karts\emergency_animation.cpp"
>
</File>
<File
RelativePath="..\..\karts\ghost_kart.cpp"
>
</File>
<File
RelativePath="..\..\karts\kart.cpp"
>
@ -1104,6 +1108,10 @@
<Filter
Name="replay"
>
<File
RelativePath="..\..\replay\replay.cpp"
>
</File>
<File
RelativePath="..\..\replay\replay_base.cpp"
>
@ -1970,6 +1978,10 @@
RelativePath="..\..\karts\emergency_animation.hpp"
>
</File>
<File
RelativePath="..\..\karts\ghost_kart.hpp"
>
</File>
<File
RelativePath="..\..\karts\kart.hpp"
>
@ -2210,6 +2222,10 @@
<Filter
Name="replay"
>
<File
RelativePath="..\..\replay\replay.hpp"
>
</File>
<File
RelativePath="..\..\replay\replay_base.hpp"
>

View File

@ -35,6 +35,7 @@
#include "modes/profile_world.hpp"
#include "modes/world.hpp"
#include "race/history.hpp"
#include "replay/replay.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/options_screen_input2.hpp"
#include "states_screens/state_manager.hpp"
@ -234,7 +235,13 @@ void InputManager::handleStaticAction(int key, int value)
break;
case KEY_F10:
if(world && value) history->Save();
if(world && value)
{
if(control_is_pressed)
Replay::get()->Save();
else
history->Save();
}
break;
case KEY_F11:

86
src/karts/ghost_kart.cpp Normal file
View File

@ -0,0 +1,86 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 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 "karts/ghost_kart.hpp"
#include "modes/world.hpp"
#include "LinearMath/btQuaternion.h"
GhostKart::GhostKart(const std::string& ident)
: Kart(ident, /*world kart id*/99999,
/*position*/-1, /*is_first_kart*/false,
btTransform(), RaceManager::KT_GHOST)
{
m_current = 0;
} // GhostKart
// ----------------------------------------------------------------------------
void GhostKart::reset()
{
m_node->setVisible(true);
Kart::reset();
m_current = 0;
// This will set the correct start position
update(0);
} // reset
// ----------------------------------------------------------------------------
/** Sets the next time and transform. The current time and transform becomes
* the previous time and transform.
* \param
*/
void GhostKart::addTransform(float time, const btTransform &trans)
{
// FIXME: for now avoid that transforms for the same time are set
// twice (to avoid division by zero in update). This should be
// done when saving in replay
if(m_all_times.size()>0 && m_all_times.back()==time)
return;
m_all_times.push_back(time);
m_all_transform.push_back(trans);
} // addTransform
// ----------------------------------------------------------------------------
void GhostKart::update(float dt)
{
float t = World::getWorld()->getTime();
// Don't do anything at startup
if(t==0) return;
// Find (if necessary) the next index to use
while(m_current+1 < m_all_times.size() &&
t>=m_all_times[m_current+1])
{
m_current ++;
}
if(m_current+1==m_all_times.size())
{
m_node->setVisible(false);
return;
}
float f =(t - m_all_times[m_current])
/ (m_all_times[m_current+1] - m_all_times[m_current]);
setXYZ((1-f)*m_all_transform[m_current ].getOrigin()
+ f *m_all_transform[m_current+1].getOrigin() );
const btQuaternion q = m_all_transform[m_current].getRotation()
.slerp(m_all_transform[m_current+1].getRotation(),
f);
setRotation(q);
Moveable::updateGraphics(dt, Vec3(0,0,0), btQuaternion(0, 0, 0, 1));
} // update

59
src/karts/ghost_kart.hpp Normal file
View File

@ -0,0 +1,59 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 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.
#ifndef HEADER_GHOST_KART_HPP
#define HEADER_GHOST_KART_HPP
#include "karts/kart.hpp"
#include "LinearMath/btTransform.h"
#include <vector>
/** \defgroup karts */
/** A ghost kart. It does not have a phsyics representation. It gets two
* transforms from the replay objects at two consecutive time steps,
* and will interpolate between those positions depending on the current
* time
*/
class GhostKart : public Kart
{
private:
/** The list of the times at which the transform were reached. */
std::vector<float> m_all_times;
/** The transforms to assume at the corresponding time in m_all_times. */
std::vector<btTransform> m_all_transform;
/** Pointer to the last index in m_all_times that is smaller than
* the current world time. */
unsigned int m_current;
public:
GhostKart(const std::string& ident);
virtual void update (float dt);
virtual void addTransform(float time, const btTransform &trans);
virtual void reset();
// ------------------------------------------------------------------------
// Not needed to create any physics for a ghost kart.
virtual void createPhysics() {}
// ------------------------------------------------------------------------
}; // GhostKart
#endif

View File

@ -72,7 +72,7 @@
* \param init_transform The initial position and rotation for this kart.
*/
Kart::Kart (const std::string& ident, unsigned int world_kart_id,
Track* track, int position, bool is_first_kart,
int position, bool is_first_kart,
const btTransform& init_transform, RaceManager::KartType type)
: TerrainInfo(1),
Moveable(), EmergencyAnimation(this), MaxSpeed(this), m_powerup(this)
@ -183,7 +183,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
animations = false;
}
loadData(type, is_first_kart, track, animations);
loadData(type, is_first_kart, animations);
m_kart_gfx = new KartGFX(this);
reset();
@ -2019,7 +2019,7 @@ void Kart::updateFlying()
* effects (particle systems etc.)
*/
void Kart::loadData(RaceManager::KartType type, bool is_first_kart,
Track* track, bool is_animated_model)
bool is_animated_model)
{
m_node = m_kart_model->attachModel(is_animated_model);
@ -2037,15 +2037,17 @@ void Kart::loadData(RaceManager::KartType type, bool is_first_kart,
// Attach Particle System
if (type == RaceManager::KT_PLAYER && UserConfigParams::m_weather_effects &&
Track *track = World::getWorld()->getTrack();
if (type == RaceManager::KT_PLAYER &&
UserConfigParams::m_weather_effects &&
track->getSkyParticles() != NULL)
{
track->getSkyParticles()->setBoxSizeX(150.0f);
track->getSkyParticles()->setBoxSizeZ(150.0f);
track->getSkyParticles()->setBoxSizeXZ(150.0f, 150.0f);
m_sky_particles_emitter = new ParticleEmitter(track->getSkyParticles(),
core::vector3df(0.0f, 40.0f, 100.0f),
getNode());
m_sky_particles_emitter =
new ParticleEmitter(track->getSkyParticles(),
core::vector3df(0.0f, 40.0f, 100.0f),
getNode());
// FIXME: in multiplayer mode, this will result in several instances
// of the heightmap being calculated and kept in memory

View File

@ -216,14 +216,14 @@ protected:
public:
Kart(const std::string& ident, unsigned int world_kart_id,
Track* track, int position, bool is_first_kart,
int position, bool is_first_kart,
const btTransform& init_transform, RaceManager::KartType type);
virtual ~Kart();
void loadData(RaceManager::KartType type, bool is_first_kart, Track* track,
void loadData(RaceManager::KartType type, bool is_first_kart,
bool animatedModel);
virtual void updateGraphics(float dt, const Vec3& off_xyz,
const btQuaternion& off_rotation);
void createPhysics ();
virtual void createPhysics ();
bool isInRest () const;
void setSuspensionLength();
void applyEngineForce (float force);

View File

@ -21,11 +21,11 @@
#include "items/item.hpp"
KartWithStats::KartWithStats(const std::string& ident,
unsigned int world_kart_id, Track* track,
unsigned int world_kart_id,
int position, bool is_first_kart,
const btTransform& init_transform,
RaceManager::KartType type)
: Kart(ident, world_kart_id, track, position, is_first_kart,
: Kart(ident, world_kart_id, position, is_first_kart,
init_transform, type)
{
reset();

View File

@ -66,7 +66,7 @@ private:
public:
KartWithStats(const std::string& ident,
unsigned int world_kart_id, Track* track,
unsigned int world_kart_id,
int position, bool is_first_kart,
const btTransform& init_transform,
RaceManager::KartType type);

View File

@ -172,6 +172,7 @@
#include "race/highscore_manager.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
#include "replay/replay.hpp"
#include "states_screens/story_mode_lobby.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
@ -390,6 +391,7 @@ void cmdLineHelp (char* invocation)
" --profile-time=n Enable automatic driven profile mode for n "
"seconds.\n"
" --no-graphics Do not display the actual race.\n"
" --ghost Replay ghost data together with one player kart."
// " --history Replay history file 'history.dat'.\n"
// " --history=n Replay history file 'history.dat' using:\n"
// " n=1: recorded positions\n"
@ -911,6 +913,13 @@ int handleCmdLine(int argc, char **argv)
race_manager->setNumLaps(1);
}
}
else if( !strcmp(argv[i], "--ghost"))
{
Replay::get()->doReplay();
// Force the no-start screen flag, since this initialises
// the player structures correctly.
UserConfigParams::m_no_start_screen = true;
}
else if( sscanf(argv[i], "--history=%d", &n)==1)
{
history->doReplayHistory( (History::HistoryReplayMode)n);
@ -1049,6 +1058,7 @@ void initRest()
// The order here can be important, e.g. KartPropertiesManager needs
// defaultKartProperties, which are defined in stk_config.
history = new History ();
Replay::create();
material_manager = new MaterialManager ();
track_manager = new TrackManager ();
kart_properties_manager = new KartPropertiesManager();
@ -1112,6 +1122,7 @@ void cleanSuperTuxKart()
if(track_manager) delete track_manager;
if(material_manager) delete material_manager;
if(history) delete history;
Replay::destroy();
if(sfx_manager) delete sfx_manager;
if(music_manager) delete music_manager;
delete ParticleKindManager::get();

View File

@ -93,7 +93,6 @@ Kart *ProfileWorld::createKart(const std::string &kart_ident, int index,
Kart *new_kart = new KartWithStats(kart_ident,
/*world kart id*/ index,
m_track,
/*position*/ index+1,
/*is_first_kart*/false,
init_pos,

View File

@ -46,6 +46,7 @@
#include "race/highscore_manager.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
#include "replay/replay.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/race_gui_base.hpp"
#include "states_screens/race_gui.hpp"
@ -128,7 +129,6 @@ void World::init()
m_physics = new Physics();
unsigned int num_karts = race_manager->getNumberOfKarts();
assert(num_karts > 0);
// Load the track models - this must be done before the karts so that the
@ -137,18 +137,22 @@ void World::init()
for(unsigned int i=0; i<num_karts; i++)
{
const std::string& kart_ident =
history->replayHistory() ? history->getKartIdent(i)
: race_manager->getKartIdent(i);
int local_player_id = race_manager->getKartLocalPlayerId(i);
int global_player_id = race_manager->getKartGlobalPlayerId(i);
std::string kart_ident = history->replayHistory()
? history->getKartIdent(i)
: race_manager->getKartIdent(i);
int local_player_id = race_manager->getKartLocalPlayerId(i);
int global_player_id = race_manager->getKartGlobalPlayerId(i);
Kart* newkart = createKart(kart_ident, i, local_player_id,
global_player_id);
global_player_id,
race_manager->getKartType(i));
m_karts.push_back(newkart);
m_track->adjustForFog(newkart->getNode());
} // for i
if(Replay::get()->isReplay())
Replay::get()->Load();
resetAllKarts();
// Note: track reset must be called after all karts exist, since check
// objects need to allocate data structures depending on the number
@ -156,6 +160,7 @@ void World::init()
m_track->reset();
if(!history->replayHistory()) history->initRecording();
if(!Replay::get()->isReplay()) Replay::get()->initRecording();
network_manager->worldLoaded();
powerup_manager->updateWeightsForRace(num_karts);
@ -186,15 +191,16 @@ void World::createRaceGUI()
* this player globally (i.e. including network players).
*/
Kart *World::createKart(const std::string &kart_ident, int index,
int local_player_id, int global_player_id)
int local_player_id, int global_player_id,
RaceManager::KartType kart_type)
{
int position = index+1;
btTransform init_pos = m_track->getStartTransform(index);
Kart *new_kart = new Kart(kart_ident, index,m_track, position,
Kart *new_kart = new Kart(kart_ident, index, position,
(local_player_id == 0), init_pos,
race_manager->getKartType(index));
Controller *controller = NULL;
switch(race_manager->getKartType(index))
switch(kart_type)
{
case RaceManager::KT_PLAYER:
controller = new PlayerController(new_kart,
@ -629,6 +635,7 @@ void World::update(float dt)
#endif
history->update(dt);
Replay::get()->update(dt);
if(history->replayHistory()) dt=history->getNextDelta();
WorldStatus::update(dt);
// Clear race state so that new information can be stored
@ -905,7 +912,8 @@ void World::restartRace()
{
(*i)->reset();
}
if(Replay::get()->isReplay())
Replay::get()->reset();
resetAllKarts();
// Start music from beginning
@ -919,6 +927,7 @@ void World::restartRace()
race_manager->reset();
// Make sure to overwrite the data from the previous race.
if(!history->replayHistory()) history->initRecording();
if(!Replay::get()->isReplay()) Replay::get()->initRecording();
} // restartRace

View File

@ -96,7 +96,8 @@ protected:
loadAIController (Kart *kart);
virtual Kart *createKart(const std::string &kart_ident, int index,
int local_player_id, int global_player_id);
int local_player_id, int global_player_id,
RaceManager::KartType type);
/** Pointer to the track. The track is managed by world. */
Track* m_track;

View File

@ -24,11 +24,11 @@
from the network manager.
*/
NetworkKart::NetworkKart(const std::string &kart_name,
unsigned int world_kart_id, Track* track,
int position,
const btTransform &init_transform, int global_player_id,
unsigned int world_kart_id, int position,
const btTransform &init_transform,
int global_player_id,
RaceManager::KartType type)
: Kart(kart_name, world_kart_id, track, position,
: Kart(kart_name, world_kart_id, position,
/*is_first_kart*/false, init_transform, type)
{
m_global_player_id = global_player_id;

View File

@ -29,8 +29,7 @@ private:
int m_global_player_id; // to identify this kart to the network manager
public:
NetworkKart(const std::string& kart_name, unsigned int world_kart_id,
Track* track, int position,
const btTransform& init_transform,
int position, const btTransform& init_transform,
int global_player_id, RaceManager::KartType type);
void setControl(const KartControl& kc);
virtual bool isNetworkKart() const { return true; }

View File

@ -44,7 +44,7 @@ History::History()
void History::startReplay()
{
Load();
} // initReplay
} // startReplay
//-----------------------------------------------------------------------------
/** Initialise the history for a new recording. It especially allocates memory

View File

@ -195,8 +195,7 @@ public:
static const int DIFFICULTY_COUNT = 3;
/** Different kart types: A local player, a player connected via network,
* an AI kart, the leader kart (currently not used), a ghost kart
* (currently not used). */
* an AI kart, the leader kart (currently not used), a ghost kart. */
enum KartType { KT_PLAYER, KT_NETWORK_PLAYER, KT_AI, KT_LEADER, KT_GHOST };
private:

345
src/replay/replay.cpp Normal file
View File

@ -0,0 +1,345 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006 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 "replay/replay.hpp"
#include <stdio.h>
#include "io/file_manager.hpp"
#include "modes/world.hpp"
#include "karts/ghost_kart.hpp"
#include "karts/kart.hpp"
#include "physics/physics.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
bool Replay::m_do_replay = false;
Replay *Replay::m_replay = NULL;
//-----------------------------------------------------------------------------
/** Initialises the Replay engine
*/
Replay::Replay()
{
m_next = 0;
m_num_ghost_karts = 0;
} // Replay
//-----------------------------------------------------------------------------
/** Frees all stored data. */
Replay::~Replay()
{
m_events.clear();
} // ~Replay
//-----------------------------------------------------------------------------
/** Starts replay from the replay file in the current directory.
*/
void Replay::initReplay()
{
m_next = 0;
Load();
} // initReplayd
//-----------------------------------------------------------------------------
/** Initialise the replay for a new recording. It especially allocates memory
* to store the replay data.
*/
void Replay::initRecording()
{
unsigned int size = stk_config->m_max_history
* race_manager->getNumberOfKarts();
m_events.resize(size);
m_next = 0;
m_ghost_karts.clear();
} // initRecording
//-----------------------------------------------------------------------------
/** Resets all ghost karts back to start position.
*/
void Replay::reset()
{
m_next = 0;
for(unsigned int i=0; i<m_ghost_karts.size(); i++)
{
m_ghost_karts[i]->reset();
}
} // reset
//-----------------------------------------------------------------------------
/** Depending on mode either saves the data for the current time step, or
* replays the data.
* /param dt Time step.
*/
void Replay::update(float dt)
{
if(m_do_replay)
updateReplay(dt);
else
updateRecording(dt);
} // update
//-----------------------------------------------------------------------------
/** Saves the current replay data.
* \param dt Time step size.
*/
void Replay::updateRecording(float dt)
{
World *world = World::getWorld();
unsigned int num_karts = world->getNumKarts();
if(m_next + num_karts>=m_events.size())
{
printf("Can't store more replay information.\n");
return;
}
// Once we use interpolate results, we don't have to increase
// m_next by num_karts, so count how often to increase
unsigned int count = 0;
for(unsigned int i=0; i<num_karts; i++)
{
ReplayEvent *p = &(m_events[m_next+i]);
const Kart *kart = world->getKart(i);
p->m_time = World::getWorld()->getTime();
p->m_type = ReplayEvent::EV_TRANSFORM;
p->m_kart_id = i;
p->m_event.m_t = kart->getTrans();
count ++;
} // for i
m_next += count;
} // updateRecording
//-----------------------------------------------------------------------------
/** Updates all ghost karts.
* \param dt Time step size.
*/
void Replay::updateReplay(float dt)
{
// First update all ghost karts
for(unsigned int i=0; i<m_ghost_karts.size(); i++)
m_ghost_karts[i]->update(dt);
} // updateReplay
//-----------------------------------------------------------------------------
/** Saves the replay data stored in the internal data structures.
*/
void Replay::Save()
{
std::string filename = file_manager->getConfigDir()+"/"
+ race_manager->getTrackName()+".replay";
FILE *fd = fopen(filename.c_str(),"w");
if(!fd)
{
filename = race_manager->getTrackName()+".replay";
fd = fopen(filename.c_str(), "w");
}
if(!fd)
{
printf("Can't open '%s' for writing - can't save replay data.\n",
filename.c_str());
return;
}
printf("Replay saved in '%s'.\n", filename.c_str());
World *world = World::getWorld();
int num_karts = world->getNumKarts();
fprintf(fd, "Version: %s\n", STK_VERSION);
fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty());
fprintf(fd, "track: %s\n", world->getTrack()->getIdent().c_str());
fprintf(fd, "Laps: %d\n", race_manager->getNumLaps());
fprintf(fd, "numkarts: %d\n", num_karts);
int k;
for(k=0; k<num_karts; k++)
{
fprintf(fd, "model %d: %s\n",k, world->getKart(k)->getIdent().c_str());
}
fprintf(fd, "size: %d\n", m_next);
for(unsigned int i=0; i<m_events.size(); i++)
{
const ReplayEvent *p=&(m_events[i]);
if(p->m_type==ReplayEvent::EV_TRANSFORM)
fprintf(fd, "%d %f %d %f %f %f %f %f %f %f\n",
p->m_type, p->m_time, p->m_kart_id,
p->m_event.m_t.getOrigin().getX(),
p->m_event.m_t.getOrigin().getY(),
p->m_event.m_t.getOrigin().getZ(),
p->m_event.m_t.getRotation().getX(),
p->m_event.m_t.getRotation().getY(),
p->m_event.m_t.getRotation().getZ(),
p->m_event.m_t.getRotation().getW()
);
} // for k
fprintf(fd, "Replay file end.\n");
fclose(fd);
} // Save
//-----------------------------------------------------------------------------
/** Loads a replay data from file called 'trackname'.replay.
*/
void Replay::Load()
{
char s[1024], s1[1024];
int n;
std::string filename = file_manager->getConfigDir()+"/"
+ race_manager->getTrackName() + ".replay";
FILE *fd = fopen(filename.c_str(),"r");
if(!fd)
{
filename = race_manager->getTrackName()+".replay";
fd = fopen(filename.c_str(), "r");
if(!fd)
{
printf("Can't read '%', ghost replay disabled.\n",
filename.c_str());
m_do_replay = false;
initRecording();
return;
}
}
printf("Reading replay file '%s'.\n", filename.c_str());
if (fgets(s, 1023, fd) == NULL)
{
fprintf(stderr, "ERROR: could not read '%s'.\n", filename.c_str());
exit(-2);
}
if (sscanf(s,"Version: %s",s1)!=1)
{
fprintf(stderr, "ERROR: no Version information found in replay file"
" (bogus replay file)\n");
exit(-2);
}
else
{
if (strcmp(s1,STK_VERSION))
{
fprintf(stderr, "WARNING: replay is version '%s'\n",s1);
fprintf(stderr, " STK version is '%s'\n",STK_VERSION);
}
}
if (fgets(s, 1023, fd) == NULL)
{
fprintf(stderr, "ERROR: could not read '%s'.\n", filename.c_str());
exit(-2);
}
if(sscanf(s, "difficulty: %d",&n)!=1)
{
fprintf(stderr,"WARNING: No difficulty found in replay file.\n");
exit(-2);
}
if(race_manager->getDifficulty()!=(RaceManager::Difficulty)n)
printf("Warning, difficulty of replay is '%d', "
"while '%d' is selected.\n",
race_manager->getDifficulty(), n);
fgets(s, 1023, fd);
if(sscanf(s, "track: %s",s1)!=1)
{
fprintf(stderr,"WARNING: Track not found in replay file.\n");
}
assert(std::string(s1)==race_manager->getTrackName());
race_manager->setTrack(s1);
unsigned int num_laps;
fgets(s, 1023, fd);
if(sscanf(s, "Laps: %d",&num_laps)!=1)
{
fprintf(stderr,"WARNING: No number of laps found in replay file.\n");
exit(-2);
}
race_manager->setNumLaps(num_laps);
fgets(s, 1023, fd);
if(sscanf(s, "numkarts: %d",&m_num_ghost_karts)!=1)
{
fprintf(stderr,"WARNING: No number of karts found in replay file.\n");
exit(-2);
}
for(unsigned int i=0; i<m_num_ghost_karts; i++)
{
fgets(s, 1023, fd);
if(sscanf(s, "model %d: %s",&n, s1)!=2)
{
fprintf(stderr,
"WARNING: No model information for kart %d found.\n",
i);
exit(-2);
}
m_ghost_karts.push_back(new GhostKart(std::string(s1)));
m_kart_ident.push_back(s1);
if(i<race_manager->getNumPlayers())
{
race_manager->setLocalKartInfo(i, s1);
}
} // for i<nKarts
// FIXME: The model information is currently ignored
fgets(s, 1023, fd);
unsigned int size;
if(sscanf(s,"size: %d",&size)!=1)
{
fprintf(stderr,
"WARNING: Number of records not found in replay file.\n");
exit(-2);
}
m_events.resize(size);
m_next = 0;
for(unsigned int i=0; i<size; i++)
{
fgets(s, 1023, fd);
unsigned int kart_id;
float x, y, z, rx, ry, rz, rw, time;
// Check for EV_TRANSFORM event:
// -----------------------------
if(sscanf(s, "0 %f %d %f %f %f %f %f %f %f %f\n",
&time, &kart_id,
&x, &y, &z,
&rx, &ry, &rz, &rw
)==9)
{
btQuaternion q(rx, ry, rz, rw);
btVector3 xyz(x, y, z);
m_ghost_karts[kart_id]->addTransform(time, btTransform(q, xyz));
}
else
{
// Invalid record found
// ---------------------
fprintf(stderr, "Can't read replay data line %d:\n", i);
fprintf(stderr, "%s", s);
fprintf(stderr, "Ignored.\n");
}
} // for k
fprintf(fd, "Replay file end.\n");
fclose(fd);
} // Load

115
src/replay/replay.hpp Normal file
View File

@ -0,0 +1,115 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012 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.
#ifndef HEADER_REPLAY_HPP
#define HEADER_REPLAY_HPP
#include "LinearMath/btTransform.h"
#include "utils/no_copy.hpp"
#include <vector>
class GhostKart;
/**
* \ingroup race
*/
class Replay : public NoCopy
{
private:
struct ReplayEvent
{
/** The id of the kart for which triggers this event. */
unsigned int m_kart_id;
/** Time at which this event happens. */
float m_time;
enum {EV_TRANSFORM, EV_NONE} m_type;
union
{
struct
{
btTransform m_t;
};
struct
{
int m_int_test; int a; int b;
};
} m_event; // union
}; // ReplayEvent
// ------------------------------------------------------------------------
/** The array storing all events. */
std::vector<ReplayEvent> m_events;
/** Static pointer to the one instance of the replay object. */
static Replay *m_replay;
/** True if a replay is done. */
static bool m_do_replay;
/** Points to the next free entry. */
unsigned int m_next;
/** Number of (ghost) karts contained in the replay file. */
unsigned int m_num_ghost_karts;
/** The identities of the karts to use. */
std::vector<std::string> m_kart_ident;
/** All ghost karts. */
std::vector<GhostKart*> m_ghost_karts;
void updateRecording (float dt);
void updateReplay(float dt);
Replay();
~Replay();
public:
void initReplay();
void initRecording();
void update(float dt);
void reset();
void Save();
void Load();
// ------------------------------------------------------------------------
/** Creates a new instance of the replay object. */
static void create() { m_replay = new Replay(); }
// ------------------------------------------------------------------------
/** Returns the instance of the replay object. */
static Replay *get() { assert(m_replay); return m_replay; }
// ------------------------------------------------------------------------
/** Delete the instance of the replay object. */
static void destroy() { delete m_replay; m_replay=NULL; }
// ------------------------------------------------------------------------
/** Sets that a replay is to be done. */
static void doReplay() { m_do_replay = true; }
// ------------------------------------------------------------------------
/** Returns if a replay is to be done. */
static bool isReplay() { return m_do_replay; }
// ------------------------------------------------------------------------
/** Returns the identifier of the n-th kart. */
const std::string& getKartIdent(unsigned int n) const
{
return m_kart_ident[n];
}
// ------------------------------------------------------------------------
/** Returns the number of karts contained in the replay file. */
unsigned int getNumberGhostKarts() const { return m_num_ghost_karts;}
}; // Replay
#endif