// // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2006-2015 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 "race/history.hpp" #include #include "io/file_manager.hpp" #include "modes/world.hpp" #include "karts/abstract_kart.hpp" #include "physics/physics.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" History* history = 0; //----------------------------------------------------------------------------- /** Initialises the history object and sets the mode to none. */ History::History() { m_replay_mode = HISTORY_NONE; } // History //----------------------------------------------------------------------------- /** Starts replay from the history file in the current directory. */ void History::startReplay() { Load(); } // startReplay //----------------------------------------------------------------------------- /** Initialise the history for a new recording. It especially allocates memory * to store the history. */ void History::initRecording() { unsigned int max_frames = (unsigned int)( stk_config->m_replay_max_time / stk_config->m_replay_dt ); allocateMemory(max_frames); m_current = -1; m_wrapped = false; m_size = 0; } // initRecording //----------------------------------------------------------------------------- /** Allocates memory for the history. This is used when recording as well * as when replaying (since in replay the data is read into memory first). * \param number_of_frames Maximum number of frames to store. */ void History::allocateMemory(int number_of_frames) { m_all_deltas.resize (number_of_frames); unsigned int num_karts = race_manager->getNumberOfKarts(); m_all_controls.resize (number_of_frames*num_karts); m_all_xyz.resize (number_of_frames*num_karts); m_all_rotations.resize(number_of_frames*num_karts); } // allocateMemory //----------------------------------------------------------------------------- /** Depending on mode either saves the data for the current time step, or * replays the data. * /param dt Time step. */ void History::update(float dt) { if(m_replay_mode==HISTORY_NONE) updateSaving(dt); else updateReplay(dt); } // update //----------------------------------------------------------------------------- /** Saves the current history. * \param dt Time step size. */ void History::updateSaving(float dt) { m_current++; if(m_current>=(int)m_all_deltas.size()) { m_wrapped = true; m_current = 0; } else { // m_size must be m_all_deltas.size() or smaller if(m_size<(int)m_all_deltas.size()) m_size ++; } m_all_deltas[m_current] = dt; World *world = World::getWorld(); unsigned int num_karts = world->getNumKarts(); unsigned int index = m_current*num_karts; for(unsigned int i=0; igetKart(i); m_all_controls[index+i] = kart->getControls(); m_all_xyz[index+i] = kart->getXYZ(); m_all_rotations[index+i] = kart->getVisualRotation(); } // for i } // updateSaving //----------------------------------------------------------------------------- /** Sets the kart position and controls to the recorded history value. * \param dt Time step size. */ void History::updateReplay(float dt) { m_current++; World *world = World::getWorld(); if(m_current>=(int)m_all_deltas.size()) { Log::info("History", "Replay finished"); m_current = 0; // Note that for physics replay all physics parameters // need to be reset, e.g. velocity, ... world->reset(); } unsigned int num_karts = world->getNumKarts(); for(unsigned k=0; kgetKart(k); unsigned int index=m_current*num_karts+k; if(m_replay_mode==HISTORY_POSITION) { kart->setXYZ(m_all_xyz[index]); kart->setRotation(m_all_rotations[index]); } else { kart->setControls(m_all_controls[index]); } } } // updateReplay //----------------------------------------------------------------------------- /** Saves the history stored in the internal data structures into a file called * history.dat. */ void History::Save() { FILE *fd = fopen("history.dat","w"); if(fd) Log::info("History", "Saved in ./history.dat."); else { std::string fn = file_manager->getUserConfigFile("history.dat"); fd = fopen(fn.c_str(), "w"); if(fd) Log::info("History", "Saved in '%s'.", fn.c_str()); } if(!fd) { Log::info("History", "Can't open history.dat file for writing - can't save history."); Log::info("History", "Make sure history.dat in the current directory " "or the config directory is writable."); return; } World *world = World::getWorld(); const int num_karts = world->getNumKarts(); fprintf(fd, "Version: %s\n", STK_VERSION); fprintf(fd, "numkarts: %d\n", num_karts); fprintf(fd, "numplayers: %d\n", race_manager->getNumPlayers()); fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty()); fprintf(fd, "reverse: %c\n", race_manager->getReverseTrack() ? 'y' : 'n'); fprintf(fd, "track: %s\n", world->getTrack()->getIdent().c_str()); assert(num_karts > 0); int k; for(k=0; kgetKart(k)->getIdent().c_str()); } fprintf(fd, "size: %d\n", m_size); int index = m_wrapped ? m_current : 0; for(int i=0; igetUserConfigFile("history.dat"); fd = fopen(fn.c_str(), "r"); if(fd) Log::info("History", "Reading '%s'.", fn.c_str()); } if(!fd) Log::fatal("History", "Could not open history.dat"); if (fgets(s, 1023, fd) == NULL) Log::fatal("History", "Could not read history.dat."); if (sscanf(s,"Version: %1023s",s1)!=1) Log::fatal("History", "No Version information found in history file (bogus history file)."); else if (strcmp(s1,STK_VERSION)) Log::warn("History", "History is version '%s', STK version is '%s'.", s1, STK_VERSION); if (fgets(s, 1023, fd) == NULL) Log::fatal("History", "Could not read history.dat."); unsigned int num_karts; if(sscanf(s, "numkarts: %u", &num_karts)!=1) Log::fatal("History", "No number of karts found in history file."); race_manager->setNumKarts(num_karts); fgets(s, 1023, fd); if(sscanf(s, "numplayers: %d",&n)!=1) Log::fatal("History", "No number of players found in history file."); race_manager->setNumLocalPlayers(n); fgets(s, 1023, fd); if(sscanf(s, "difficulty: %d",&n)!=1) Log::fatal("History", "No difficulty found in history file."); race_manager->setDifficulty((RaceManager::Difficulty)n); // Optional (not supported in older history files): include reverse fgets(s, 1023, fd); char r; if (sscanf(s, "reverse: %c", &r) == 1) { fgets(s, 1023, fd); race_manager->setReverseTrack(r == 'y'); } if(sscanf(s, "track: %1023s",s1)!=1) Log::warn("History", "Track not found in history file."); race_manager->setTrack(s1); // This value doesn't really matter, but should be defined, otherwise // the racing phase can switch to 'ending' race_manager->setNumLaps(10); for(unsigned int i=0; igetNumPlayers()) { race_manager->setLocalKartInfo(i, s1); } } // for i