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:
parent
272e99b7f4
commit
bce6113fd7
@ -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 \
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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
86
src/karts/ghost_kart.cpp
Normal 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
59
src/karts/ghost_kart.hpp
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
11
src/main.cpp
11
src/main.cpp
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -44,7 +44,7 @@ History::History()
|
||||
void History::startReplay()
|
||||
{
|
||||
Load();
|
||||
} // initReplay
|
||||
} // startReplay
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Initialise the history for a new recording. It especially allocates memory
|
||||
|
@ -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
345
src/replay/replay.cpp
Normal 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
115
src/replay/replay.hpp
Normal 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
|
Loading…
Reference in New Issue
Block a user