2007-05-27 12:01:53 -04:00
|
|
|
// $Id$
|
|
|
|
//
|
|
|
|
// 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
|
2008-06-12 20:53:52 -04:00
|
|
|
// as published by the Free Software Foundation; either version 3
|
2007-05-27 12:01:53 -04:00
|
|
|
// 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.
|
|
|
|
|
2009-06-02 21:36:48 -04:00
|
|
|
#include "modes/world.hpp"
|
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
#include <assert.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <ctime>
|
|
|
|
|
2008-11-06 23:34:01 -05:00
|
|
|
#include "audio/sound_manager.hpp"
|
|
|
|
#include "audio/sfx_manager.hpp"
|
|
|
|
#include "audio/sfx_base.hpp"
|
2009-01-22 17:27:13 -05:00
|
|
|
#include "challenges/unlock_manager.hpp"
|
2009-06-11 06:00:43 -04:00
|
|
|
#include "config/user_config.hpp"
|
2009-01-23 00:23:22 -05:00
|
|
|
#include "graphics/camera.hpp"
|
2009-07-18 13:48:36 -04:00
|
|
|
#include "states_screens/state_manager.hpp"
|
|
|
|
#include "states_screens/race_gui.hpp"
|
2009-03-11 23:49:31 -04:00
|
|
|
#include "io/file_manager.hpp"
|
2008-11-06 23:34:01 -05:00
|
|
|
#include "items/projectile_manager.hpp"
|
|
|
|
#include "karts/auto_kart.hpp"
|
2010-02-14 19:54:28 -05:00
|
|
|
#include "karts/controller/default_ai_controller.hpp"
|
|
|
|
#include "karts/controller/new_ai_controller.hpp"
|
|
|
|
#include "karts/controller/player_controller.hpp"
|
|
|
|
#include "karts/controller/end_controller.hpp"
|
2008-11-06 23:34:01 -05:00
|
|
|
#include "karts/kart_properties_manager.hpp"
|
2008-09-07 09:51:44 -04:00
|
|
|
#include "network/network_manager.hpp"
|
2008-09-07 10:42:37 -04:00
|
|
|
#include "network/race_state.hpp"
|
2009-08-19 09:48:28 -04:00
|
|
|
#include "physics/btKart.hpp"
|
2009-06-02 21:36:48 -04:00
|
|
|
#include "race/highscore_manager.hpp"
|
|
|
|
#include "race/history.hpp"
|
|
|
|
#include "race/race_manager.hpp"
|
2009-01-22 17:27:13 -05:00
|
|
|
#include "tracks/track.hpp"
|
2009-01-22 07:02:40 -05:00
|
|
|
#include "tracks/track_manager.hpp"
|
2009-01-22 17:27:13 -05:00
|
|
|
#include "utils/constants.hpp"
|
2009-01-23 00:23:22 -05:00
|
|
|
#include "utils/translation.hpp"
|
2009-01-27 19:38:28 -05:00
|
|
|
#include "utils/string_utils.hpp"
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-11 02:11:52 -05:00
|
|
|
World* World::m_world = NULL;
|
|
|
|
|
2010-02-17 06:59:51 -05:00
|
|
|
/** The main world class is used to handle the track and the karts.
|
|
|
|
* The end of the race is detected in two phases: first the (abstract)
|
|
|
|
* function isRaceOver, which must be implemented by all game modes,
|
|
|
|
* must return true. In which case enterRaceOverState is called. At
|
|
|
|
* this time a winning (or losing) animation can be played. The WorldStatus
|
|
|
|
* class will in its enterRaceOverState switch to DELAY_FINISH_PHASE,
|
|
|
|
* but the remaining AI kart will keep on racing during that time.
|
|
|
|
* After a time period specified in stk_config.xml WorldStatus will
|
|
|
|
* switch to FINISH_PHASE and call terminateRace. Now the finishing status
|
|
|
|
* of all karts is set (i.e. in a normal race the arrival time for karts
|
|
|
|
* will be estimated), highscore is updated, and the race result gui
|
|
|
|
* is being displayed.
|
|
|
|
*/
|
2008-09-20 19:45:22 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
2009-08-20 20:32:24 -04:00
|
|
|
/** Constructor. Note that in the constructor it is not possible to call any
|
2010-02-17 06:59:51 -05:00
|
|
|
* functions that use World::getWorld(), since this is only defined
|
|
|
|
* after the constructor. Those functions must be called in the init()
|
2009-08-20 20:32:24 -04:00
|
|
|
* function, which is called immediately after the constructor.
|
|
|
|
*/
|
2010-02-08 07:59:03 -05:00
|
|
|
World::World() : WorldStatus()
|
2008-11-07 21:07:54 -05:00
|
|
|
{
|
2010-02-17 21:08:11 -05:00
|
|
|
m_physics = NULL;
|
|
|
|
m_race_gui = NULL;
|
|
|
|
m_use_highscores = true;
|
|
|
|
m_track = NULL;
|
|
|
|
|
2010-02-09 04:24:45 -05:00
|
|
|
WorldStatus::setClockMode(CLOCK_CHRONO);
|
2009-07-12 07:54:21 -04:00
|
|
|
} // World
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2009-08-20 20:32:24 -04:00
|
|
|
/** This function is called after the World constructor. In init() functions
|
2010-02-17 06:59:51 -05:00
|
|
|
* can be called that use World::getWorld(). The init function is
|
2009-08-20 20:32:24 -04:00
|
|
|
* called immediately after the constructor.
|
|
|
|
*/
|
2008-11-07 21:07:54 -05:00
|
|
|
void World::init()
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2008-09-07 10:42:37 -04:00
|
|
|
race_state = new RaceState();
|
2008-05-07 21:28:07 -04:00
|
|
|
m_faster_music_active = false;
|
|
|
|
m_fastest_lap = 9999999.9f;
|
|
|
|
m_fastest_kart = 0;
|
|
|
|
m_eliminated_karts = 0;
|
|
|
|
m_eliminated_players = 0;
|
2010-02-11 02:11:52 -05:00
|
|
|
m_num_players = 0;
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2009-07-29 22:40:30 -04:00
|
|
|
// Create the race gui before anything else is attached to the scene node
|
2009-08-18 06:59:21 -04:00
|
|
|
// (which happens when the track is loaded). This allows the race gui to
|
2009-07-29 22:40:30 -04:00
|
|
|
// do any rendering on texture.
|
2009-07-27 07:56:09 -04:00
|
|
|
m_race_gui = new RaceGUI();
|
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
// Grab the track file
|
|
|
|
try
|
|
|
|
{
|
1) Removed race_setup and race_mode data structures. All this
information is now only managed by the race_manager, no
more in-between objects to transfer information along.
2) The scores for grand prix are now defined in the stk_config.dat
file (10, 8, 6, 5, 4, .., 1, 0, 0) points
3) Bugfix: unlock information wasn't saved anymore. Added specific
saving after unlocking, plus re-inserted the 'generic' save
at the end of STK again.
4) bugfix/work around: Visual Studio complains about incompatible
iterators in sdldrv - apparently caused by using erase, and
then keep on using the iterator.
5) Fixed bug when running a race in a GP again (scores/times
were added each time).
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1681 178a84e3-b1eb-0310-8ba1-8eac791a3b58
2008-04-09 09:52:48 -04:00
|
|
|
m_track = track_manager->getTrack(race_manager->getTrackName());
|
2007-05-27 12:01:53 -04:00
|
|
|
}
|
|
|
|
catch(std::runtime_error)
|
|
|
|
{
|
2009-01-23 00:23:22 -05:00
|
|
|
std::ostringstream msg;
|
|
|
|
msg << "Track '" << race_manager->getTrackName() << "' not found.\n";
|
|
|
|
throw std::runtime_error(msg.str());
|
2007-05-27 12:01:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the physics
|
2008-09-19 02:07:29 -04:00
|
|
|
m_physics = new Physics();
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-10 06:40:33 -05:00
|
|
|
unsigned int num_karts = race_manager->getNumberOfKarts();
|
|
|
|
|
|
|
|
assert(num_karts > 0);
|
2007-05-27 12:01:53 -04:00
|
|
|
|
|
|
|
// Load the track models - this must be done before the karts so that the
|
|
|
|
// karts can be positioned properly on (and not in) the tracks.
|
2009-08-13 07:12:26 -04:00
|
|
|
m_track->loadTrackModel();
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-10 06:40:33 -05:00
|
|
|
for(unsigned int i=0; i<num_karts; i++)
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2008-10-14 16:20:54 -04:00
|
|
|
btTransform init_pos=m_track->getStartTransform(i);
|
2009-08-13 00:53:51 -04:00
|
|
|
const std::string& kart_ident = race_manager->getKartIdent(i);
|
|
|
|
int local_player_id = race_manager->getKartLocalPlayerId(i);
|
|
|
|
int global_player_id = race_manager->getKartGlobalPlayerId(i);
|
2009-11-29 08:04:57 -05:00
|
|
|
Kart* newkart = createKart(kart_ident, i, local_player_id,
|
2009-08-20 20:32:24 -04:00
|
|
|
global_player_id, init_pos);
|
2010-02-10 17:59:17 -05:00
|
|
|
m_karts.push_back(newkart);
|
|
|
|
newkart->setWorldKartId(m_karts.size()-1);
|
2007-05-27 12:01:53 -04:00
|
|
|
} // for i
|
|
|
|
|
|
|
|
resetAllKarts();
|
2009-08-18 06:59:21 -04:00
|
|
|
// Note: track reset must be called after all karts exist, since check
|
2009-07-06 09:35:33 -04:00
|
|
|
// objects need to allocate data structures depending on the number
|
|
|
|
// of karts.
|
|
|
|
m_track->reset();
|
2008-05-07 21:28:07 -04:00
|
|
|
m_track->startMusic();
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2008-10-06 09:40:11 -04:00
|
|
|
if(!history->replayHistory()) history->initRecording();
|
2008-09-07 10:09:52 -04:00
|
|
|
network_manager->worldLoaded();
|
2010-02-26 14:09:05 -05:00
|
|
|
|
|
|
|
// erase messages left over
|
|
|
|
RaceGUI* m = World::getWorld()->getRaceGUI();
|
|
|
|
if (m) m->clearAllMessages();
|
2009-08-20 20:32:24 -04:00
|
|
|
} // init
|
2007-05-27 12:01:53 -04:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-08-19 00:03:25 -04:00
|
|
|
/** Creates a kart, having a certain position, starting location, and local
|
|
|
|
* and global player id (if applicable).
|
|
|
|
* \param kart_ident Identifier of the kart to create.
|
|
|
|
* \param index Index of the kart.
|
|
|
|
* \param local_player_id If the kart is a player kart this is the index of
|
|
|
|
* this player on the local machine.
|
2009-11-29 08:04:57 -05:00
|
|
|
* \param global_player_id If the kart is a player kart this is the index of
|
2009-08-19 00:03:25 -04:00
|
|
|
* this player globally (i.e. including network players).
|
2010-02-17 06:59:51 -05:00
|
|
|
* \param init_pos The start transform (xyz and hpr).
|
2009-08-19 00:03:25 -04:00
|
|
|
*/
|
2009-08-19 09:45:44 -04:00
|
|
|
Kart *World::createKart(const std::string &kart_ident, int index,
|
2009-08-19 00:03:25 -04:00
|
|
|
int local_player_id, int global_player_id,
|
|
|
|
const btTransform &init_pos)
|
|
|
|
{
|
2010-02-14 19:54:28 -05:00
|
|
|
int position = index+1;
|
|
|
|
Kart *new_kart = new Kart(kart_ident, position, init_pos);
|
|
|
|
Controller *controller = NULL;
|
2009-08-19 00:03:25 -04:00
|
|
|
switch(race_manager->getKartType(index))
|
|
|
|
{
|
|
|
|
case RaceManager::KT_PLAYER:
|
2010-02-14 19:54:28 -05:00
|
|
|
controller = new PlayerController(new_kart,
|
|
|
|
StateManager::get()->getActivePlayer(local_player_id),
|
|
|
|
local_player_id);
|
2010-02-11 02:11:52 -05:00
|
|
|
m_num_players ++;
|
2009-08-19 00:03:25 -04:00
|
|
|
break;
|
2010-02-19 21:43:57 -05:00
|
|
|
case RaceManager::KT_NETWORK_PLAYER:
|
|
|
|
break; // Avoid compiler warning about enum not handled.
|
2010-02-14 19:54:28 -05:00
|
|
|
//controller = new NetworkController(kart_ident, position, init_pos,
|
|
|
|
// global_player_id);
|
|
|
|
//m_num_players++;
|
|
|
|
//break;
|
2009-08-19 00:03:25 -04:00
|
|
|
case RaceManager::KT_AI:
|
2010-02-14 19:54:28 -05:00
|
|
|
controller = loadAIController(new_kart);
|
2009-08-19 00:03:25 -04:00
|
|
|
break;
|
|
|
|
case RaceManager::KT_GHOST:
|
|
|
|
break;
|
|
|
|
case RaceManager::KT_LEADER:
|
|
|
|
break;
|
|
|
|
}
|
2010-02-14 19:54:28 -05:00
|
|
|
new_kart->setController(controller);
|
|
|
|
return new_kart;
|
2009-08-19 00:03:25 -04:00
|
|
|
} // createKart
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2010-02-14 19:54:28 -05:00
|
|
|
/** Creates an AI controller for the kart.
|
|
|
|
* \param kart The kart to be controlled by an AI.
|
|
|
|
*/
|
|
|
|
Controller* World::loadAIController(Kart *kart)
|
2009-08-19 00:03:25 -04:00
|
|
|
{
|
2010-02-14 19:54:28 -05:00
|
|
|
Controller *controller;
|
2010-01-11 18:51:59 -05:00
|
|
|
// const int NUM_ROBOTS = 1;
|
2009-11-08 07:19:33 -05:00
|
|
|
// For now: instead of random switching, use each
|
|
|
|
// robot in turns: switch(m_random.get(NUM_ROBOTS))
|
2010-01-10 07:12:07 -05:00
|
|
|
// static int turn=1;
|
|
|
|
// turn=1-turn;
|
|
|
|
|
|
|
|
// For now disable the new AI.
|
|
|
|
int turn=0;
|
2009-11-08 07:19:33 -05:00
|
|
|
switch(turn)
|
2009-08-19 00:03:25 -04:00
|
|
|
{
|
|
|
|
case 0:
|
2010-02-14 19:54:28 -05:00
|
|
|
controller = new DefaultAIController(kart);
|
2009-11-08 07:19:33 -05:00
|
|
|
break;
|
|
|
|
case 1:
|
2010-02-14 19:54:28 -05:00
|
|
|
controller = new NewAIController(kart);
|
2009-08-19 00:03:25 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
std::cerr << "Warning: Unknown robot, using default." << std::endl;
|
2010-02-14 19:54:28 -05:00
|
|
|
controller = new DefaultAIController(kart);
|
2009-08-19 00:03:25 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-14 19:54:28 -05:00
|
|
|
return controller;
|
|
|
|
} // loadAIController
|
2009-08-19 00:03:25 -04:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-08-20 20:32:24 -04:00
|
|
|
World::~World()
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2009-07-12 07:54:21 -04:00
|
|
|
delete m_race_gui;
|
2008-09-07 10:42:37 -04:00
|
|
|
delete race_state;
|
2009-01-15 18:05:50 -05:00
|
|
|
// In case that a race is aborted (e.g. track not found) m_track is 0.
|
|
|
|
if(m_track)
|
|
|
|
m_track->cleanup();
|
2007-12-08 08:04:56 -05:00
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
for ( unsigned int i = 0 ; i < m_karts.size() ; i++ )
|
|
|
|
delete m_karts[i];
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
m_karts.clear();
|
2007-05-27 12:01:53 -04:00
|
|
|
projectile_manager->cleanup();
|
2009-01-15 18:05:50 -05:00
|
|
|
// In case that the track is not found, m_physics is still undefined.
|
|
|
|
if(m_physics)
|
|
|
|
delete m_physics;
|
2007-05-27 12:01:53 -04:00
|
|
|
|
|
|
|
sound_manager -> stopMusic();
|
2010-02-11 02:11:52 -05:00
|
|
|
m_world = NULL;
|
2007-11-03 09:13:26 -04:00
|
|
|
} // ~World
|
2009-07-12 07:54:21 -04:00
|
|
|
|
2010-02-08 07:59:03 -05:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Called when 'go' is being displayed for the first time. Here the brakes
|
|
|
|
* of the karts are released.
|
|
|
|
*/
|
|
|
|
void World::onGo()
|
|
|
|
{
|
|
|
|
// Reset the brakes now that the prestart
|
|
|
|
// phase is over (braking prevents the karts
|
|
|
|
// from sliding downhill)
|
2010-02-10 17:59:17 -05:00
|
|
|
for(unsigned int i=0; i<m_karts.size(); i++)
|
2010-02-08 07:59:03 -05:00
|
|
|
{
|
2010-02-10 17:59:17 -05:00
|
|
|
m_karts[i]->resetBrakes();
|
2010-02-08 07:59:03 -05:00
|
|
|
}
|
|
|
|
} // onGo
|
|
|
|
|
2008-09-20 19:45:22 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
2010-02-09 04:24:45 -05:00
|
|
|
/** Called at the end of a race. Updates highscores, pauses the game, and
|
2010-02-17 06:59:51 -05:00
|
|
|
* informs the unlock manager about the finished race. This function must
|
|
|
|
* be called after all other stats were updated from the different game
|
|
|
|
* modes.
|
2010-02-09 04:24:45 -05:00
|
|
|
*/
|
2008-09-20 19:45:22 -04:00
|
|
|
void World::terminateRace()
|
|
|
|
{
|
2010-02-17 06:59:51 -05:00
|
|
|
// Update the estimated finishing time for all karts that haven't
|
|
|
|
// finished yet.
|
|
|
|
const unsigned int kart_amount = getNumKarts();
|
|
|
|
for(unsigned int i = 0; i < kart_amount ; i++)
|
|
|
|
{
|
|
|
|
if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated())
|
|
|
|
{
|
|
|
|
m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i]));
|
|
|
|
|
|
|
|
}
|
|
|
|
} // i<kart_amount
|
2008-09-22 21:16:50 -04:00
|
|
|
updateHighscores();
|
2010-02-08 07:59:03 -05:00
|
|
|
WorldStatus::pause();
|
2008-09-20 19:45:22 -04:00
|
|
|
unlock_manager->raceFinished();
|
2010-02-26 14:09:05 -05:00
|
|
|
|
|
|
|
RaceGUI* m = World::getWorld()->getRaceGUI();
|
|
|
|
if (m) m->clearAllMessages();
|
|
|
|
|
2010-02-17 06:59:51 -05:00
|
|
|
WorldStatus::terminateRace();
|
2010-02-08 07:59:03 -05:00
|
|
|
} // terminateRace
|
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Waits till each kart is resting on the ground
|
|
|
|
*
|
|
|
|
* Does simulation steps still all karts reach the ground, i.e. are not
|
|
|
|
* moving anymore
|
|
|
|
*/
|
|
|
|
void World::resetAllKarts()
|
|
|
|
{
|
2009-08-18 07:05:40 -04:00
|
|
|
//Project karts onto track from above. This will lower each kart so
|
|
|
|
//that at least one of its wheel will be on the surface of the track
|
2010-02-10 17:59:17 -05:00
|
|
|
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
|
2009-08-18 07:05:40 -04:00
|
|
|
{
|
2009-08-19 09:45:44 -04:00
|
|
|
///start projection from top of kart
|
2010-02-24 00:23:00 -05:00
|
|
|
btVector3 up_offset(0, 0.5f * ((*i)->getKartHeight()), 0);
|
2009-08-19 09:45:44 -04:00
|
|
|
(*i)->getVehicle()->getRigidBody()->translate (up_offset);
|
|
|
|
|
2009-08-18 07:05:40 -04:00
|
|
|
bool kart_over_ground = m_physics->projectKartDownwards(*i);
|
|
|
|
|
|
|
|
if (!kart_over_ground)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: no valid starting position for kart %d on track %s.\n",
|
2010-02-10 17:59:17 -05:00
|
|
|
(int)(i-m_karts.begin()), m_track->getIdent().c_str());
|
2009-08-18 07:05:40 -04:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
bool all_finished=false;
|
2008-02-12 22:42:18 -05:00
|
|
|
// kart->isInRest() is not fully correct, since it only takes the
|
|
|
|
// velocity in count, which might be close to zero when the kart
|
|
|
|
// is just hitting the floor, before being pushed up again by
|
|
|
|
// the suspension. So we just do a longer initial simulation,
|
|
|
|
// which should be long enough for all karts to be firmly on ground.
|
2009-08-18 07:05:40 -04:00
|
|
|
for(int i=0; i<60; i++) m_physics->update(1.f/60.f);
|
2008-07-09 20:41:26 -04:00
|
|
|
|
|
|
|
// Stil wait will all karts are in rest (and handle the case that a kart
|
|
|
|
// fell through the ground, which can happen if a kart falls for a long
|
|
|
|
// time, therefore having a high speed when hitting the ground.
|
2007-05-27 12:01:53 -04:00
|
|
|
while(!all_finished)
|
|
|
|
{
|
2007-11-21 20:20:57 -05:00
|
|
|
m_physics->update(1.f/60.f);
|
2007-05-27 12:01:53 -04:00
|
|
|
all_finished=true;
|
2010-02-10 17:59:17 -05:00
|
|
|
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2009-08-18 06:59:21 -04:00
|
|
|
if(!(*i)->isInRest())
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2008-07-09 20:41:26 -04:00
|
|
|
float hot;
|
|
|
|
Vec3 normal;
|
|
|
|
const Material *material;
|
2009-08-18 06:59:21 -04:00
|
|
|
// We can't use (*i)->getXYZ(), since this is only defined
|
2008-07-09 20:41:26 -04:00
|
|
|
// after update() was called. Instead we have to get the
|
|
|
|
// real position of the rigid body.
|
|
|
|
btTransform t;
|
|
|
|
(*i)->getBody()->getMotionState()->getWorldTransform(t);
|
2009-08-18 06:59:21 -04:00
|
|
|
// This test can not be done only once before the loop, since
|
2008-07-09 20:41:26 -04:00
|
|
|
// it can happen that the kart falls through the track later!
|
|
|
|
m_track->getTerrainInfo(t.getOrigin(), &hot, &normal, &material);
|
|
|
|
if(!material)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "ERROR: no valid starting position for kart %d on track %s.\n",
|
2010-02-10 17:59:17 -05:00
|
|
|
(int)(i-m_karts.begin()), m_track->getIdent().c_str());
|
2008-07-09 20:41:26 -04:00
|
|
|
exit(-1);
|
|
|
|
}
|
2007-05-27 12:01:53 -04:00
|
|
|
all_finished=false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // while
|
2008-01-29 23:36:00 -05:00
|
|
|
|
2008-11-06 20:05:52 -05:00
|
|
|
// Now store the current (i.e. in rest) suspension length for each kart,
|
|
|
|
// so that the karts can visualise the suspension.
|
2010-02-10 17:59:17 -05:00
|
|
|
for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
|
2008-11-06 20:05:52 -05:00
|
|
|
(*i)->setSuspensionLength();
|
2010-02-10 17:59:17 -05:00
|
|
|
for(unsigned int i=0; i<m_karts.size(); i++)
|
|
|
|
if(m_karts[i]->getCamera())
|
|
|
|
m_karts[i]->getCamera()->setInitialTransform();
|
2007-05-27 12:01:53 -04:00
|
|
|
} // resetAllKarts
|
|
|
|
|
2010-02-17 06:59:51 -05:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** This is the main interface to update the world. This function calls
|
|
|
|
* update(), and checks for the end of the race. Note that race over
|
|
|
|
* handling can not necessarily be done in update(), since not all
|
|
|
|
* data structures might have been updated (e.g.LinearWorld must
|
|
|
|
* call World::update() first, to get updated kart positions. If race
|
|
|
|
* over would be handled in World::update, LinearWorld had no opportunity
|
|
|
|
* to update its data structures before the race is finished).
|
|
|
|
* \param dt Time step size.
|
|
|
|
*/
|
|
|
|
void World::updateWorld(float dt)
|
|
|
|
{
|
|
|
|
update(dt);
|
|
|
|
if( (!isFinishPhase()) && isRaceOver())
|
|
|
|
{
|
|
|
|
enterRaceOverState();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // updateWorld
|
|
|
|
|
2010-02-23 20:24:48 -05:00
|
|
|
#define MEASURE_FPS 0
|
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
2007-12-12 09:07:26 -05:00
|
|
|
void World::update(float dt)
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2010-02-23 20:24:48 -05:00
|
|
|
#if MEASURE_FPS
|
|
|
|
static float time = 0.0f;
|
|
|
|
time += dt;
|
|
|
|
if (time > 5.0f)
|
|
|
|
{
|
|
|
|
time -= 5.0f;
|
|
|
|
printf("%i\n",irr_driver->getVideoDriver()->getFPS());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-06 09:40:11 -04:00
|
|
|
if(history->replayHistory()) dt=history->getNextDelta();
|
2010-02-08 07:59:03 -05:00
|
|
|
WorldStatus::update(dt);
|
2008-09-07 10:42:37 -04:00
|
|
|
// Clear race state so that new information can be stored
|
|
|
|
race_state->clear();
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2008-10-06 09:40:11 -04:00
|
|
|
if(network_manager->getMode()!=NetworkManager::NW_CLIENT &&
|
|
|
|
!history->dontDoPhysics())
|
|
|
|
{
|
|
|
|
m_physics->update(dt);
|
|
|
|
}
|
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
const int kart_amount = m_karts.size();
|
2008-09-23 20:43:45 -04:00
|
|
|
for (int i = 0 ; i < kart_amount; ++i)
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
2008-04-15 09:57:18 -04:00
|
|
|
// Update all karts that are not eliminated
|
2010-02-10 17:59:17 -05:00
|
|
|
if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ;
|
2007-12-12 09:07:26 -05:00
|
|
|
}
|
2009-10-22 21:54:05 -04:00
|
|
|
// The order of updates is rather important: if track update would
|
|
|
|
// be called before kart update, then the check manager (called from
|
|
|
|
// track update) will be using the old kart position to determine
|
2010-02-08 07:59:03 -05:00
|
|
|
// e.g. if a kart has crossed a new line. But linear world (from
|
2009-10-22 21:54:05 -04:00
|
|
|
// which this is called in case of a race) will be using the new
|
|
|
|
// position of the karts to determine the driveline quad a kart
|
|
|
|
// is on. So if a kart just arrived at quad 0 (meaning the distance
|
|
|
|
// along the track goes from close-to-lap-length to close-to-zero),
|
|
|
|
// the order of karts will be incorrect, since this kart will not
|
|
|
|
// have started the next lap. While this will only last for one
|
|
|
|
// frame (since in the next frame the check manager will detect
|
|
|
|
// the new lap), it causes an unwanted display of icons in the
|
|
|
|
// icon display.
|
|
|
|
m_track->update(dt);
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2007-12-12 09:07:26 -05:00
|
|
|
projectile_manager->update(dt);
|
2009-03-31 20:55:20 -04:00
|
|
|
} // update
|
|
|
|
|
2008-05-19 23:33:48 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
2008-09-28 22:29:33 -04:00
|
|
|
|
2010-03-25 18:29:47 -04:00
|
|
|
Highscores* World::getHighscores() const
|
2008-09-22 21:16:50 -04:00
|
|
|
{
|
2008-09-23 20:13:31 -04:00
|
|
|
if(!m_use_highscores) return NULL;
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2010-03-25 18:29:47 -04:00
|
|
|
const Highscores::HighscoreType type = "HST_" + getIdent();
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2010-03-25 18:29:47 -04:00
|
|
|
Highscores * highscores =
|
|
|
|
highscore_manager->getHighscores(type,
|
|
|
|
getNumKarts(),
|
|
|
|
race_manager->getDifficulty(),
|
|
|
|
race_manager->getTrackName(),
|
|
|
|
race_manager->getNumLaps());
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-09-22 21:16:50 -04:00
|
|
|
return highscores;
|
2010-02-10 06:40:33 -05:00
|
|
|
} // getHighscores
|
|
|
|
|
2008-09-22 21:16:50 -04:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/*
|
2010-02-09 04:24:45 -05:00
|
|
|
* Usually called at the end of a race. Checks if the current times are worth a new
|
2008-09-22 21:16:50 -04:00
|
|
|
* score, if so it notifies the HighscoreManager so the new score is added and saved.
|
|
|
|
*/
|
2008-05-19 23:33:48 -04:00
|
|
|
void World::updateHighscores()
|
|
|
|
{
|
2008-09-23 20:13:31 -04:00
|
|
|
if(!m_use_highscores) return;
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-05-19 23:33:48 -04:00
|
|
|
// Add times to highscore list. First compute the order of karts,
|
|
|
|
// so that the timing of the fastest kart is added first (otherwise
|
|
|
|
// someone might get into the highscore list, only to be kicked out
|
|
|
|
// again by a faster kart in the same race), which might be confusing
|
|
|
|
// if we ever decide to display a message (e.g. during a race)
|
2010-02-10 17:59:17 -05:00
|
|
|
unsigned int *index = new unsigned int[m_karts.size()];
|
2008-09-29 11:27:09 -04:00
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
const unsigned int kart_amount = m_karts.size();
|
2008-09-23 20:43:45 -04:00
|
|
|
for (unsigned int i=0; i<kart_amount; i++ )
|
2008-05-19 23:33:48 -04:00
|
|
|
{
|
2008-09-29 11:27:09 -04:00
|
|
|
index[i] = 999; // first reset the contents of the array
|
2008-10-22 12:05:08 -04:00
|
|
|
}
|
|
|
|
for (unsigned int i=0; i<kart_amount; i++ )
|
|
|
|
{
|
2010-02-10 17:59:17 -05:00
|
|
|
const int pos = m_karts[i]->getPosition()-1;
|
2008-10-24 00:39:52 -04:00
|
|
|
if(pos < 0 || pos >= (int)kart_amount) continue; // wrong position
|
2008-10-22 12:05:08 -04:00
|
|
|
index[pos] = i;
|
2008-05-19 23:33:48 -04:00
|
|
|
}
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2008-09-23 20:43:45 -04:00
|
|
|
for(unsigned int pos=0; pos<kart_amount; pos++)
|
2008-05-19 23:33:48 -04:00
|
|
|
{
|
2008-09-29 21:54:09 -04:00
|
|
|
|
|
|
|
if(index[pos] == 999)
|
2008-07-14 01:21:37 -04:00
|
|
|
{
|
2008-09-29 21:54:09 -04:00
|
|
|
// no kart claimed to be in this position, most likely means
|
|
|
|
// the kart location data is wrong
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-09-29 21:54:09 -04:00
|
|
|
#ifdef DEBUG
|
2008-07-14 01:21:37 -04:00
|
|
|
fprintf(stderr, "Error, incorrect kart positions:");
|
2010-02-10 17:59:17 -05:00
|
|
|
for (unsigned int i=0; i<m_karts.size(); i++ )
|
2008-07-14 01:21:37 -04:00
|
|
|
{
|
2010-02-10 17:59:17 -05:00
|
|
|
fprintf(stderr, "i=%d position %d\n",i, m_karts[i]->getPosition());
|
2008-07-14 01:21:37 -04:00
|
|
|
}
|
|
|
|
#endif
|
2008-09-29 21:54:09 -04:00
|
|
|
continue;
|
|
|
|
}
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-10-21 12:04:54 -04:00
|
|
|
// Only record times for player karts and only if they finished the race
|
2010-02-14 19:54:28 -05:00
|
|
|
if(!m_karts[index[pos]]->getController()->isPlayerController()) continue;
|
2010-02-10 17:59:17 -05:00
|
|
|
if (!m_karts[index[pos]]->hasFinishedRace()) continue;
|
2008-05-19 23:33:48 -04:00
|
|
|
|
2008-10-22 12:05:08 -04:00
|
|
|
assert(index[pos] >= 0);
|
2010-02-10 17:59:17 -05:00
|
|
|
assert(index[pos] < m_karts.size());
|
2010-02-14 19:54:28 -05:00
|
|
|
Kart *k = (Kart*)m_karts[index[pos]];
|
2008-05-19 23:33:48 -04:00
|
|
|
|
2010-03-25 18:29:47 -04:00
|
|
|
Highscores* highscores = getHighscores();
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2010-02-14 19:54:28 -05:00
|
|
|
PlayerController *controller = (PlayerController*)(k->getController());
|
2009-08-29 14:59:25 -04:00
|
|
|
if(highscores->addData(k->getIdent(),
|
2010-02-14 19:54:28 -05:00
|
|
|
controller->getPlayer()->getProfile()->getName(),
|
2008-09-22 21:16:50 -04:00
|
|
|
k->getFinishTime())>0 )
|
2008-05-19 23:33:48 -04:00
|
|
|
{
|
2010-03-25 18:29:47 -04:00
|
|
|
highscore_manager->saveHighscores();
|
2008-05-19 23:33:48 -04:00
|
|
|
}
|
2008-09-29 11:43:58 -04:00
|
|
|
} // next position
|
2008-05-19 23:33:48 -04:00
|
|
|
delete []index;
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-05-19 23:33:48 -04:00
|
|
|
} // updateHighscores
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-10 06:40:33 -05:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Returns the n-th player kart. Note that this function is O(N), not O(1),
|
|
|
|
* so it shouldn't be called inside of loops.
|
2010-02-10 07:47:18 -05:00
|
|
|
* \param n Index of player kart to return.
|
2010-02-10 06:40:33 -05:00
|
|
|
*/
|
2010-02-14 19:54:28 -05:00
|
|
|
Kart *World::getPlayerKart(unsigned int n) const
|
2010-02-10 06:40:33 -05:00
|
|
|
{
|
2010-02-10 07:47:18 -05:00
|
|
|
unsigned int count=-1;
|
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
for(unsigned int i=0; i<m_karts.size(); i++)
|
2010-02-14 19:54:28 -05:00
|
|
|
if(m_karts[i]->getController()->isPlayerController())
|
2010-02-10 07:47:18 -05:00
|
|
|
{
|
|
|
|
count++;
|
2010-02-14 19:54:28 -05:00
|
|
|
if(count==n) return m_karts[i];
|
2010-02-10 07:47:18 -05:00
|
|
|
}
|
2010-02-10 06:40:33 -05:00
|
|
|
return NULL;
|
|
|
|
} // getPlayerKart
|
|
|
|
|
2010-02-10 07:47:18 -05:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Returns the nth local player kart, i.e. a player kart that has a camera.
|
|
|
|
* \param n Index of player kart to return.
|
|
|
|
*/
|
2010-02-14 19:54:28 -05:00
|
|
|
Kart *World::getLocalPlayerKart(unsigned int n) const
|
2010-02-10 07:47:18 -05:00
|
|
|
{
|
|
|
|
unsigned int count=-1;
|
2010-02-10 17:59:17 -05:00
|
|
|
for(unsigned int i=0; i<m_karts.size(); i++)
|
2010-02-10 07:47:18 -05:00
|
|
|
{
|
2010-02-14 19:54:28 -05:00
|
|
|
if(m_karts[i]->getCamera() && m_karts[i]->getController()->isPlayerController())
|
2010-02-10 07:47:18 -05:00
|
|
|
{
|
|
|
|
count++;
|
2010-02-14 19:54:28 -05:00
|
|
|
if(count==n) return m_karts[i];
|
2010-02-10 07:47:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
} // getLocalPlayerKart
|
|
|
|
|
2008-04-15 09:57:18 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
2010-02-24 20:48:32 -05:00
|
|
|
/** Remove (eliminate) a kart from the race */
|
|
|
|
void World::removeKart(int kart_number, bool notifyOfElimination)
|
2008-04-15 09:57:18 -04:00
|
|
|
{
|
2010-02-10 17:59:17 -05:00
|
|
|
Kart *kart = m_karts[kart_number];
|
2010-02-24 20:48:32 -05:00
|
|
|
|
2009-08-18 06:59:21 -04:00
|
|
|
// Display a message about the eliminated kart in the race gui
|
2010-02-24 20:48:32 -05:00
|
|
|
if (notifyOfElimination)
|
2009-08-18 06:59:21 -04:00
|
|
|
{
|
2010-02-24 20:48:32 -05:00
|
|
|
for (KartList::iterator i = m_karts.begin(); i != m_karts.end(); i++ )
|
2009-07-12 07:54:21 -04:00
|
|
|
{
|
2010-02-24 20:48:32 -05:00
|
|
|
if(!(*i)->getCamera()) continue;
|
|
|
|
if(*i==kart)
|
|
|
|
{
|
2010-03-04 20:58:27 -05:00
|
|
|
m_race_gui->addMessage(_("You have been eliminated!"), *i, 2.0f, 60);
|
2010-02-24 20:48:32 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-03-04 20:58:27 -05:00
|
|
|
m_race_gui->addMessage(StringUtils::insertValues(_("'%s' has been eliminated."),
|
2010-02-24 20:48:32 -05:00
|
|
|
kart->getName().c_str()),
|
|
|
|
*i, 2.0f, 60);
|
|
|
|
}
|
|
|
|
} // for i in kart
|
|
|
|
}
|
|
|
|
|
2010-02-14 19:54:28 -05:00
|
|
|
if(kart->getController()->isPlayerController())
|
2008-04-15 09:57:18 -04:00
|
|
|
{
|
2009-08-18 06:59:21 -04:00
|
|
|
// Change the camera so that it will be attached to the leader
|
2008-04-15 09:57:18 -04:00
|
|
|
// and facing backwards.
|
2010-02-14 19:54:28 -05:00
|
|
|
Camera* camera=kart->getCamera();
|
2008-04-15 09:57:18 -04:00
|
|
|
camera->setMode(Camera::CM_LEADER_MODE);
|
|
|
|
m_eliminated_players++;
|
|
|
|
}
|
2010-02-17 06:59:51 -05:00
|
|
|
|
2009-08-18 06:59:21 -04:00
|
|
|
// The kart can't be really removed from the m_kart array, since otherwise
|
|
|
|
// a race can't be restarted. So it's only marked to be eliminated (and
|
|
|
|
// ignored in all loops). Important:world->getCurrentNumKarts() returns
|
|
|
|
// the number of karts still racing. This value can not be used for loops
|
2008-04-16 20:20:06 -04:00
|
|
|
// over all karts, use race_manager->getNumKarts() instead!
|
2008-04-15 09:57:18 -04:00
|
|
|
kart->eliminate();
|
|
|
|
m_eliminated_karts++;
|
|
|
|
|
|
|
|
} // removeKart
|
2009-07-16 08:09:15 -04:00
|
|
|
|
2008-09-21 20:55:27 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void World::getDefaultCollectibles(int& collectible_type, int& amount )
|
|
|
|
{
|
2008-10-29 22:02:56 -04:00
|
|
|
collectible_type = POWERUP_NOTHING;
|
2008-09-21 20:55:27 -04:00
|
|
|
amount = 0;
|
|
|
|
}
|
2007-05-27 12:01:53 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void World::restartRace()
|
|
|
|
{
|
2010-02-08 07:59:03 -05:00
|
|
|
WorldStatus::reset();
|
2008-05-07 21:28:07 -04:00
|
|
|
m_faster_music_active = false;
|
|
|
|
m_eliminated_karts = 0;
|
|
|
|
m_eliminated_players = 0;
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2010-02-10 17:59:17 -05:00
|
|
|
for ( KartList::iterator i = m_karts.begin(); i != m_karts.end() ; ++i )
|
2007-05-27 12:01:53 -04:00
|
|
|
{
|
|
|
|
(*i)->reset();
|
|
|
|
}
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
resetAllKarts();
|
2009-08-18 06:59:21 -04:00
|
|
|
|
2008-09-20 19:45:22 -04:00
|
|
|
// Start music from beginning
|
|
|
|
sound_manager->stopMusic();
|
2009-12-17 19:36:35 -05:00
|
|
|
m_track->reset();
|
2008-05-07 21:28:07 -04:00
|
|
|
m_track->startMusic();
|
2008-09-20 19:45:22 -04:00
|
|
|
|
2008-09-20 10:23:20 -04:00
|
|
|
// Enable SFX again
|
|
|
|
sfx_manager->resumeAll();
|
2008-09-20 19:45:22 -04:00
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
projectile_manager->cleanup();
|
|
|
|
race_manager->reset();
|
2008-02-11 19:01:27 -05:00
|
|
|
} // restartRace
|
2007-05-27 12:01:53 -04:00
|
|
|
|
2007-06-09 21:13:39 -04:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void World::pause()
|
|
|
|
{
|
2008-09-20 10:23:20 -04:00
|
|
|
sound_manager->pauseMusic();
|
|
|
|
sfx_manager->pauseAll();
|
2010-02-08 07:59:03 -05:00
|
|
|
WorldStatus::pause();
|
|
|
|
} // pause
|
2007-06-09 21:13:39 -04:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2010-02-14 19:54:28 -05:00
|
|
|
void World::unpause()
|
2007-06-09 21:13:39 -04:00
|
|
|
{
|
2008-09-20 10:23:20 -04:00
|
|
|
sound_manager->resumeMusic() ;
|
|
|
|
sfx_manager->resumeAll();
|
2010-02-08 07:59:03 -05:00
|
|
|
WorldStatus::unpause();
|
2010-02-10 17:59:17 -05:00
|
|
|
for(unsigned int i=0; i<m_karts.size(); i++)
|
2010-02-14 19:54:28 -05:00
|
|
|
if(m_karts[i]->getController()->isPlayerController())
|
|
|
|
((PlayerController*)(m_karts[i]->getController()))->resetInputState();
|
2010-02-08 07:59:03 -05:00
|
|
|
} // pause
|
2007-06-09 21:13:39 -04:00
|
|
|
|
2007-05-27 12:01:53 -04:00
|
|
|
/* EOF */
|