stk-code_catmod/src/modes/linear_world.cpp

837 lines
33 KiB
C++
Raw Normal View History

// 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/linear_world.hpp"
#include <iostream>
#include "audio/music_manager.hpp"
#include "audio/sfx_base.hpp"
#include "audio/sfx_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/kart_properties.hpp"
#include "network/network_manager.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/track_sector.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
//-----------------------------------------------------------------------------
/** Constructs the linear world. Note that here no functions can be called
* that use World::getWorld(), since it is not yet defined.
*/
LinearWorld::LinearWorld() : WorldWithRank()
{
m_kart_display_info = NULL;
m_last_lap_sfx = sfx_manager->createSoundSource("last_lap_fanfare");
m_last_lap_sfx_played = false;
m_last_lap_sfx_playing = false;
m_fastest_lap = 9999999.9f;
} // LinearWorld
// ----------------------------------------------------------------------------
/** Actually initialises the world, i.e. creates all data structures to
* for all karts etc. In init functions can be called that use
* World::getWorld().
*/
void LinearWorld::init()
{
WorldWithRank::init();
assert(!m_track->isArena());
m_last_lap_sfx_played = false;
m_last_lap_sfx_playing = false;
const unsigned int kart_amount = m_karts.size();
m_kart_display_info = new RaceGUIBase::KartIconDisplayInfo[kart_amount];
for(unsigned int n=0; n<kart_amount; n++)
{
KartInfo info;
info.getSector()->update(m_karts[n]->getXYZ());
m_kart_info.push_back(info);
} // next kart
} // init
//-----------------------------------------------------------------------------
LinearWorld::~LinearWorld()
{
sfx_manager->deleteSFX(m_last_lap_sfx);
// In case that a track is not found, m_kart_display info was never
// initialised.
if(m_kart_display_info)
delete[] m_kart_display_info;
} // ~LinearWorld
//-----------------------------------------------------------------------------
void LinearWorld::restartRace()
{
WorldWithRank::restartRace();
//if(m_last_lap_sfx->getStatus()== SFXManager::SFX_PLAYING)
// m_last_lap_sfx->stop();
m_last_lap_sfx_played = false;
m_last_lap_sfx_playing = false;
const unsigned int kart_amount = m_karts.size();
for(unsigned int i=0; i<kart_amount; i++)
{
m_kart_info[i].reset();
m_kart_info[i].getSector()->update(m_karts[i]->getXYZ());
} // next kart
// First all kart infos must be updated before the kart position can be
// recomputed, since otherwise 'new' (initialised) valued will be compared
// with old values.
updateRacePosition();
#ifdef DEBUG
//FIXME: this could be defined somewhere in a central header so it can
// be used everywhere
#define assertExpr( ARG1, OP, ARG2 ) if (!(ARG1 OP ARG2)) \
{ \
std::cerr << "Failed assert " << #ARG1 << #OP << #ARG2 << " @ " << __FILE__ << ":" << __LINE__ \
<< "; values are (" << ARG1 << #OP << ARG2 << ")\n"; \
assert(false); \
}
for (unsigned int i=0; i<kart_amount; i++)
{
for (unsigned int j=i+1; j<kart_amount; j++)
{
assertExpr( m_karts[i]->getPosition(), !=,
m_karts[j]->getPosition() );
}
}
#endif
} // restartRace
//-----------------------------------------------------------------------------
/** General update function called once per frame. This updates the kart
* sectors, which are then used to determine the kart positions.
* \param dt Time step size.
*/
void LinearWorld::update(float dt)
{
// run generic parent stuff that applies to all modes. It
// especially updates the kart positions.
WorldWithRank::update(dt);
if (m_last_lap_sfx_playing &&
m_last_lap_sfx->getStatus() != SFXManager::SFX_PLAYING)
{
if(music_manager->getCurrentMusic())
music_manager->getCurrentMusic()->resetTemporaryVolume();
m_last_lap_sfx_playing = false;
}
const unsigned int kart_amount = getNumKarts();
// Do stuff specific to this subtype of race.
// ------------------------------------------
for(unsigned int n=0; n<kart_amount; n++)
{
KartInfo& kart_info = m_kart_info[n];
AbstractKart* kart = m_karts[n];
// Nothing to do for karts that are currently being
// rescued or eliminated
if(kart->getKartAnimation()) continue;
kart_info.getSector()->update(kart->getXYZ());
kart_info.m_overall_distance = kart_info.m_race_lap
* m_track->getTrackLength()
+ getDistanceDownTrackForKart(kart->getWorldKartId());
} // for n
// Update all positions. This must be done after _all_ karts have
// updated their position and laps etc, otherwise inconsistencies
// (like two karts at same position) can occur.
// ---------------------------------------------------------------
WorldWithRank::updateTrack(dt);
updateRacePosition();
for (unsigned int i=0; i<kart_amount; i++)
{
// ---------- update rank ------
if (m_karts[i]->hasFinishedRace() ||
m_karts[i]->isEliminated() ) continue;
// During the last lap update the estimated finish time.
// This is used to play the faster music, and by the AI
if (m_kart_info[i].m_race_lap == race_manager->getNumLaps()-1)
{
m_kart_info[i].m_estimated_finish =
estimateFinishTimeForKart(m_karts[i]);
}
checkForWrongDirection(i);
}
#ifdef DEBUG
// Debug output in case that the double position error occurs again.
std::vector<int> pos_used;
pos_used.resize(kart_amount+1, -99);
for(unsigned int i=0; i<kart_amount; i++)
{
if(pos_used[m_karts[i]->getPosition()]!=-99)
{
for(unsigned int j =0; j<kart_amount; j++)
{
printf("kart id=%d, position=%d, finished=%d, laps=%d, "
"distanceDownTrack=%f overallDistance=%f %s\n",
j, m_karts[j]->getPosition(),
m_karts[j]->hasFinishedRace(),
m_kart_info[j].m_race_lap,
getDistanceDownTrackForKart(m_karts[j]->getWorldKartId()),
m_kart_info[j].m_overall_distance,
(m_karts[j]->getPosition() == m_karts[i]->getPosition()
? "<--- !!!" : "") );
}
}
pos_used[m_karts[i]->getPosition()]=i;
}
#endif
} // update
//-----------------------------------------------------------------------------
/** Is called by check structures if a kart starts a new lap.
* \param kart_index Index of the kart.
*/
void LinearWorld::newLap(unsigned int kart_index)
{
KartInfo &kart_info = m_kart_info[kart_index];
AbstractKart *kart = m_karts[kart_index];
// Only update the kart controller if a kart that has already finished
// the race crosses the start line again. This avoids 'fastest lap'
// messages if the end controller does a fastest lap, but especially
// allows the end controller to switch end cameras
if(kart->hasFinishedRace())
{
kart->getController()->newLap(kart_info.m_race_lap);
return;
}
const int lap_count = race_manager->getNumLaps();
// Only increase the lap counter and set the new time if the
// kart hasn't already finished the race (otherwise the race_gui
// will begin another countdown).
if(kart_info.m_race_lap+1 <= lap_count)
{
assert(kart->getWorldKartId()==kart_index);
kart_info.m_time_at_last_lap=getTime();
kart_info.m_race_lap++;
m_kart_info[kart_index].m_overall_distance =
m_kart_info[kart_index].m_race_lap * m_track->getTrackLength()
+ getDistanceDownTrackForKart(kart->getWorldKartId());
}
// Last lap message (kart_index's assert in previous block already)
if (raceHasLaps() && kart_info.m_race_lap+1 == lap_count)
{
m_race_gui->addMessage(_("Final lap!"), kart,
3.0f, video::SColor(255, 210, 100, 50), true);
if(!m_last_lap_sfx_played && lap_count > 1)
{
if (UserConfigParams::m_music)
{
m_last_lap_sfx->play();
m_last_lap_sfx_played = true;
m_last_lap_sfx_playing = true;
// In case that no music is defined
if(music_manager->getCurrentMusic() &&
music_manager->getMasterMusicVolume() > 0.2f)
{
music_manager->getCurrentMusic()->setTemporaryVolume(0.2f);
}
}
else
{
m_last_lap_sfx_played = true;
m_last_lap_sfx_playing = false;
}
}
}
else if (raceHasLaps() && kart_info.m_race_lap > 0 && kart_info.m_race_lap+1 < lap_count)
{
m_race_gui->addMessage(_("Lap %i", kart_info.m_race_lap+1),
kart, 3.0f, video::SColor(255, 210, 100, 50),
true);
}
// The race positions must be updated here: consider the situation where
// the first kart does not cross the finish line in its last lap, instead
// it passes it, the kart reverses and crosses the finishing line
// backwards. Just before crossing the finishing line the kart will be on
// the last lap, but with a distance along the track close to zero.
// Therefore its position will be wrong. If the race position gets updated
// after increasing the number of laps (but before tagging the kart to have
// finished the race) the position will be correct (since the kart now
// has one additional lap it will be ahead of the other karts).
// Without this call the incorrect position for this kart would remain
// (since a kart that has finished the race does not get its position
// changed anymore), potentially resulting in a duplicated race position
// (since the first kart does not have position 1, no other kart can get
// position 1, so one rank will be duplicated).
// Similarly the situation can happen if the distance along track should
// go back to zero before actually crossing the finishing line. While this
// should not happen, it could potentially be caused by floating point
// errors. In this case the call to updateRacePosition will avoid
// duplicated race positions as well.
updateRacePosition();
// Race finished
if(kart_info.m_race_lap >= race_manager->getNumLaps() && raceHasLaps())
{
// A client does not detect race finished by itself, it will
// receive a message from the server. So a client does not do
// anything here.
if(network_manager->getMode()!=NetworkManager::NW_CLIENT)
{
kart->finishedRace(getTime());
}
}
float time_per_lap;
if (kart_info.m_race_lap == 1) // just completed first lap
{
time_per_lap=getTime();
}
else //completing subsequent laps
{
time_per_lap=getTime() - kart_info.m_lap_start_time;
}
// if new fastest lap
if(time_per_lap < m_fastest_lap && raceHasLaps() &&
kart_info.m_race_lap>0)
{
m_fastest_lap = time_per_lap;
std::string s = StringUtils::timeToString(time_per_lap);
irr::core::stringw m_fastest_lap_message;
//I18N: as in "fastest lap: 60 seconds by Wilber"
m_fastest_lap_message += _C("fastest_lap", "%s by %s", s.c_str(),
core::stringw(kart->getName()));
m_race_gui->addMessage(m_fastest_lap_message, NULL,
3.0f, video::SColor(255, 255, 255, 255), false);
m_race_gui->addMessage(_("New fastest lap"), NULL,
3.0f, video::SColor(255, 255, 255, 255), false);
} // end if new fastest lap
kart_info.m_lap_start_time = getTime();
kart->getController()->newLap(kart_info.m_race_lap);
} // newLap
//-----------------------------------------------------------------------------
/** Gets the sector a kart is on. This return UNKNOWN_SECTOR if the kart_id
* is larger than the current kart info. This is necessary in the case that
* a collision with the track happens during resetAllKarts: at this time
* m_kart_info is not initialised (and has size 0), so it would trigger this
* assert. While this normally does not happen, it is useful for track
* designers that STK does not crash.
* \param kart_id The world kart id of the kart for which to return
* the sector.
*/
int LinearWorld::getSectorForKart(const AbstractKart *kart) const
{
if(kart->getWorldKartId()>=m_kart_info.size())
return QuadGraph::UNKNOWN_SECTOR;
return m_kart_info[kart->getWorldKartId()].getSector()
->getCurrentGraphNode();
} // getSectorForKart
//-----------------------------------------------------------------------------
/** Returns the distance the kart has travelled along the track since
* crossing the start line..
* \param kart_id Index of the kart.
*/
float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const
{
assert(kart_id < (int)m_kart_info.size());
return m_kart_info[kart_id].getSector()->getDistanceFromStart();
} // getDistanceDownTrackForKart
//-----------------------------------------------------------------------------
/** Gets the distance of the kart from the center of the driveline. Positive
* is to the right of the center, negative values to the left.
* \param kart_id Index of kart.
*/
float LinearWorld::getDistanceToCenterForKart(const int kart_id) const
{
assert(kart_id < (int)m_kart_info.size());
return m_kart_info[kart_id].getSector()->getDistanceToCenter();
} // getDistanceToCenterForKart
//-----------------------------------------------------------------------------
int LinearWorld::getLapForKart(const int kart_id) const
{
assert(kart_id < (int)m_kart_info.size());
return m_kart_info[kart_id].m_race_lap;
} // getLapForKart
//-----------------------------------------------------------------------------
/** Returns the estimated finishing time. Only valid during the last lap!
* \param kart_id Id of the kart.
*/
float LinearWorld::getEstimatedFinishTime(const int kart_id) const
{
assert(kart_id < (int)m_kart_info.size());
assert(m_kart_info[kart_id].m_race_lap == race_manager->getNumLaps()-1);
return m_kart_info[kart_id].m_estimated_finish;
} // getEstimatedFinishTime
//-----------------------------------------------------------------------------
float LinearWorld::getTimeAtLapForKart(const int kart_id) const
{
assert(kart_id < (int)m_kart_info.size());
return m_kart_info[kart_id].m_time_at_last_lap;
} // getTimeAtLapForKart
//-----------------------------------------------------------------------------
RaceGUIBase::KartIconDisplayInfo* LinearWorld::getKartsDisplayInfo()
{
int laps_of_leader = -1;
float time_of_leader = -1;
// Find the best time for the lap. We can't simply use
// the time of the kart at position 1, since the kart
// might have been overtaken by now
const unsigned int kart_amount = getNumKarts();
for(unsigned int i = 0; i < kart_amount ; i++)
{
RaceGUIBase::KartIconDisplayInfo& rank_info = m_kart_display_info[i];
AbstractKart* kart = m_karts[i];
// reset color
rank_info.r = 1.0;
rank_info.g = 1.0;
rank_info.b = 1.0;
rank_info.lap = -1;
if(kart->isEliminated()) continue;
const float lap_time = getTimeAtLapForKart(kart->getWorldKartId());
const int current_lap = getLapForKart( kart->getWorldKartId() );
rank_info.lap = current_lap;
if(current_lap > laps_of_leader)
{
// more laps than current leader --> new leader and
// new time computation
laps_of_leader = current_lap;
time_of_leader = lap_time;
} else if(current_lap == laps_of_leader)
{
// Same number of laps as leader: use fastest time
time_of_leader=std::min(time_of_leader,lap_time);
}
}
// we now know the best time of the lap. fill the remaining bits of info
for(unsigned int i = 0; i < kart_amount ; i++)
{
RaceGUIBase::KartIconDisplayInfo& rank_info = m_kart_display_info[i];
KartInfo& kart_info = m_kart_info[i];
AbstractKart* kart = m_karts[i];
const int position = kart->getPosition();
// Don't compare times when crossing the start line first
if(laps_of_leader>0 &&
(getTime() - getTimeAtLapForKart(kart->getWorldKartId())<5.0f ||
rank_info.lap != laps_of_leader) &&
raceHasLaps())
{ // Display for 5 seconds
std::string str;
if(position == 1)
{
str = " " + StringUtils::timeToString(
getTimeAtLapForKart(kart->getWorldKartId()) );
}
else
{
float timeBehind;
timeBehind = (kart_info.m_race_lap==laps_of_leader
? getTimeAtLapForKart(kart->getWorldKartId())
: getTime())
- time_of_leader;
str = "+" + StringUtils::timeToString(timeBehind);
}
rank_info.m_text = irr::core::stringw(str.c_str());
}
else
{
rank_info.m_text = "";
}
int numLaps = race_manager->getNumLaps();
if(kart_info.m_race_lap>=numLaps)
{ // kart is finished, display in green
rank_info.g = rank_info.b = 0;
}
else if(kart_info.m_race_lap>=0 && numLaps>1)
{
rank_info.g = rank_info.b =
1.0f-(float)kart_info.m_race_lap/((float)numLaps-1.0f);
}
} // next kart
return m_kart_display_info;
} // getKartsDisplayInfo
//-----------------------------------------------------------------------------
/** Estimate the arrival time of any karts that haven't arrived yet by using
* their average speed up to now and the distance still to race. This
* approach guarantees that the order of the karts won't change anymore
* (karts ahead will have a higher average speed and therefore finish the
* race earlier than karts further behind), so the position doesn't have to
* be updated to get the correct scoring.
* \param kart The kart for which to estimate the finishing times.
*/
float LinearWorld::estimateFinishTimeForKart(AbstractKart* kart)
{
const KartInfo &kart_info = m_kart_info[kart->getWorldKartId()];
float full_distance = race_manager->getNumLaps()
* m_track->getTrackLength();
if(full_distance == 0)
full_distance = 1; // For 0 lap races avoid warning below
#ifdef DEBUG
if(full_distance < kart_info.m_overall_distance)
{
printf("WARNING: full distance < distance covered for kart '%s':\n",
kart->getIdent().c_str());
printf("%f < %f\n", full_distance, kart_info.m_overall_distance);
}
#endif
// Avoid potential problems (floating point issues, coding bug?) if a
// kart has driven more than the full distance, but not finished:
// Return the current time plus initial position to spread arrival
// times a bit. This code should generally not be used at all, it's
// just here to avoid invalid finishing times.
if(full_distance < kart_info.m_overall_distance)
return getTime() + kart->getInitialPosition();
// Finish time is the time needed for the whole race with
// the computed average speed computed.
float average_speed = getTime()==0
? 1.0f
: kart_info.m_overall_distance/getTime();
// Overall distance is negative before the starting line is crossed
// for the first time. Unlikely to happen in real races, but will
// happen in 0-laps races.
if(average_speed<0)
average_speed = 1.0f;
// Avoid NAN as results when average_speed is low
// Instead just set time to 99:59:00
if(average_speed<0.1f)
return 99*60+59;
return getTime() + (full_distance - kart_info.m_overall_distance)
/ average_speed;
} // estimateFinishTimeForKart
//-----------------------------------------------------------------------------
/** Decide where to drop a rescued kart
*/
void LinearWorld::moveKartAfterRescue(AbstractKart* kart)
{
KartInfo& info = m_kart_info[kart->getWorldKartId()];
info.getSector()->rescue();
int sector = info.getSector()->getCurrentGraphNode();
kart->setXYZ( QuadGraph::get()
->getQuadOfNode(sector).getCenter());
btQuaternion heading(btVector3(0.0f, 1.0f, 0.0f),
m_track->getAngle(sector) );
kart->setRotation(heading);
// A certain epsilon is added here to the Z coordinate, in case
// that the drivelines are somewhat under the track. Otherwise, the
// kart might be placed a little bit under the track, triggering
// a rescue, ... (experimentally found value)
float epsilon = 0.5f * kart->getKartHeight();
btTransform pos;
pos.setOrigin(kart->getXYZ()+btVector3(0, kart->getKartHeight() + epsilon,
0));
pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f),
m_track->getAngle(sector)));
kart->getBody()->setCenterOfMassTransform(pos);
//project kart to surface of track
bool kart_over_ground = m_physics->projectKartDownwards(kart);
if (kart_over_ground)
{
//add vertical offset so that the kart starts off above the track
float vertical_offset =
kart->getKartProperties()->getVertRescueOffset()
* kart->getKartHeight();
kart->getBody()->translate(btVector3(0, vertical_offset, 0));
}
else
{
fprintf(stderr, "WARNING: invalid position after rescue for kart %s "
"on track %s.\n",
(kart->getIdent().c_str()), m_track->getIdent().c_str());
}
} // moveKartAfterRescue
//-----------------------------------------------------------------------------
/** Find the position (rank) of every kart. ATM it uses a stable O(n^2)
* algorithm by counting for each kart how many other karts are ahead of
* it.
*/
void LinearWorld::updateRacePosition()
{
// Mostly for debugging:
beginSetKartPositions();
const unsigned int kart_amount = m_karts.size();
#ifdef DEBUG
bool rank_changed = false;
#endif
// NOTE: if you do any changes to this loop, the next loop (see
// DEBUG_KART_RANK below) needs to have the same changes applied
// so that debug output is still correct!!!!!!!!!!!
for (unsigned int i=0; i<kart_amount; i++)
{
AbstractKart* kart = m_karts[i];
// Karts that are either eliminated or have finished the
// race already have their (final) position assigned. If
// these karts would get their rank updated, it could happen
// that a kart that finished first will be overtaken after
// crossing the finishing line and become second!
if(kart->isEliminated() || kart->hasFinishedRace())
{
// This is only necessary to support debugging inconsistencies
// in kart position parameters.
setKartPosition(i, kart->getPosition());
continue;
}
KartInfo& kart_info = m_kart_info[i];
int p = 1 ;
const unsigned int my_id = kart->getWorldKartId();
const float my_distance = m_kart_info[my_id].m_overall_distance;
// Count karts ahead of the current kart, i.e. kart that are
// already finished or have covered a larger overall distance.
for (unsigned int j = 0 ; j < kart_amount ; j++)
{
// don't compare a kart with itself and ignore eliminated karts
if(j == my_id || m_karts[j]->isEliminated())
continue;
// If the other kart has:
// - finished the race (but this kart hasn't)
// - or is ahead
// - or has the same distance (very unlikely) but started earlier
// it is ahead --> increase position
if((!kart->hasFinishedRace() && m_karts[j]->hasFinishedRace()) ||
m_kart_info[j].m_overall_distance > my_distance ||
(m_kart_info[j].m_overall_distance == my_distance &&
m_karts[j]->getInitialPosition()<kart->getInitialPosition() ) )
{
p++;
}
} //next kart
#ifndef DEBUG
setKartPosition(i, p);
#else
rank_changed |= kart->getPosition()!=p;
if (!setKartPosition(i,p))
{
std::cerr << "ERROR, same rank used twice!!\n";
std::cerr << "Info used to decide ranking :\n";
for (unsigned int d=0; d<kart_amount; d++)
{
std::cerr << " kart " << m_karts[d]->getIdent()
<< " has finished(" << m_karts[d]->hasFinishedRace()
<< "), is at lap (" << getLapForKart(d)
<< "), is at distance("
<< m_kart_info[d].m_overall_distance
<< "), is eliminated(" << m_karts[d]->isEliminated()
<< ")" << std::endl;
}
std::cerr << "Who has each ranking so far :\n";
for (unsigned int d=0; d<i; d++)
{
std::cerr << " " << m_karts[d]->getIdent() << " has rank "
<< m_karts[d]->getPosition() << std::endl;
}
std::cerr << " --> And " << kart->getIdent()
<< " is being set at rank " << p << std::endl;
history->Save();
assert(false);
}
#endif
// Switch on faster music if not already done so, if the
// first kart is doing its last lap, and if the estimated
// remaining time is less than 30 seconds.
if(!m_faster_music_active &&
kart_info.m_race_lap == race_manager->getNumLaps()-1 &&
p==1 &&
useFastMusicNearEnd() &&
kart_info.m_estimated_finish > 0 &&
kart_info.m_estimated_finish - getTime() < 30.0f )
{
music_manager->switchToFastMusic();
m_faster_music_active=true;
}
} // for i<kart_amount
// Define this to get a detailled analyses each time a race position
// changes.
#ifdef DEBUG
#undef DEBUG_KART_RANK
#ifdef DEBUG_KART_RANK
if(rank_changed)
{
std::cout << "Counting laps at "<<getTime()<<" seconds.\n";
for (unsigned int i=0; i<kart_amount; i++)
{
AbstractKart* kart = m_karts[i];
std::cout << "counting karts ahead of " << kart->getIdent()
<< " (laps " << m_kart_info[i].m_race_lap
<< ", progress " << m_kart_info[i].m_overall_distance
<< " finished " << kart->hasFinishedRace()
<< " eliminated " << kart->isEliminated()
<< " initial position "<< kart->getInitialPosition()
<< ").\n";
// Karts that are either eliminated or have finished the
// race already have their (final) position assigned. If
// these karts would get their rank updated, it could happen
// that a kart that finished first will be overtaken after
// crossing the finishing line and become second!
if(kart->isEliminated() || kart->hasFinishedRace()) continue;
KartInfo& kart_info = m_kart_info[i];
int p = 1 ;
const int my_id = kart->getWorldKartId();
const float my_distance = m_kart_info[my_id].m_overall_distance;
for (unsigned int j = 0 ; j < kart_amount ; j++)
{
if(j == my_id) continue;
if(m_karts[j]->isEliminated())
{
std::cout << " " << p << " : " << m_karts[j]->getIdent()
<< " because it is eliminated.\n";
continue;
}
if(!kart->hasFinishedRace() && m_karts[j]->hasFinishedRace())
{
p++;
std::cout << " " << p << " : " << m_karts[j]->getIdent()
<< " because it has finished the race.\n";
continue;
}
if(m_kart_info[j].m_overall_distance > my_distance)
{
p++;
std::cout << " " << p << " : " << m_karts[j]->getIdent()
<< " because it is ahead "
<< m_kart_info[j].m_overall_distance <<".\n";
continue;
}
if(m_kart_info[j].m_overall_distance == my_distance &&
m_karts[j]->getInitialPosition()<kart->getInitialPosition() )
{
p++;
std::cout << " " << p << " : " << m_karts[j]->getIdent()
<< " has same distance, but started ahead "
<< m_karts[j]->getInitialPosition()<<".\n";
}
} // next kart j
} // for i<kart_amount
std::cout << "-------------------------------------------\n";
} // if rank_changed
#endif
#endif
endSetKartPositions();
} // updateRacePosition
//-----------------------------------------------------------------------------
/** Checks if a kart is going in the wrong direction. This is done only for
* player karts to display a message to the player.
* \param i Kart id.
*/
void LinearWorld::checkForWrongDirection(unsigned int i)
{
if(!m_karts[i]->getController()->isPlayerController()) return;
if(!m_kart_info[i].getSector()->isOnRoad()||
m_karts[i]->getKartAnimation()) return;
const AbstractKart *kart=m_karts[i];
// If the kart can go in more than one directions from the current track
// don't do any reverse message handling, since it is likely that there
// will be one direction in which it isn't going backwards anyway.
int sector = m_kart_info[i].getSector()->getCurrentGraphNode();
if(QuadGraph::get()->getNumberOfSuccessors(sector)>1)
return;
// check if the player is going in the wrong direction
float angle_diff = kart->getHeading() -
m_track->getAngle(sector);
if(angle_diff > M_PI) angle_diff -= 2*M_PI;
else if (angle_diff < -M_PI) angle_diff += 2*M_PI;
// Display a warning message if the kart is going back way (unless
// the kart has already finished the race).
if (( angle_diff > DEGREE_TO_RAD* 120.0f ||
angle_diff < -DEGREE_TO_RAD*120.0f) &&
kart->getVelocityLC().getY() > 0.0f &&
!kart->hasFinishedRace() )
{
m_race_gui->addMessage(_("WRONG WAY!"), kart,
/* time */ -1.0f,
video::SColor(255,255,255,255),
/*important*/ true,
/*big font*/ true);
} // if angle is too big
} // checkForWrongDirection
//-----------------------------------------------------------------------------