first iteration of new game mode code. most things seem okay, maybe high scores/rankings should be double-checked, also the end-of-race screen seems a bit messed up

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2287 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria 2008-09-20 23:45:22 +00:00
parent 164a7b87cf
commit 67c514f1ed
17 changed files with 1140 additions and 1726 deletions

View File

@ -144,6 +144,8 @@ supertuxkart_SOURCES = main.cpp \
gui/feature_unlocked.cpp gui/feature_unlocked.hpp \
gui/font.hpp gui/font.cpp \
robots/default_robot.cpp robots/default_robot.hpp \
modes/follow_the_leader.cpp modes/follow_the_leader.hpp \
modes/standard_race.cpp modes/standard_race.hpp \
replay_buffer_tpl.hpp \
replay_buffers.hpp replay_buffers.cpp \
replay_base.hpp replay_base.cpp \
@ -162,9 +164,5 @@ supertuxkart_LDADD = -L. -lstatic_ssg \
-lplibjs -lplibsl -lplibssg -lplibpu -lplibfnt -lplibsg \
-lplibul -lplibssgaux $(bullet_LIBS) $(enet_LIBS) $(opengl_LIBS) $(sdl_LIBS) $(openal_LIBS)
.PHONY: pot
pot:
xgettext -o supertuxkart.pot -k_ --c++ *.?pp */*.?pp
SUBDIRS = robots

View File

@ -161,7 +161,7 @@ void Camera::update (float dt)
kart_hpr.setRoll(0.0f);
// Only adjust the pitch if it's not the race start, otherwise
// the camera will change pitch during ready-set-go.
if(world->isRacePhase())
if(world->getClock().isRacePhase())
{
// If the terrain pitch is 'significantly' different from the camera angle,
// start adjusting the camera. This helps with steep declines, where

View File

@ -311,14 +311,6 @@ std::string FileManager::getHighscoreFile(const std::string& fname) const
{
return getHomeDir()+"/"+fname;
} // getHighscoreFile
//-----------------------------------------------------------------------------
#ifdef HAVE_GHOST_REPLAY
std::string FileManager::getReplayFile(const std::string& fname) const
{
return m_root_dir+"/replay/"+fname;
} // getReplayFile
#endif
//-----------------------------------------------------------------------------
void FileManager::initConfigDir()
{

View File

@ -59,9 +59,6 @@ public:
std::string getSFXFile (const std::string& fname) const;
std::string getFontFile (const std::string& fname) const;
std::string getModelFile (const std::string& fname) const;
#ifdef HAVE_GHOST_REPLAY
std::string getReplayFile (const std::string& fname) const;
#endif
void listFiles(std::set<std::string>& result, const std::string& dir,
bool is_full_path=false, bool make_full_path=false)

View File

@ -260,8 +260,9 @@ void RaceGUI::drawFPS ()
//-----------------------------------------------------------------------------
void RaceGUI::drawTimer ()
{
if(world->getPhase()!=World::RACE_PHASE &&
world->getPhase()!=World::DELAY_FINISH_PHASE ) return;
// if(world->getPhase() != RACE_PHASE &&
// world->getPhase() != DELAY_FINISH_PHASE ) return;
if(!world->shouldDrawTimer()) return;
char str[256];
assert(world != NULL);
@ -922,7 +923,7 @@ void RaceGUI::drawStatusText(const float dt)
glOrtho ( 0, user_config->m_width, 0, user_config->m_height, 0, 100 ) ;
switch (world->getPhase())
{
case World::READY_PHASE:
case READY_PHASE:
{
GLfloat const COLORS[] = { 0.9f, 0.66f, 0.62f, 1.0f };
//I18N: as in "ready, set, go", shown at the beginning of the race
@ -932,7 +933,7 @@ void RaceGUI::drawStatusText(const float dt)
COLORS );
}
break;
case World::SET_PHASE:
case SET_PHASE:
{
GLfloat const COLORS[] = { 0.9f, 0.9f, 0.62f, 1.0f };
//I18N: as in "ready, set, go", shown at the beginning of the race
@ -942,7 +943,7 @@ void RaceGUI::drawStatusText(const float dt)
COLORS );
}
break;
case World::GO_PHASE:
case GO_PHASE:
{
GLfloat const COLORS[] = { 0.39f, 0.82f, 0.39f, 1.0f };
//I18N: as in "ready, set, go", shown at the beginning of the race
@ -968,7 +969,7 @@ void RaceGUI::drawStatusText(const float dt)
// The penalty message needs to be displayed for up to one second
// after the start of the race, otherwise it disappears if
// "Go" is displayed and the race starts
if(world->isStartPhase() || world->getTime()<1.0f)
if(world->getClock().isStartPhase() || world->getTime()<1.0f)
{
for(unsigned int i=0; i<race_manager->getNumLocalPlayers(); i++)
{
@ -989,7 +990,7 @@ void RaceGUI::drawStatusText(const float dt)
if(race_manager->getNumLocalPlayers() >= 3)
split_screen_ratio_x = 0.5;
if ( world->isRacePhase() )
if ( world->getClock().isRacePhase() )
{
const int numPlayers = race_manager->getNumLocalPlayers();

File diff suppressed because it is too large Load Diff

View File

@ -120,11 +120,11 @@ void MainLoop::run()
if(user_config->m_profile) dt=1.0f/60.0f;
// In the first call dt might be large (includes loading time),
// which can cause the camera to significantly tilt
scene->draw(world->getPhase()==World::SETUP_PHASE ? 0.0f : dt);
scene->draw(world->getPhase()==SETUP_PHASE ? 0.0f : dt);
network_manager->receiveUpdates();
if ( world->getPhase() != World::LIMBO_PHASE)
if ( world->getPhase() != LIMBO_PHASE)
{
world->update(dt);

View File

@ -0,0 +1,114 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modes/follow_the_leader.hpp"
#include "unlock_manager.hpp"
#include "gui/menu_manager.hpp"
#include "user_config.hpp"
// -----------------------------
FollowTheLeaderRace::FollowTheLeaderRace() : World(), ClockListener()
{
m_leader_intervals = stk_config->m_leader_intervals;
m_clock.registerEventListener(this);
m_clock.setMode(COUNTDOWN, m_leader_intervals[0]);
}
// ----------------------------
FollowTheLeaderRace::~FollowTheLeaderRace()
{
}
void FollowTheLeaderRace::restartRace()
{
World::restartRace();
m_leader_intervals = stk_config->m_leader_intervals;
}
// clock events
void FollowTheLeaderRace::countdownReachedZero()
{
}
void FollowTheLeaderRace::onGo()
{
// Reset the brakes now that the prestart
// phase is over (braking prevents the karts
// from sliding downhill)
for(unsigned int i=0; i<m_kart.size(); i++)
{
m_kart[i]->resetBrakes();
}
}
void FollowTheLeaderRace::update(float delta)
{
m_clock.updateClock(delta);
World::update(delta);
if(!m_clock.isRacePhase()) return;
if(m_clock.getTime() < 0.0f)
{
if(m_leader_intervals.size()>1)
m_leader_intervals.erase(m_leader_intervals.begin());
m_clock.setTime(m_leader_intervals[0]);
int kart_number;
// If the leader kart is not the first kart, remove the first
// kart, otherwise remove the last kart.
int position_to_remove = m_kart[0]->getPosition()==1
? getCurrentNumKarts() : 1;
for (kart_number=0; kart_number<(int)m_kart.size(); kart_number++)
{
if(m_kart[kart_number]->isEliminated()) continue;
if(m_kart[kart_number]->getPosition()==position_to_remove)
break;
}
if(kart_number==(int)m_kart.size())
{
fprintf(stderr,"Problem with removing leader: position %d not found\n",
position_to_remove);
for(int i=0; i<(int)m_kart.size(); i++)
{
fprintf(stderr,"kart %d: eliminated %d position %d\n",
i,m_kart[i]->isEliminated(), m_kart[i]->getPosition());
} // for i
} // kart_number==m_kart.size()
else
{
removeKart(kart_number);
}
// The follow the leader race is over if there is only one kart left,
// or if all players have gone
if(getCurrentNumKarts()==2 || getCurrentNumPlayers()==0)
{
// Add the results for the remaining kart
for(int i=1; i<(int)race_manager->getNumKarts(); i++)
if(!m_kart[i]->isEliminated())
race_manager->RaceFinished(m_kart[i], m_clock.getTime());
m_clock.raceOver();
return;
}
}
}
void FollowTheLeaderRace::onTerminate()
{
World::terminateRace();
}

View File

@ -0,0 +1,41 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef _follow_the_leader_hpp_
#define _follow_the_leader_hpp_
#include "world.hpp"
class FollowTheLeaderRace : public World, public ClockListener
{
std::vector<float> m_leader_intervals; // time till elimination in follow leader
public:
FollowTheLeaderRace();
~FollowTheLeaderRace();
// clock events
virtual void countdownReachedZero();
virtual void onGo();
virtual void onTerminate();
// overriding World methods
virtual void update(float delta);
virtual void restartRace();
};
#endif

View File

@ -0,0 +1,82 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "modes/standard_race.hpp"
#include "user_config.hpp"
#include "unlock_manager.hpp"
#include "gui/menu_manager.hpp"
// -----------------------------
StandardRace::StandardRace() : World(), ClockListener()
{
m_clock.registerEventListener(this);
m_clock.setMode(CHRONO);
}
// ----------------------------
StandardRace::~StandardRace()
{
}
// clock events
void StandardRace::countdownReachedZero() { }
void StandardRace::onGo()
{
// Reset the brakes now that the prestart
// phase is over (braking prevents the karts
// from sliding downhill)
for(unsigned int i=0; i<m_kart.size(); i++)
{
m_kart[i]->resetBrakes();
}
}
void StandardRace::restartRace()
{
World::restartRace();
}
void StandardRace::update(float delta)
{
m_clock.updateClock(delta);
World::update(delta);
if(!m_clock.isRacePhase()) return;
// =========================================================
if(race_manager->getFinishedKarts() >= race_manager->getNumKarts() )
{
m_clock.raceOver();
if(user_config->m_profile<0) printProfileResultAndExit();
unlock_manager->raceFinished();
} // if all karts are finished
// All player karts are finished, but computer still racing
// ===========================================================
else if(race_manager->allPlayerFinished())
{
// Set delay mode to have time for camera animation, and
// to give the AI some time to get non-estimated timings
m_clock.raceOver(true /* delay */);
}
}
void StandardRace::onTerminate()
{
World::terminateRace();
}

View File

@ -0,0 +1,39 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef _standard_race_
#define _standard_race_
#include "world.hpp"
class StandardRace : public World, public ClockListener
{
public:
StandardRace();
~StandardRace();
// clock events
virtual void countdownReachedZero();
virtual void onGo();
virtual void onTerminate();
// overriding World methods
virtual void update(float delta);
virtual void restartRace();
};
#endif

View File

@ -171,7 +171,7 @@ void PlayerKart::update(float dt)
{
steer(dt, m_steer_val);
if(world->isStartPhase())
if(world->getClock().isStartPhase())
{
if(m_controls.accel!=0.0 || m_controls.brake!=false ||
m_controls.fire|m_controls.wheelie|m_controls.jump)

View File

@ -30,6 +30,8 @@
#include "user_config.hpp"
#include "stk_config.hpp"
#include "network/network_manager.hpp"
#include "modes/standard_race.hpp"
#include "modes/follow_the_leader.hpp"
RaceManager* race_manager= NULL;
@ -231,7 +233,15 @@ void RaceManager::startNextRace()
// variable world. Admittedly a bit ugly, but simplifies
// handling of objects which get created in the constructor
// and need world to be defined.
new World();
/*
RM_GRAND_PRIX, RM_SINGLE,
RM_QUICK_RACE, RM_TIME_TRIAL, RM_FOLLOW_LEADER
FIXME
*/
if(m_minor_mode==RM_FOLLOW_LEADER) new FollowTheLeaderRace();
else new StandardRace();
m_active_race = true;
} // startNextRace

View File

@ -97,7 +97,7 @@ void DefaultRobot::update( float delta )
return;
}
if( world->isStartPhase())
if( world->getClock().isStartPhase() )
{
handle_race_start();
AutoKart::update( delta );
@ -513,7 +513,7 @@ void DefaultRobot::handle_rescue(const float DELTA)
// check if kart is stuck
if(getSpeed()<2.0f && !isRescue() && !world->isStartPhase())
if(getSpeed()<2.0f && !isRescue() && !world->getClock().isStartPhase())
{
m_time_since_stuck += DELTA;
if(m_time_since_stuck > 2.0f)

View File

@ -167,7 +167,7 @@ public:
bool m_no_start_screen;
bool m_smoke;
bool m_display_fps;
int m_profile; // Positive number: time in seconds, neg: # laps
int m_profile; // Positive number: time in seconds, neg: # laps. (used to profile AI)
bool m_print_kart_sizes; // print all kart sizes
// 0 if no profiling. Never saved in config file!
bool m_skidding;

View File

@ -50,35 +50,174 @@
#include "network/network_manager.hpp"
#include "network/race_state.hpp"
#ifdef HAVE_GHOST_REPLAY
# include "replay_player.hpp"
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
# define snprintf _snprintf
#endif
World* world = 0;
//-----------------------------------------------------------------------------
Clock::Clock()
{
m_mode = CHRONO;
m_time = 0.0f;
m_auxiliary_timer = 0.0f;
m_listener = NULL;
m_phase = SETUP_PHASE;
m_previous_phase = SETUP_PHASE; // initialise it just in case
// for profiling AI
m_phase = user_config->m_profile ? RACE_PHASE : SETUP_PHASE;
// FIXME - is it a really good idea to reload and delete the sound every race??
m_prestart_sound = sfx_manager->newSFX(SFXManager::SOUND_PRESTART);
m_start_sound = sfx_manager->newSFX(SFXManager::SOUND_START);
}
//-----------------------------------------------------------------------------
void Clock::reset()
{
m_time = 0.0f;
m_auxiliary_timer = 0.0f;
m_phase = READY_PHASE; // FIXME - unsure
m_previous_phase = SETUP_PHASE;
}
//-----------------------------------------------------------------------------
Clock::~Clock()
{
sfx_manager->deleteSFX(m_prestart_sound);
sfx_manager->deleteSFX(m_start_sound);
}
//-----------------------------------------------------------------------------
void Clock::setMode(const ClockType mode, const float initial_time)
{
m_mode = mode;
m_time = initial_time;
}
//-----------------------------------------------------------------------------
void Clock::raceOver(const bool delay)
{
if(m_phase == DELAY_FINISH_PHASE or m_phase == FINISH_PHASE) return; // we already know
if(delay)
{
m_phase = DELAY_FINISH_PHASE;
m_auxiliary_timer = 0.0f;
}
else
m_phase = FINISH_PHASE;
}
//-----------------------------------------------------------------------------
void Clock::updateClock(const float dt)
{
switch(m_phase)
{
// Note: setup phase must be a separate phase, since the race_manager
// checks the phase when updating the camera: in the very first time
// step dt is large (it includes loading time), so the camera might
// tilt way too much. A separate setup phase for the first frame
// simplifies this handling
case SETUP_PHASE:
m_auxiliary_timer = 0.0f;
m_phase = READY_PHASE;
m_prestart_sound->play();
return; // loading time, don't play sound yet
case READY_PHASE:
if(m_auxiliary_timer>1.0)
{
m_phase=SET_PHASE;
m_prestart_sound->play();
}
m_auxiliary_timer += dt;
return;
case SET_PHASE :
if(m_auxiliary_timer>2.0)
{
// set phase is over, go to the next one
m_phase=GO_PHASE;
m_start_sound->play();
assert(m_listener != NULL);
m_listener -> onGo();
}
m_auxiliary_timer += dt;
return;
case GO_PHASE :
if(m_auxiliary_timer>3.0) // how long to display the 'go' message
m_phase=RACE_PHASE;
m_auxiliary_timer += dt;
break;
case DELAY_FINISH_PHASE :
{
m_auxiliary_timer += dt;
// Nothing more to do if delay time is not over yet
if(m_auxiliary_timer < TIME_DELAY_TILL_FINISH) break;
m_phase = FINISH_PHASE;
break;
}
case FINISH_PHASE:
m_listener->onTerminate();
return;
}
switch(m_mode)
{
case CHRONO:
m_time += dt;
break;
case COUNTDOWN:
m_time -= dt;
if(m_time <= 0.0)
{
assert(m_listener != NULL);
m_listener -> countdownReachedZero();
}
break;
default: break;
}
}
//-----------------------------------------------------------------------------
void Clock::setTime(const float time)
{
m_time = time;
}
//-----------------------------------------------------------------------------
void Clock::registerEventListener(ClockListener* listener)
{
m_listener = listener;
}
//-----------------------------------------------------------------------------
void Clock::pause()
{
m_previous_phase = m_phase;
m_phase = LIMBO_PHASE;
}
//-----------------------------------------------------------------------------
void Clock::unpause()
{
m_phase = m_previous_phase;
}
World* world = 0; // FIXME, use singleton or something instead of this global variable
//-----------------------------------------------------------------------------
World::World()
#ifdef HAVE_GHOST_REPLAY
, m_p_replay_player(NULL)
#endif
{
delete world;
world = this;
race_state = new RaceState();
m_phase = SETUP_PHASE;
m_previous_phase = SETUP_PHASE; // initialise it just in case
m_track = NULL;
m_clock = 0.0f;
m_faster_music_active = false;
m_fastest_lap = 9999999.9f;
m_fastest_kart = 0;
m_eliminated_karts = 0;
m_eliminated_players = 0;
m_leader_intervals = stk_config->m_leader_intervals;
m_clock.setMode( CHRONO );
// Grab the track file
try
{
@ -176,33 +315,14 @@ World::World()
callback_manager->initAll();
menu_manager->switchToRace();
m_prestart_sound = sfx_manager->newSFX(SFXManager::SOUND_PRESTART);
m_start_sound = sfx_manager->newSFX(SFXManager::SOUND_START);
m_track->startMusic();
m_phase = user_config->m_profile ? RACE_PHASE : SETUP_PHASE;
#ifdef HAVE_GHOST_REPLAY
m_replay_recorder.initRecorder( race_manager->getNumKarts() );
m_p_replay_player = new ReplayPlayer;
if( !loadReplayHumanReadable( "test1" ) )
{
delete m_p_replay_player;
m_p_replay_player = NULL;
}
if( m_p_replay_player ) m_p_replay_player->showReplayAt( 0.0 );
#endif
network_manager->worldLoaded();
} // World
//-----------------------------------------------------------------------------
World::~World()
{
#ifdef HAVE_GHOST_REPLAY
saveReplayHumanReadable( "test" );
#endif
delete race_state;
m_track->cleanup();
// Clear all callbacks
@ -216,8 +336,6 @@ World::~World()
delete m_physics;
sound_manager -> stopMusic();
sfx_manager->deleteSFX(m_prestart_sound);
sfx_manager->deleteSFX(m_start_sound);
sgVec3 sun_pos;
sgVec4 ambient_col, specular_col, diffuse_col;
@ -230,18 +348,15 @@ World::~World()
ssgGetLight ( 0 ) -> setColour ( GL_AMBIENT , ambient_col ) ;
ssgGetLight ( 0 ) -> setColour ( GL_DIFFUSE , diffuse_col ) ;
ssgGetLight ( 0 ) -> setColour ( GL_SPECULAR, specular_col ) ;
#ifdef HAVE_GHOST_REPLAY
m_replay_recorder.destroy();
if( m_p_replay_player )
{
m_p_replay_player->destroy();
delete m_p_replay_player;
m_p_replay_player = NULL;
}
#endif
} // ~World
//-----------------------------------------------------------------------------
void World::terminateRace()
{
m_clock.pause();
menu_manager->pushMenu(MENUID_RACERESULT);
estimateFinishTimes();
unlock_manager->raceFinished();
}
//-----------------------------------------------------------------------------
/** Waits till each kart is resting on the ground
*
@ -300,21 +415,7 @@ void World::update(float dt)
// Clear race state so that new information can be stored
race_state->clear();
if(user_config->m_replay_history) dt=history->GetNextDelta();
updateRaceStatus(dt);
if( getPhase() == FINISH_PHASE )
{
if(race_manager->getMinorMode()==RaceManager::RM_FOLLOW_LEADER)
{
pause();
menu_manager->pushMenu(MENUID_RACERESULT);
unlock_manager->raceFinished();
return;
}
updateHighscores();
pause();
menu_manager->pushMenu(MENUID_RACERESULT);
}
if(!user_config->m_replay_history) history->StoreDelta(dt);
if(network_manager->getMode()!=NetworkManager::NW_CLIENT) m_physics->update(dt);
for (int i = 0 ; i <(int) m_kart.size(); ++i)
@ -335,16 +436,6 @@ void World::update(float dt)
/* Routine stuff we do even when paused */
callback_manager->update(dt);
#ifdef HAVE_GHOST_REPLAY
// we start recording after START_PHASE, since during start-phase m_clock is incremented
// normally, but after switching to RACE_PHASE m_clock is set back to 0.0
if( m_phase == GO_PHASE )
{
m_replay_recorder.pushFrame();
if( m_p_replay_player ) m_p_replay_player->showReplayAt( m_clock );
}
#endif
}
// ----------------------------------------------------------------------------
void World::updateHighscores()
@ -373,7 +464,6 @@ void World::updateHighscores()
}
}
#endif
// FIXME: end
// Only record times for player karts
if(!m_kart[index[pos]]->isPlayerKart()) continue;
@ -394,180 +484,6 @@ void World::updateHighscores()
delete []index;
} // updateHighscores
// ----------------------------------------------------------------------------
#ifdef HAVE_GHOST_REPLAY
bool World::saveReplayHumanReadable( std::string const &filename ) const
{
std::string path;
path = file_manager->getReplayFile(filename+"."+ReplayBase::REPLAY_FILE_EXTENSION_HUMAN_READABLE);
FILE *fd = fopen( path.c_str(), "w" );
if( !fd )
{
fprintf(stderr, "Error while opening replay file for writing '%s'\n", path.c_str());
return false;
}
int nKarts = world->getNumKarts();
const char *version = "unknown";
#ifdef VERSION
version = VERSION;
#endif
fprintf(fd, "Version: %s\n", version);
fprintf(fd, "numkarts: %d\n", m_kart.size());
fprintf(fd, "numplayers: %d\n", race_manager->getNumPlayers());
fprintf(fd, "difficulty: %d\n", race_manager->getDifficulty());
fprintf(fd, "track: %s\n", m_track->getIdent().c_str());
for(int i=0; i<race_manager->getNumKarts(); i++)
{
fprintf(fd, "model %d: %s\n", i, race_manager->getKartName(i).c_str());
}
if( !m_replay_recorder.saveReplayHumanReadable( fd ) )
{
fclose( fd ); fd = NULL;
return false;
}
fclose( fd ); fd = NULL;
return true;
} // saveReplayHumanReadable
#endif // HAVE_GHOST_REPLAY
#ifdef HAVE_GHOST_REPLAY
//-----------------------------------------------------------------------------
bool World::loadReplayHumanReadable( std::string const &filename )
{
assert( m_p_replay_player );
m_p_replay_player->destroy();
std::string path = file_manager->getReplayFile(filename+"."+
ReplayBase::REPLAY_FILE_EXTENSION_HUMAN_READABLE);
try
{
path = file_manager->getPath(path.c_str());
}
catch(std::runtime_error& e)
{
fprintf( stderr, "Couldn't find replay-file: '%s'\n", path.c_str() );
return false;
}
FILE *fd = fopen( path.c_str(), "r" );
if( !fd )
{
fprintf(stderr, "Error while opening replay file for loading '%s'\n", path.c_str());
return false;
}
bool blnRet = m_p_replay_player->loadReplayHumanReadable( fd );
fclose( fd ); fd = NULL;
return blnRet;
} // loadReplayHumanReadable
#endif // HAVE_GHOST_REPLAY
//-----------------------------------------------------------------------------
void World::updateRaceStatus(float dt)
{
switch (m_phase) {
// Note: setup phase must be a separate phase, since the race_manager
// checks the phase when updating the camera: in the very first time
// step dt is large (it includes loading time), so the camera might
// tilt way too much. A separate setup phase for the first frame
// simplifies this handling
case SETUP_PHASE: m_clock = 0.0f;
m_phase = READY_PHASE;
m_prestart_sound->play();
dt = 0.0f; // solves the problem of adding track loading time
return; // loading time, don't play sound yet
case READY_PHASE: if(m_clock>1.0)
{
m_phase=SET_PHASE;
m_prestart_sound->play();
}
m_clock += dt;
return;
case SET_PHASE : if(m_clock>2.0)
{
m_phase=GO_PHASE;
if(race_manager->getMinorMode()==RaceManager::RM_FOLLOW_LEADER)
m_clock=m_leader_intervals[0];
else
m_clock=0.0f;
m_start_sound->play();
// Reset the brakes now that the prestart
// phase is over (braking prevents the karts
// from sliding downhill)
for(unsigned int i=0; i<m_kart.size(); i++)
{
m_kart[i]->resetBrakes();
}
#ifdef HAVE_GHOST_REPLAY
// push positions at time 0.0 to replay-data
m_replay_recorder.pushFrame();
#endif
}
m_clock += dt;
return;
case GO_PHASE : if(race_manager->getMinorMode()==RaceManager::RM_FOLLOW_LEADER)
{
// Switch to race if more than 1 second has past
if(m_clock<m_leader_intervals[0]-1.0f)
m_phase=RACE_PHASE;
m_clock -= dt;
}
else
{
if(m_clock>1.0) // how long to display the 'go' message
m_phase=RACE_PHASE;
m_clock += dt;
}
return;
case DELAY_FINISH_PHASE :
{
m_clock += dt;
// Nothing more to do if delay time is not over yet
if(m_clock - m_finish_delay_start_time
< TIME_DELAY_TILL_FINISH) return;
m_phase = FINISH_PHASE;
estimateFinishTimes();
unlock_manager->raceFinished();
return;
}
default : break;
} // switch
if(race_manager->getMinorMode()==RaceManager::RM_FOLLOW_LEADER)
return updateLeaderMode(dt);
// The status must now be race mode!
// =================================
m_clock += dt;
// 2) A player comes in last, go immediately to finish phase
// =========================================================
if(race_manager->getFinishedKarts() >= race_manager->getNumKarts() )
{
m_phase = FINISH_PHASE;
if(user_config->m_profile<0) printProfileResultAndExit();
unlock_manager->raceFinished();
} // if all karts are finished
// 3) All player karts are finished, but computer still racing
// ===========================================================
else if(race_manager->allPlayerFinished())
{
// Set delay mode to have time for camera animation, and
// to give the AI some time to get non-estimated timings
m_phase = DELAY_FINISH_PHASE;
m_finish_delay_start_time = m_clock;
}
} // updateRaceStatus
//-----------------------------------------------------------------------------
void World::estimateFinishTimes()
@ -582,60 +498,6 @@ void World::estimateFinishTimes()
} // for i
} // estimateFinishTimes
//-----------------------------------------------------------------------------
void World::updateLeaderMode(float dt)
{
// Count 'normal' till race phase has started, then count backwards
if(m_phase==RACE_PHASE || m_phase==GO_PHASE)
m_clock -=dt;
else
m_clock +=dt;
if(m_clock<0.0f)
{
if(m_leader_intervals.size()>1)
m_leader_intervals.erase(m_leader_intervals.begin());
m_clock=m_leader_intervals[0];
int kart_number;
// If the leader kart is not the first kart, remove the first
// kart, otherwise remove the last kart.
int position_to_remove = m_kart[0]->getPosition()==1
? getCurrentNumKarts() : 1;
for (kart_number=0; kart_number<(int)m_kart.size(); kart_number++)
{
if(m_kart[kart_number]->isEliminated()) continue;
if(m_kart[kart_number]->getPosition()==position_to_remove)
break;
}
if(kart_number==(int)m_kart.size())
{
fprintf(stderr,"Problem with removing leader: position %d not found\n",
position_to_remove);
for(int i=0; i<(int)m_kart.size(); i++)
{
fprintf(stderr,"kart %d: eliminated %d position %d\n",
i,m_kart[i]->isEliminated(), m_kart[i]->getPosition());
} // for i
} // kart_number==m_kart.size()
else
{
removeKart(kart_number);
}
// The follow the leader race is over if there is only one kart left,
// or if all players have gone
if(getCurrentNumKarts()==2 ||getCurrentNumPlayers()==0)
{
// Add the results for the remaining kart
for(int i=1; i<(int)race_manager->getNumKarts(); i++)
if(!m_kart[i]->isEliminated())
race_manager->RaceFinished(m_kart[i], m_clock);
m_phase=FINISH_PHASE;
return;
}
} // m_clock<0
return;
} // updateLeaderMode
//-----------------------------------------------------------------------------
void World::printProfileResultAndExit()
{
@ -694,7 +556,7 @@ void World::removeKart(int kart_number)
// ignored in all loops). Important:world->getCurrentNumKarts() returns
// the number of karts still racing. This value can not be used for loops
// over all karts, use race_manager->getNumKarts() instead!
race_manager->RaceFinished(kart, m_clock);
race_manager->RaceFinished(kart, m_clock.getTime());
kart->eliminate();
m_eliminated_karts++;
@ -782,23 +644,25 @@ void World::loadTrack()
//-----------------------------------------------------------------------------
void World::restartRace()
{
m_clock = 0.0f;
m_phase = SETUP_PHASE;
m_previous_phase = SETUP_PHASE;
m_clock.reset();
m_faster_music_active = false;
m_eliminated_karts = 0;
m_eliminated_players = 0;
m_leader_intervals = stk_config->m_leader_intervals;
for ( Karts::iterator i = m_kart.begin(); i != m_kart.end() ; ++i )
{
(*i)->reset();
}
resetAllKarts();
sound_manager->stopMusic(); // Start music from beginning
// Start music from beginning
sound_manager->stopMusic();
m_track->startMusic();
// Enable SFX again
sfx_manager->resumeAll();
herring_manager->reset();
projectile_manager->cleanup();
race_manager->reset();
@ -806,16 +670,6 @@ void World::restartRace()
// Resets the cameras in case that they are pointing too steep up or down
scene->reset();
#ifdef HAVE_GHOST_REPLAY
m_replay_recorder.destroy();
m_replay_recorder.initRecorder( race_manager->getNumKarts() );
if( m_p_replay_player )
{
m_p_replay_player->reset();
m_p_replay_player->showReplayAt( 0.0 );
}
#endif
} // restartRace
//-----------------------------------------------------------------------------
@ -845,8 +699,7 @@ void World::pause()
{
sound_manager->pauseMusic();
sfx_manager->pauseAll();
m_previous_phase = m_phase;
m_phase = LIMBO_PHASE;
m_clock.pause();
}
//-----------------------------------------------------------------------------
@ -854,7 +707,7 @@ void World::unpause()
{
sound_manager->resumeMusic() ;
sfx_manager->resumeAll();
m_phase = m_previous_phase;
m_clock.unpause();
}
/* EOF */

View File

@ -32,11 +32,6 @@
#include "network/network_kart.hpp"
#include "utils/random_generator.hpp"
#ifdef HAVE_GHOST_REPLAY
# include "replay_recorder.hpp"
class ReplayPlayer;
#endif
class SFXBase;
/** This class is responsible for running the actual race. A world is created
@ -78,117 +73,207 @@ class SFXBase;
* would be done in the mode specific world (instead of in the
* RaceManager).
*/
class World
enum ClockType
{
CLOCK_NONE,
CHRONO, // counts up
COUNTDOWN
};
/**
* abstract base class, derive from it to receive events from the clock
*/
class ClockListener
{
public:
typedef std::vector<Kart*> Karts;
/** resources, this should be put in a separate class or replaced by a smart
* resource manager
virtual ~ClockListener(){};
/*
* Will be called to notify your derived class that the clock,
* which is in COUNTDOWN mode, has reached zero.
*/
enum Phase {
// Game setup, e.g. track loading
SETUP_PHASE,
// 'Ready' is displayed
READY_PHASE,
// 'Set' is displayed
SET_PHASE,
// 'Go' is displayed, but this is already race phase
GO_PHASE,
// the actual race has started, no ready/set/go is displayed anymore
RACE_PHASE,
// All players have finished, now wait a certain amount of time for AI
// karts to finish. If they do not finish in that time, finish the race
DELAY_FINISH_PHASE,
// The player crossed the finishing line and his and the time of
// the other players is displayed, controll is automatic
FINISH_PHASE,
// The state after finish where no calculations are done.
LIMBO_PHASE,
};
Track* m_track;
virtual void countdownReachedZero() = 0;
/** debug text that will be overlaid to the screen */
std::string m_debug_text[10];
/*
* Called when the race actually starts.
*/
virtual void onGo() = 0;
/**
* Called when race is over and should be terminated (mostly called by the clock).
*/
virtual void onTerminate() = 0;
};
World();
virtual ~World();
void update(float delta);
// Note: GO_PHASE is both: start phase and race phase
bool isStartPhase() const {return m_phase<GO_PHASE;}
bool isRacePhase() const {return m_phase>=GO_PHASE && m_phase<LIMBO_PHASE;}
void restartRace();
void disableRace(); // Put race into limbo phase
PlayerKart *getPlayerKart(int player) const { return m_player_karts[player]; }
unsigned int getCurrentNumLocalPlayers() const {return m_local_player_karts.size();}
PlayerKart *getLocalPlayerKart(int n) const { return m_local_player_karts[n]; }
NetworkKart*getNetworkKart(int n) const {return m_network_karts[n]; }
Kart *getKart(int kartId) const { assert(kartId >= 0 &&
kartId < int(m_kart.size()));
return m_kart[kartId]; }
unsigned int getCurrentNumKarts() const { return (int)m_kart.size()-
m_eliminated_karts; }
unsigned int getCurrentNumPlayers() const { return (int)m_player_karts.size()-
m_eliminated_players; }
/** Returns the phase of the game */
Phase getPhase() const { return m_phase; }
Physics *getPhysics() const { return m_physics; }
Track *getTrack() const { return m_track; }
Kart* getFastestKart() const { return m_fastest_kart; }
float getFastestLapTime() const { return m_fastest_lap; }
void setFastestLap(Kart *k, float time) {m_fastest_kart=k;m_fastest_lap=time;}
const Highscores* getHighscores() const { return m_highscores; }
float getTime() const { return m_clock; }
void pause();
void unpause();
enum Phase {
// Game setup, e.g. track loading
SETUP_PHASE,
// 'Ready' is displayed
READY_PHASE,
// 'Set' is displayed
SET_PHASE,
// 'Go' is displayed, but this is already race phase
GO_PHASE,
// the actual race has started, no ready/set/go is displayed anymore
RACE_PHASE,
// All players have finished, now wait a certain amount of time for AI
// karts to finish. If they do not finish in that time, finish the race
DELAY_FINISH_PHASE,
// The player crossed the finishing line and his and the time of
// the other players is displayed, controll is automatic
FINISH_PHASE,
// The state after finish where no calculations are done.
LIMBO_PHASE,
};
/**
* A class that manages the clock (countdown, chrono, etc.) Also manages stuff
* like the 'ready/set/go' text at the beginning or the delay at the end of a race.
*/
class Clock
{
private:
SFXBase *m_prestart_sound;
SFXBase *m_start_sound;
/**
* Elasped/remaining time in seconds
*/
float m_time;
ClockType m_mode;
/**
* This object will be called to notify it of time events. Currently,
* this is only relevant for countdown mode.
*/
ClockListener* m_listener;
Phase m_phase;
/**
* Counts time during the initial 'ready/set/go' phase, or at the end of a race.
* This timer basically kicks in when we need to calculate non-race time like labels.
*/
float m_auxiliary_timer;
/**
* Remember previous phase e.g. on pause
*/
Phase m_previous_phase;
public:
Clock();
~Clock();
void reset();
// Note: GO_PHASE is both: start phase and race phase
bool isStartPhase() const { return m_phase<GO_PHASE; }
bool isRacePhase() const { return m_phase>=GO_PHASE && m_phase<LIMBO_PHASE; }
const Phase getPhase() const { return m_phase; }
/**
* Call to specify what kind of clock you want. The second argument
* can be used to specify the initial time value (especially useful
* for countdowns)
*/
void setMode(const ClockType mode, const float initial_time=0.0f);
int getMode() const { return m_mode; }
/**
* Call each frame, with the elapsed time as argument.
*/
void updateClock(const float dt);
float getTime() const { return m_time; }
void setTime(const float time);
void pause();
void unpause();
void raceOver(const bool delay=false);
void registerEventListener(ClockListener* listener);
};
class World
{
protected:
typedef std::vector<Kart*> Karts;
std::vector<PlayerKart*> m_player_karts;
std::vector<PlayerKart*> m_local_player_karts;
std::vector<NetworkKart*> m_network_karts;
RandomGenerator m_random;
Clock m_clock;
Karts m_kart;
Physics* m_physics;
float m_fastest_lap;
Kart* m_fastest_kart;
Highscores* m_highscores;
Phase m_phase;
Phase m_previous_phase; // used during the race popup menu
float m_clock;
float m_finish_delay_start_time;
int m_eliminated_karts; // number of eliminated karts
int m_eliminated_players; // number of eliminated players
std::vector<float>
m_leader_intervals; // time till elimination in follow leader
bool m_faster_music_active; // true if faster music was activated
SFXBase *m_prestart_sound;
SFXBase *m_start_sound;
void updateRacePosition(int k);
void updateHighscores ();
void loadTrack ();
void updateRaceStatus (float dt);
void resetAllKarts ();
void removeKart (int kart_number);
Kart* loadRobot (const std::string& kart_name, int position,
const btTransform& init_pos);
void updateLeaderMode (float dt);
void printProfileResultAndExit();
void estimateFinishTimes();
#ifdef HAVE_GHOST_REPLAY
private:
bool saveReplayHumanReadable( std::string const &filename ) const;
bool loadReplayHumanReadable( std::string const &filename );
ReplayRecorder m_replay_recorder;
ReplayPlayer *m_p_replay_player;
#endif
public:
Track* m_track;
/** debug text that will be overlaid to the screen */
std::string m_debug_text[10];
World();
virtual ~World();
virtual void update(float delta);
virtual void restartRace();
void disableRace(); // Put race into limbo phase
PlayerKart *getPlayerKart(int player) const { return m_player_karts[player]; }
unsigned int getCurrentNumLocalPlayers() const { return m_local_player_karts.size(); }
PlayerKart *getLocalPlayerKart(int n) const { return m_local_player_karts[n]; }
NetworkKart *getNetworkKart(int n) const { return m_network_karts[n]; }
Kart *getKart(int kartId) const { assert(kartId >= 0 &&
kartId < int(m_kart.size()));
return m_kart[kartId]; }
unsigned int getCurrentNumKarts() const { return (int)m_kart.size() -
m_eliminated_karts; }
unsigned int getCurrentNumPlayers() const { return (int)m_player_karts.size()-
m_eliminated_players; }
Physics *getPhysics() const { return m_physics; }
Track *getTrack() const { return m_track; }
Kart* getFastestKart() const { return m_fastest_kart; }
float getFastestLapTime() const { return m_fastest_lap; }
void setFastestLap(Kart *k, float time) {m_fastest_kart=k;m_fastest_lap=time; }
const Highscores* getHighscores() const { return m_highscores; }
float getTime() const { return m_clock.getTime(); }
Phase getPhase() const { return m_clock.getPhase(); }
const Clock& getClock() const { return m_clock; }
/**
* Called when race is over and should be terminated (mostly called by the clock).
*/
void terminateRace();
void pause();
void unpause();
/**
* The code that draws the timer should call this first to know
* whether the game mode wants a timer drawn
*/
bool shouldDrawTimer() const { return ((m_clock.getPhase() == RACE_PHASE or
m_clock.getPhase() == DELAY_FINISH_PHASE) and
m_clock.getMode() != CLOCK_NONE); }
};