Refactored replay by splitting it into one base class, and dedicated
classes for recording and for replaying. Added compression (by use of interpolation) settings to stk_config - atm only using a certain frequency (delta-t setting in stk_config) is used. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10904 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
4f1961044c
commit
3fc43d4d14
@ -52,6 +52,15 @@
|
||||
<!-- Mostly for debugging: maximum number of history entries -->
|
||||
<history max-frames="10000"/>
|
||||
|
||||
<!-- Replay related values, mostly concerned with saving less data
|
||||
and using interpolation instead.
|
||||
delta-t Minumum time between saving consecutive transform events.
|
||||
delta-pos If the interpolated position is within this delta, a
|
||||
transform event is not generated.
|
||||
delta-angle If the interpolated angle is within this delta,
|
||||
a transform event is not generated. -->
|
||||
<replay delta-t="0.05" delta-pos="0.1" delta-angle="0.5" />
|
||||
|
||||
<!-- Skidmark data: maximum number of skid marks, and
|
||||
time for skidmarks to fade out. -->
|
||||
<skid-marks max-number="100" fadeout-time="60"/>
|
||||
|
@ -138,7 +138,12 @@ void STKConfig::load(const std::string &filename)
|
||||
CHECK_NEG(m_leader_time_per_kart, "leader time-per-kart" );
|
||||
CHECK_NEG(m_penalty_time, "penalty-time" );
|
||||
CHECK_NEG(m_max_display_news, "max-display-news" );
|
||||
CHECK_NEG(m_replay_delta_angle, "replay delta-angle" );
|
||||
CHECK_NEG(m_replay_delta_pos2, "replay delta-position" );
|
||||
CHECK_NEG(m_replay_dt, "replay delta-t" );
|
||||
|
||||
// Square distance to make distance checks cheaper (no sqrt)
|
||||
m_replay_delta_pos2 *= m_replay_delta_pos2;
|
||||
m_default_kart_properties->checkAllSet(filename);
|
||||
} // load
|
||||
|
||||
@ -167,6 +172,9 @@ void STKConfig::init_defaults()
|
||||
m_min_track_version = -100;
|
||||
m_max_track_version = -100;
|
||||
m_max_display_news = -100;
|
||||
m_replay_delta_angle = -100;
|
||||
m_replay_delta_pos2 = -100;
|
||||
m_replay_dt = -100;
|
||||
m_title_music = NULL;
|
||||
m_enable_networking = true;
|
||||
m_smooth_normals = false;
|
||||
@ -345,6 +353,12 @@ void STKConfig::getAllData(const XMLNode * root)
|
||||
if(const XMLNode *networking_node= root->getNode("networking"))
|
||||
networking_node->get("enable", &m_enable_networking);
|
||||
|
||||
if(const XMLNode *replay_node = root->getNode("replay"))
|
||||
{
|
||||
replay_node->get("delta-angle", &m_replay_delta_angle);
|
||||
replay_node->get("delta-pos", &m_replay_delta_pos2 );
|
||||
replay_node->get("delta-t", &m_replay_dt );
|
||||
}
|
||||
// Get the default KartProperties
|
||||
// ------------------------------
|
||||
const XMLNode *node = root -> getNode("general-kart-defaults");
|
||||
|
@ -119,8 +119,21 @@ public:
|
||||
* position is computed. */
|
||||
std::vector<int> m_score_increase;
|
||||
|
||||
/** Filename of the title music to play.*/
|
||||
MusicInformation
|
||||
*m_title_music; /**<Filename of the title music to play.*/
|
||||
*m_title_music;
|
||||
|
||||
/** Minimum time between consecutive saved tranform events. */
|
||||
float m_replay_dt;
|
||||
|
||||
/** Maximum difference between interpolated and actual position. If the
|
||||
* difference is larger than this, a new event is generated. */
|
||||
float m_replay_delta_pos2;
|
||||
|
||||
/** A heading difference of more than that will trigger a new event to
|
||||
* be generated. */
|
||||
float m_replay_delta_angle;
|
||||
|
||||
private:
|
||||
/** True if stk_config has been loaded. This is necessary if the
|
||||
* --stk-config command line parameter has been specified to avoid
|
||||
|
@ -1109,7 +1109,15 @@
|
||||
Name="replay"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay.cpp"
|
||||
RelativePath="..\..\replay\replay_base.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay_play.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay_recorder.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
@ -2207,7 +2215,15 @@
|
||||
Name="replay"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay.hpp"
|
||||
RelativePath="..\..\replay\replay_base.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay_play.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\replay\replay_recorder.hpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "replay/replay.hpp"
|
||||
#include "replay/replay_recorder.hpp"
|
||||
#include "states_screens/kart_selection.hpp"
|
||||
#include "states_screens/options_screen_input2.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
@ -237,8 +237,8 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
case KEY_F10:
|
||||
if(world && value)
|
||||
{
|
||||
if(control_is_pressed)
|
||||
Replay::get()->Save();
|
||||
if(control_is_pressed && ReplayRecorder::get())
|
||||
ReplayRecorder::get()->Save();
|
||||
else
|
||||
history->Save();
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void GhostKart::update(float dt)
|
||||
{
|
||||
m_current ++;
|
||||
}
|
||||
if(m_current+1==m_all_times.size())
|
||||
if(m_current+1>=m_all_times.size())
|
||||
{
|
||||
m_node->setVisible(false);
|
||||
return;
|
||||
|
@ -172,7 +172,8 @@
|
||||
#include "race/highscore_manager.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "replay/replay.hpp"
|
||||
#include "replay/replay_play.hpp"
|
||||
#include "replay/replay_recorder.hpp"
|
||||
#include "states_screens/story_mode_lobby.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "states_screens/dialogs/message_dialog.hpp"
|
||||
@ -915,7 +916,7 @@ int handleCmdLine(int argc, char **argv)
|
||||
}
|
||||
else if( !strcmp(argv[i], "--ghost"))
|
||||
{
|
||||
Replay::get()->doReplay();
|
||||
ReplayPlay::create();
|
||||
// Force the no-start screen flag, since this initialises
|
||||
// the player structures correctly.
|
||||
UserConfigParams::m_no_start_screen = true;
|
||||
@ -1058,7 +1059,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();
|
||||
ReplayRecorder::create();
|
||||
material_manager = new MaterialManager ();
|
||||
track_manager = new TrackManager ();
|
||||
kart_properties_manager = new KartPropertiesManager();
|
||||
@ -1122,7 +1123,7 @@ void cleanSuperTuxKart()
|
||||
if(track_manager) delete track_manager;
|
||||
if(material_manager) delete material_manager;
|
||||
if(history) delete history;
|
||||
Replay::destroy();
|
||||
ReplayRecorder::destroy();
|
||||
if(sfx_manager) delete sfx_manager;
|
||||
if(music_manager) delete music_manager;
|
||||
delete ParticleKindManager::get();
|
||||
|
@ -46,7 +46,8 @@
|
||||
#include "race/highscore_manager.hpp"
|
||||
#include "race/history.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "replay/replay.hpp"
|
||||
#include "replay/replay_play.hpp"
|
||||
#include "replay/replay_recorder.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
#include "states_screens/race_gui.hpp"
|
||||
@ -150,8 +151,8 @@ void World::init()
|
||||
|
||||
} // for i
|
||||
|
||||
if(Replay::get()->isReplay())
|
||||
Replay::get()->Load();
|
||||
if(ReplayPlay::get())
|
||||
ReplayPlay::get()->Load();
|
||||
|
||||
resetAllKarts();
|
||||
// Note: track reset must be called after all karts exist, since check
|
||||
@ -160,7 +161,7 @@ void World::init()
|
||||
m_track->reset();
|
||||
|
||||
if(!history->replayHistory()) history->initRecording();
|
||||
if(!Replay::get()->isReplay()) Replay::get()->initRecording();
|
||||
if(ReplayRecorder::get()) ReplayRecorder::get()->init();
|
||||
network_manager->worldLoaded();
|
||||
|
||||
powerup_manager->updateWeightsForRace(num_karts);
|
||||
@ -635,7 +636,8 @@ void World::update(float dt)
|
||||
#endif
|
||||
|
||||
history->update(dt);
|
||||
Replay::get()->update(dt);
|
||||
if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt);
|
||||
if(ReplayPlay::get()) ReplayPlay::get()->update(dt);
|
||||
if(history->replayHistory()) dt=history->getNextDelta();
|
||||
WorldStatus::update(dt);
|
||||
// Clear race state so that new information can be stored
|
||||
@ -912,8 +914,8 @@ void World::restartRace()
|
||||
{
|
||||
(*i)->reset();
|
||||
}
|
||||
if(Replay::get()->isReplay())
|
||||
Replay::get()->reset();
|
||||
if(ReplayPlay::get())
|
||||
ReplayPlay::get()->reset();
|
||||
resetAllKarts();
|
||||
|
||||
// Start music from beginning
|
||||
@ -927,7 +929,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();
|
||||
if(ReplayRecorder::get()) ReplayRecorder::get()->init();
|
||||
|
||||
} // restartRace
|
||||
|
||||
|
@ -1,347 +0,0 @@
|
||||
//
|
||||
// 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 "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"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
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 '%s', 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\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
|
||||
|
@ -1,108 +0,0 @@
|
||||
//
|
||||
// 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 <string>
|
||||
#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;
|
||||
struct {
|
||||
btTransform m_t;
|
||||
} 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
|
46
src/replay/replay_base.cpp
Normal file
46
src/replay/replay_base.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
// 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 "replay/replay_base.hpp"
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
ReplayBase::ReplayBase()
|
||||
{
|
||||
m_filename = "";
|
||||
} // ReplayBaese
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Opens a replay file (depending on the track name, which is taken from
|
||||
* the race manager).
|
||||
* \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->getConfigDir()+"/"
|
||||
+ race_manager->getTrackName()+".replay";
|
||||
FILE *fd = fopen(m_filename.c_str(), writeable ? "w" : "r");
|
||||
if(!fd)
|
||||
{
|
||||
m_filename = race_manager->getTrackName()+".replay";
|
||||
fd = fopen(m_filename.c_str(), writeable ? "w" : "r");
|
||||
}
|
||||
return fd;
|
||||
|
||||
} // openReplayFilen
|
61
src/replay/replay_base.hpp
Normal file
61
src/replay/replay_base.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
//
|
||||
// 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_BASE_HPP
|
||||
#define HEADER_REPLAY_BASE_HPP
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "utils/no_copy.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* \ingroup race
|
||||
*/
|
||||
class ReplayBase : public NoCopy
|
||||
{
|
||||
private:
|
||||
/** The filename of the replay file. Only defined after calling
|
||||
* openReplayFile. */
|
||||
std::string m_filename;
|
||||
protected:
|
||||
/** Stores a transform event, i.e. a position and rotation of a kart
|
||||
* at a certain time. */
|
||||
struct TransformEvent
|
||||
{
|
||||
/** Time at which this event happens. */
|
||||
float m_time;
|
||||
/** The transform at a certain time. */
|
||||
btTransform m_transform;
|
||||
}; // TransformEvent
|
||||
// ------------------------------------------------------------------------
|
||||
ReplayBase();
|
||||
// ------------------------------------------------------------------------
|
||||
FILE *openReplayFile(bool writeable);
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the filename that was opened. */
|
||||
const std::string &getReplayFilename() const { return m_filename;}
|
||||
// ----------------------------------------------------------------------
|
||||
/** 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; }
|
||||
}; // ReplayBase
|
||||
|
||||
#endif
|
230
src/replay/replay_play.cpp
Normal file
230
src/replay/replay_play.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
//
|
||||
// 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 "replay/replay_play.hpp"
|
||||
|
||||
#include "config/stk_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "karts/ghost_kart.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
ReplayPlay *ReplayPlay::m_replay_play = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Initialises the Replay engine
|
||||
*/
|
||||
ReplayPlay::ReplayPlay()
|
||||
{
|
||||
m_next = 0;
|
||||
} // ReplayPlay
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Frees all stored data. */
|
||||
ReplayPlay::~ReplayPlay()
|
||||
{
|
||||
} // ~Replay
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Starts replay from the replay file in the current directory.
|
||||
*/
|
||||
void ReplayPlay::init()
|
||||
{
|
||||
m_next = 0;
|
||||
Load();
|
||||
} // init
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Resets all ghost karts back to start position.
|
||||
*/
|
||||
void ReplayPlay::reset()
|
||||
{
|
||||
m_next = 0;
|
||||
for(unsigned int i=0; i<m_ghost_karts.size(); i++)
|
||||
{
|
||||
m_ghost_karts[i]->reset();
|
||||
}
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates all ghost karts.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void ReplayPlay::update(float dt)
|
||||
{
|
||||
// First update all ghost karts
|
||||
for(unsigned int i=0; i<m_ghost_karts.size(); i++)
|
||||
m_ghost_karts[i]->update(dt);
|
||||
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads a replay data from file called 'trackname'.replay.
|
||||
*/
|
||||
void ReplayPlay::Load()
|
||||
{
|
||||
for(unsigned int i=0; i<m_ghost_karts.size(); i++)
|
||||
{
|
||||
delete m_ghost_karts[i];
|
||||
}
|
||||
m_ghost_karts.clear();
|
||||
char s[1024], s1[1024];
|
||||
int n;
|
||||
|
||||
FILE *fd = openReplayFile(/*writeable*/false);
|
||||
if(!fd)
|
||||
{
|
||||
printf("Can't read '%s', ghost replay disabled.\n",
|
||||
getReplayFilename().c_str());
|
||||
destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Reading replay file '%s'.\n", getReplayFilename().c_str());
|
||||
|
||||
if (fgets(s, 1023, fd) == NULL)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not read '%s'.\n",
|
||||
getReplayFilename().c_str());
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
int version;
|
||||
if (sscanf(s,"Version: %d", &version)!=1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: no Version information found in replay file"
|
||||
" (bogus replay file)\n");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
if (version!=getReplayVersion())
|
||||
{
|
||||
fprintf(stderr, "WARNING: replay is version '%d'\n",version);
|
||||
fprintf(stderr, " STK version is '%s'\n",getReplayVersion());
|
||||
fprintf(stderr, " We try to proceed, but it may fail.\n");
|
||||
}
|
||||
|
||||
if (fgets(s, 1023, fd) == NULL)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not read '%s'.\n",
|
||||
getReplayFilename().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);
|
||||
unsigned int num_ghost_karts;
|
||||
if(sscanf(s, "numkarts: %d",&num_ghost_karts)!=1)
|
||||
{
|
||||
fprintf(stderr,"WARNING: No number of karts found in replay file.\n");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
for(unsigned int k=0; k<num_ghost_karts; k++)
|
||||
{
|
||||
fgets(s, 1023, fd);
|
||||
if(sscanf(s, "model %d: %s",&n, s1)!=2)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"WARNING: No model information for kart %d found.\n",
|
||||
k);
|
||||
exit(-2);
|
||||
}
|
||||
if(n != k)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"WARNING: Expected kart id %d, got %d - ignored.\n",
|
||||
k, n);
|
||||
}
|
||||
m_ghost_karts.push_back(new GhostKart(std::string(s1)));
|
||||
|
||||
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 "
|
||||
"for kart %d.\n",
|
||||
k);
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<size; i++)
|
||||
{
|
||||
fgets(s, 1023, fd);
|
||||
float x, y, z, rx, ry, rz, rw, time;
|
||||
|
||||
// Check for EV_TRANSFORM event:
|
||||
// -----------------------------
|
||||
if(sscanf(s, "%f %f %f %f %f %f %f %f\n",
|
||||
&time,
|
||||
&x, &y, &z,
|
||||
&rx, &ry, &rz, &rw
|
||||
)==8)
|
||||
{
|
||||
btQuaternion q(rx, ry, rz, rw);
|
||||
btVector3 xyz(x, y, z);
|
||||
m_ghost_karts[k]->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
|
||||
|
||||
} // for i<nKarts
|
||||
|
||||
fprintf(fd, "Replay file end.\n");
|
||||
fclose(fd);
|
||||
} // Load
|
||||
|
62
src/replay/replay_play.hpp
Normal file
62
src/replay/replay_play.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// 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__PLAY_HPP
|
||||
#define HEADER_REPLAY__PLAY_HPP
|
||||
|
||||
#include "replay/replay_base.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class GhostKart;
|
||||
|
||||
/**
|
||||
* \ingroup replay
|
||||
*/
|
||||
class ReplayPlay : public ReplayBase
|
||||
{
|
||||
private:
|
||||
static ReplayPlay *m_replay_play;
|
||||
|
||||
/** Points to the next free entry. */
|
||||
unsigned int m_next;
|
||||
|
||||
/** All ghost karts. */
|
||||
std::vector<GhostKart*> m_ghost_karts;
|
||||
|
||||
ReplayPlay();
|
||||
~ReplayPlay();
|
||||
public:
|
||||
void init();
|
||||
void update(float dt);
|
||||
void reset();
|
||||
void Load();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Creates a new instance of the replay object. */
|
||||
static void create() { m_replay_play = new ReplayPlay(); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the instance of the replay object. */
|
||||
static ReplayPlay *get() { return m_replay_play; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Delete the instance of the replay object. */
|
||||
static void destroy() { delete m_replay_play; m_replay_play=NULL; }
|
||||
}; // Replay
|
||||
|
||||
#endif
|
166
src/replay/replay_recorder.cpp
Normal file
166
src/replay/replay_recorder.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
//
|
||||
// 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 "replay/replay_recorder.hpp"
|
||||
|
||||
#include "config/stk_config.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "karts/ghost_kart.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Initialises the Replay engine
|
||||
*/
|
||||
ReplayRecorder::ReplayRecorder()
|
||||
{
|
||||
} // ReplayRecorder
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Frees all stored data. */
|
||||
ReplayRecorder::~ReplayRecorder()
|
||||
{
|
||||
m_transform_events.clear();
|
||||
} // ~Replay
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Initialise the replay recorder. It especially allocates memory
|
||||
* to store the replay data.
|
||||
*/
|
||||
void ReplayRecorder::init()
|
||||
{
|
||||
m_transform_events.clear();
|
||||
m_transform_events.resize(race_manager->getNumberOfKarts());
|
||||
for(unsigned int i=0; i<race_manager->getNumberOfKarts(); i++)
|
||||
{
|
||||
m_transform_events[i].resize(stk_config->m_max_history);
|
||||
}
|
||||
m_count_transforms.clear();
|
||||
m_count_transforms.resize(race_manager->getNumberOfKarts(), 0);
|
||||
m_last_saved_time.clear();
|
||||
m_last_saved_time.resize(race_manager->getNumberOfKarts(), -1.0f);
|
||||
|
||||
#ifdef DEBUG
|
||||
m_count = 0;
|
||||
m_count_skipped_time = 0;
|
||||
m_count_skipped_interpolation = 0;
|
||||
#endif
|
||||
} // init
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Resets all ghost karts back to start position.
|
||||
*/
|
||||
void ReplayRecorder::reset()
|
||||
{
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Saves the current replay data.
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void ReplayRecorder::update(float dt)
|
||||
{
|
||||
World *world = World::getWorld();
|
||||
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
|
||||
unsigned int count = 0;
|
||||
for(unsigned int i=0; i<num_karts; i++)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
m_count ++;
|
||||
#endif
|
||||
if(time - m_last_saved_time[i]<stk_config->m_replay_dt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
m_count_skipped_time ++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
m_count_transforms[i]++;
|
||||
const Kart *kart = world->getKart(i);
|
||||
if(m_count_transforms[i]>=m_transform_events[i].size())
|
||||
{
|
||||
// Only print this message once.
|
||||
if(m_count_transforms[i]==m_transform_events[i].size())
|
||||
printf("Can't store more events for kart %s.\n",
|
||||
kart->getIdent().c_str());
|
||||
continue;
|
||||
}
|
||||
TransformEvent *p = &(m_transform_events[i][m_count_transforms[i]-1]);
|
||||
p->m_time = World::getWorld()->getTime();
|
||||
p->m_transform = kart->getTrans();
|
||||
} // for i
|
||||
} // updateRecording
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Saves the replay data stored in the internal data structures.
|
||||
*/
|
||||
void ReplayRecorder::Save()
|
||||
{
|
||||
FILE *fd = openReplayFile(/*writeable*/true);
|
||||
if(!fd)
|
||||
{
|
||||
printf("Can't open '%s' for writing - can't save replay data.\n",
|
||||
getReplayFilename().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Replay saved in '%s'.\n", getReplayFilename().c_str());
|
||||
|
||||
World *world = World::getWorld();
|
||||
unsigned int num_karts = world->getNumKarts();
|
||||
fprintf(fd, "Version: %d\n", getReplayVersion());
|
||||
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);
|
||||
|
||||
for(unsigned int 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_count_transforms[k]);
|
||||
|
||||
for(unsigned int i=0; i<m_count_transforms[k]; i++)
|
||||
{
|
||||
const TransformEvent *p=&(m_transform_events[k][i]);
|
||||
fprintf(fd, "%f %f %f %f %f %f %f %f\n",
|
||||
p->m_time,
|
||||
p->m_transform.getOrigin().getX(),
|
||||
p->m_transform.getOrigin().getY(),
|
||||
p->m_transform.getOrigin().getZ(),
|
||||
p->m_transform.getRotation().getX(),
|
||||
p->m_transform.getRotation().getY(),
|
||||
p->m_transform.getRotation().getZ(),
|
||||
p->m_transform.getRotation().getW()
|
||||
);
|
||||
} // for k
|
||||
}
|
||||
fprintf(fd, "Replay file end.\n");
|
||||
fclose(fd);
|
||||
} // Save
|
||||
|
80
src/replay/replay_recorder.hpp
Normal file
80
src/replay/replay_recorder.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// 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_RECORDER_HPP
|
||||
#define HEADER_REPLAY_RECORDER_HPP
|
||||
|
||||
#include "replay/replay_base.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \ingroup replay
|
||||
*/
|
||||
class ReplayRecorder : public ReplayBase
|
||||
{
|
||||
private:
|
||||
|
||||
/** A separate vector of Replay Events for all transforms. */
|
||||
std::vector< std::vector<TransformEvent> > m_transform_events;
|
||||
|
||||
/** Time at which a transform was saved for the last time. */
|
||||
std::vector<float> m_last_saved_time;
|
||||
|
||||
/** Counts the number of transform events for each kart. */
|
||||
std::vector<unsigned int> m_count_transforms;
|
||||
|
||||
/** Static pointer to the one instance of the replay object. */
|
||||
static ReplayRecorder *m_replay_recorder;
|
||||
|
||||
#ifdef DEBUG
|
||||
/** Counts overall number of events stored. */
|
||||
unsigned int m_count;
|
||||
|
||||
/** Counts number of events skipped due to minimum time between events. */
|
||||
unsigned int m_count_skipped_time;
|
||||
|
||||
/** Counts number of events skipped due to interpolation. */
|
||||
unsigned int m_count_skipped_interpolation;
|
||||
#endif
|
||||
|
||||
|
||||
ReplayRecorder();
|
||||
~ReplayRecorder();
|
||||
public:
|
||||
void init();
|
||||
void update(float dt);
|
||||
void reset();
|
||||
void Save();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Creates a new instance of the replay object. */
|
||||
static void create() {
|
||||
assert(!m_replay_recorder);
|
||||
m_replay_recorder = new ReplayRecorder();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the instance of the replay object. Returns NULL if no
|
||||
* recorder is available, i.e. recording can be disabled. */
|
||||
static ReplayRecorder *get() { return m_replay_recorder; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Delete the instance of the replay object. */
|
||||
static void destroy() { delete m_replay_recorder; m_replay_recorder=NULL; }
|
||||
}; // ReplayRecorder
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user