Mammoth patch to try and encapsulate sector and lap code better in modes. Still work in progress so expect suboptimal implementations and breakage. Known to be broken at this stage : opponents' times at the end of a lap, rescue button and rescuing in general, shortcut detection

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2303 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria
2008-09-27 19:43:57 +00:00
parent 3477a7f7e2
commit 019eaca2cf
21 changed files with 736 additions and 448 deletions

View File

@@ -146,6 +146,7 @@ supertuxkart_SOURCES = main.cpp \
modes/standard_race.cpp modes/standard_race.hpp \
modes/clock.cpp modes/clock.hpp \
modes/world.cpp modes/world.hpp \
modes/linear_world.cpp modes/linear_world.hpp \
replay_buffer_tpl.hpp \
replay_buffers.hpp replay_buffers.cpp \
replay_base.hpp replay_base.cpp \

View File

@@ -26,6 +26,7 @@
#include "grand_prix_manager.hpp"
#include "kart.hpp"
#include "track.hpp"
#include "modes/linear_world.hpp"
#if defined(WIN32) && !defined(__CYGWIN__)
# define snprintf _snprintf
@@ -234,7 +235,13 @@ bool ChallengeData::raceFinished()
}
// Quickrace / Timetrial
// ---------------------
if(kart->getLap()!=m_num_laps) return false; // wrong number of laps
// FIXME - encapsulate this better, each race mode needs to be able to specify
// its own challenges and deal with them
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
if(lworld != NULL)
{
if(lworld->getLapForKart( kart->getWorldKartId() ) != m_num_laps) return false; // wrong number of laps
}
if(m_time>0.0f && kart->getFinishTime()>m_time) return false; // too slow
return true;
} // raceFinished

View File

@@ -28,6 +28,7 @@
#include "utils/ssg_help.hpp"
#include "race_manager.hpp"
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
// static variables:
float Flyable::m_st_speed[COLLECT_MAX];
@@ -145,9 +146,15 @@ void Flyable::getClosestKart(const Kart **minKart, float *minDistSquared,
if(inFrontOf != NULL)
{
// Ignore karts behind the current one
float distance = kart->getDistanceDownTrack() - inFrontOf->getDistanceDownTrack();
if(distance<0) distance += RaceManager::getTrack()->getTrackLength();
if(distance > 50){ continue; }
// FIXME - needs an implementation that doesn't rely on drivelines
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
if(lworld != NULL)
{
float distance = lworld->getDistanceDownTrackForKart( kart->getWorldKartId() ) -
lworld->getDistanceDownTrackForKart( inFrontOf->getWorldKartId() );
if(distance<0) distance += RaceManager::getTrack()->getTrackLength();
if(distance > 50){ continue; }
}
}
if(distance2 < *minDistSquared || *minDistSquared < 0 /* not yet set */)

View File

@@ -126,7 +126,7 @@ BaseGUI::update(float dt)
//-----------------------------------------------------------------------------
void
BaseGUI::TimeToString(const double TIME, char *s)
BaseGUI::TimeToString(const double TIME, char *s) const
{
int min = (int) floor ( TIME / 60.0 ) ;
int sec = (int) floor ( TIME - (double) ( 60 * min ) ) ;

View File

@@ -49,7 +49,7 @@ public:
void lockInput() { m_locked = true; }
void unlockInput() { m_locked = false; }
void TimeToString(const double time, char *s);
void TimeToString(const double time, char *s) const;
protected:
bool m_locked;

View File

@@ -318,7 +318,7 @@ void RaceGUI::drawMap ()
//-----------------------------------------------------------------------------
// Draw players position on the race
void RaceGUI::drawPlayerIcons ()
void RaceGUI::drawPlayerIcons (const KartIconDisplayInfo* info)
{
assert(RaceManager::getWorld() != NULL);
@@ -329,83 +329,26 @@ void RaceGUI::drawPlayerIcons ()
//glEnable(GL_TEXTURE_2D);
Material *last_players_gst = 0;
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
for(unsigned int i = 0; i < race_manager->getNumKarts() ; i++)
{
Kart* kart = RaceManager::getKart(i);
if(kart->isEliminated()) continue;
float lap_time = kart->getTimeAtLap();
int laps = kart->getLap();
if(laps > laps_of_leader)
{
// more laps than current leader --> new leader and new time computation
laps_of_leader = laps;
time_of_leader = lap_time;
} else if(laps == laps_of_leader)
{
// Same number of laps as leader: use fastest time
time_of_leader=std::min(time_of_leader,lap_time);
}
} // for i<getNumKarts
int bFirst = 1;
for(unsigned int i = 0; i < race_manager->getNumKarts() ; i++)
const int kart_amount = race_manager->getNumKarts();
for(unsigned int i = 0; i < kart_amount ; i++)
{
Kart* kart = RaceManager::getKart(i);
if(kart->isEliminated()) continue;
int position = kart->getPosition();
int lap = kart->getLap();
y = user_config->m_height*3/4-20 - ((position-1)*(ICON_PLAYER_WIDHT+2));
// draw text
GLfloat COLORS[] = {1.0f, 1.0f, 1.0f, 1.0f};
int numLaps = race_manager->getNumLaps();
if(lap>=numLaps)
{ // kart is finished, display in green
COLORS[1] = COLORS[2] = 0;
}
else if(lap>=0 && numLaps>1)
{
COLORS[1] = COLORS[2] = 1.0f-(float)lap/((float)numLaps-1.0f);
}
glDisable(GL_CULL_FACE);
if(laps_of_leader>0 && // Display position during first lap
(RaceManager::getWorld()->getTime() - kart->getTimeAtLap()<5.0f || lap!=laps_of_leader) &&
race_manager->raceHasLaps())
{ // Display for 5 seconds
char str[256];
if(position==1)
{
str[0]=' '; str[1]=0;
TimeToString(kart->getTimeAtLap(), str+1);
}
else
{
float timeBehind;
timeBehind = (lap==laps_of_leader ? kart->getTimeAtLap() : RaceManager::getWorld()->getTime())
- time_of_leader;
str[0]='+'; str[1]=0;
TimeToString(timeBehind, str+1);
}
font_race->PrintShadow(str, 30, ICON_PLAYER_WIDHT+x, y+5,
COLORS);
}
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER && i==0)
GLfloat COLOR[] = {info[i].r, info[i].g, info[i].b, 1.0f};
font_race->PrintShadow(info[i].time.c_str(), 30, ICON_PLAYER_WIDHT+x, y+5, COLOR);
if(info[i].special_title.length() >0)
{
GLfloat const RED[] = { 1.0f, 0, 0, 1.0f};
font_race->PrintShadow(_("Leader"), 30, ICON_PLAYER_WIDHT+x, y+5,
RED );
font_race->PrintShadow(info[i].special_title.c_str(), 30, ICON_PLAYER_WIDHT+x, y+5, RED );
}
glEnable(GL_CULL_FACE);
bFirst = 0;
@@ -780,12 +723,15 @@ void RaceGUI::drawSpeed(Kart* kart, int offset_x, int offset_y,
} // drawSpeed
//-----------------------------------------------------------------------------
void RaceGUI::drawLap(Kart* kart, int offset_x, int offset_y,
float ratio_x, float ratio_y )
void RaceGUI::drawLap(const KartIconDisplayInfo* info, Kart* kart, int offset_x,
int offset_y, float ratio_x, float ratio_y )
{
// Don't display laps in follow the leader mode
if(!race_manager->raceHasLaps()) return;
if(kart->getLap()<0) return; // don't display 'lap 0/...'
const int lap = info[kart->getWorldKartId()].lap;
if(lap<0) return; // don't display 'lap 0/...', or do nothing if laps are disabled (-1)
float maxRatio = std::max(ratio_x, ratio_y);
char str[256];
offset_x += (int)(120*ratio_x);
@@ -802,7 +748,7 @@ void RaceGUI::drawLap(Kart* kart, int offset_x, int offset_y,
offset_y -= (int)(50*ratio_y);
sprintf(str, "%d/%d", kart->getLap()<0?0:kart->getLap()+1,
sprintf(str, "%d/%d", lap < 0 ? 0 : lap+1,
race_manager->getNumLaps());
font_race->PrintShadow(str, (int)(48*maxRatio), offset_x, offset_y);
}
@@ -989,6 +935,8 @@ void RaceGUI::drawStatusText(const float dt)
if ( RaceManager::getWorld()->getClock().isRacePhase() )
{
KartIconDisplayInfo* info = RaceManager::getWorld()->getKartsDisplayInfo(this);
const int numPlayers = race_manager->getNumLocalPlayers();
for(int pla = 0; pla < numPlayers; pla++)
@@ -1034,7 +982,7 @@ void RaceGUI::drawStatusText(const float dt)
split_screen_ratio_x, split_screen_ratio_y );
drawSpeed (player_kart, offset_x, offset_y,
split_screen_ratio_x, split_screen_ratio_y );
drawLap (player_kart, offset_x, offset_y,
drawLap (info, player_kart, offset_x, offset_y,
split_screen_ratio_x, split_screen_ratio_y );
drawAllMessages (player_kart, offset_x, offset_y,
split_screen_ratio_x, split_screen_ratio_y );
@@ -1051,7 +999,7 @@ void RaceGUI::drawStatusText(const float dt)
drawMap ();
if ( user_config->m_display_fps ) drawFPS ();
drawPlayerIcons ();
drawPlayerIcons (info);
} // if RACE_PHASE
glPopAttrib () ;

View File

@@ -35,6 +35,21 @@
class InputMap;
class RaceSetup;
/**
* Used to display the list of karts and their times or
* whatever other info is relevant to the current mode.
*/
struct KartIconDisplayInfo
{
std::string time;
// int rank;
float r, g, b;
std::string special_title;
/** Current lap of this kart, or -1 if irrelevant
*/
int lap;
};
class RaceGUI: public BaseGUI
{
@@ -104,7 +119,7 @@ private:
void drawAllMessages (Kart* player_kart,
int offset_x, int offset_y,
float ratio_x, float ratio_y );
void drawPlayerIcons ();
void drawPlayerIcons (const KartIconDisplayInfo* info);
void oldDrawPlayerIcons ();
void drawMap ();
void drawTimer ();
@@ -122,8 +137,8 @@ private:
float ratio_x, float ratio_y );
void drawSpeed (Kart* kart, int offset_x, int offset_y,
float ratio_x, float ratio_y );
void drawLap (Kart* kart, int offset_x, int offset_y,
float ratio_x, float ratio_y );
void drawLap (const KartIconDisplayInfo* info, Kart* kart, int offset_x,
int offset_y, float ratio_x, float ratio_y );
};
#endif

View File

@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
9519653D0E8C592F001BB888 /* linear_world.hpp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9519653B0E8C592F001BB888 /* linear_world.hpp */; };
9519653E0E8C592F001BB888 /* linear_world.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9519653C0E8C592F001BB888 /* linear_world.cpp */; };
95F0F25A0E85C054005F6693 /* callbacks.c in Sources */ = {isa = PBXBuildFile; fileRef = 95923F8B0E808EDC00388BDC /* callbacks.c */; };
95F0F25B0E85C054005F6693 /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = 95923FAF0E808EDC00388BDC /* list.c */; };
95F0F25C0E85C054005F6693 /* host.c in Sources */ = {isa = PBXBuildFile; fileRef = 95923F9B0E808EDC00388BDC /* host.c */; };
@@ -248,12 +250,15 @@
files = (
95F0F36E0E85C6A6005F6693 /* clock.hpp in CopyFiles */,
95F0F3830E85C76B005F6693 /* world.hpp in CopyFiles */,
9519653D0E8C592F001BB888 /* linear_world.hpp in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
9519653B0E8C592F001BB888 /* linear_world.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = linear_world.hpp; path = modes/linear_world.hpp; sourceTree = "<group>"; };
9519653C0E8C592F001BB888 /* linear_world.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = linear_world.cpp; path = modes/linear_world.cpp; sourceTree = "<group>"; };
956F3FAB0E85BE0E006F93B0 /* random_generator.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = random_generator.cpp; sourceTree = "<group>"; };
956F3FAC0E85BE0E006F93B0 /* random_generator.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = random_generator.hpp; sourceTree = "<group>"; };
956F3FAD0E85BE0E006F93B0 /* ssg_help.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = ssg_help.cpp; sourceTree = "<group>"; };
@@ -2055,6 +2060,8 @@
957B11D70E8320CD002A69EA /* follow_the_leader.hpp */,
957B11D80E8320CD002A69EA /* follow_the_leader.cpp */,
957B11B30E831DA8002A69EA /* standard_race.hpp */,
9519653B0E8C592F001BB888 /* linear_world.hpp */,
9519653C0E8C592F001BB888 /* linear_world.cpp */,
957B11B40E831DA8002A69EA /* standard_race.cpp */,
);
name = modes;
@@ -2323,6 +2330,7 @@
95F0F35D0E85C140005F6693 /* random_generator.cpp in Sources */,
95F0F36F0E85C6A6005F6693 /* clock.cpp in Sources */,
95F0F3840E85C76B005F6693 /* world.cpp in Sources */,
9519653E0E8C592F001BB888 /* linear_world.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2461,6 +2469,7 @@
"-lplibssgaux",
);
PRODUCT_NAME = SuperTuxKart;
SDKROOT = "";
ZERO_LINK = NO;
};
name = Release;

View File

@@ -66,7 +66,7 @@ Kart::Kart (const std::string& kart_name, int position,
#endif
{
m_kart_properties = kart_properties_manager->getKart(kart_name);
m_grid_position = position;
//m_grid_position = position;
m_initial_position = position;
m_num_herrings_gobbled = 0;
m_eliminated = false;
@@ -78,7 +78,7 @@ Kart::Kart (const std::string& kart_name, int position,
m_exhaust_pipe = NULL;
m_skidmark_left = NULL;
m_skidmark_right = NULL;
m_track_sector = Track::UNKNOWN_SECTOR;
// Set position and heading:
m_reset_transform = init_transform;
@@ -102,7 +102,6 @@ Kart::Kart (const std::string& kart_name, int position,
m_wheel_front_r = NULL;
m_wheel_rear_l = NULL;
m_wheel_rear_r = NULL;
m_lap_start_time = -1.0f;
m_engine_sound = sfx_manager->newSFX(SFXManager::SOUND_ENGINE);
@@ -346,10 +345,6 @@ void Kart::reset()
m_attachment.clear();
m_collectable.reset();
m_race_lap = -1;
m_lap_start_time = -1.0f;
m_time_at_last_lap = 99999.9f;
m_shortcut_sector = Track::UNKNOWN_SECTOR;
m_race_position = 9;
m_finished_race = false;
m_eliminated = false;
@@ -372,24 +367,6 @@ void Kart::reset()
setTrans(m_reset_transform);
RaceManager::getTrack()->findRoadSector(getXYZ(), &m_track_sector);
//If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of
//the road, so we have to use another function to find the sector.
if (m_track_sector == Track::UNKNOWN_SECTOR )
{
m_on_road = false;
m_track_sector = RaceManager::getTrack()->findOutOfRoadSector(
getXYZ(), Track::RS_DONT_KNOW, Track::UNKNOWN_SECTOR );
}
else
{
m_on_road = true;
}
RaceManager::getTrack()->spatialToTrack(m_curr_track_coords, getXYZ(),
m_track_sector );
m_vehicle->applyEngineForce (0.0f, 2);
m_vehicle->applyEngineForce (0.0f, 3);
@@ -408,107 +385,6 @@ void Kart::reset()
m_rescue = false;
TerrainInfo::update(getXYZ());
} // reset
//-----------------------------------------------------------------------------
int Kart::getSector() const
{
// this method only makes sense for linear races
assert(RaceManager::getWorld()->isLinearRace());
return m_track_sector;
}
//-----------------------------------------------------------------------------
float Kart::getDistanceDownTrack() const
{
// this method only makes sense for linear races
assert(RaceManager::getWorld()->isLinearRace());
return m_curr_track_coords.getY();
}
//-----------------------------------------------------------------------------
float Kart::getDistanceToCenter () const
{
// this method only makes sense for linear races
assert(RaceManager::getWorld()->isLinearRace());
return m_curr_track_coords.getX();
}
//-----------------------------------------------------------------------------
void Kart::doLapCounting ()
{
// this method only makes sense for linear races
assert(RaceManager::getWorld()->isLinearRace());
bool newLap = m_last_track_coords[1] > 300.0f && m_curr_track_coords.getY() < 20.0f;
if ( newLap )
{
// 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(m_race_lap+1<=race_manager->getNumLaps())
{
setTimeAtLap(RaceManager::getWorld()->getTime());
m_race_lap++ ;
}
// Race finished
// =============
if(m_race_lap>=race_manager->getNumLaps() &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
raceFinished(RaceManager::getWorld()->getTime());
}
// Only do timings if original time was set properly. Driving backwards
// over the start line will cause the lap start time to be set to -1.
if(m_lap_start_time>=0.0)
{
float time_per_lap;
if (m_race_lap == 1) // just completed first lap
{
time_per_lap=RaceManager::getWorld()->getTime();
}
else //completing subsequent laps
{
time_per_lap=RaceManager::getWorld()->getTime()-m_lap_start_time;
}
if(time_per_lap < RaceManager::getWorld()->getFastestLapTime() &&
race_manager->raceHasLaps())
{
RaceManager::getWorld()->setFastestLap(this, time_per_lap);
RaceGUI* m=(RaceGUI*)menu_manager->getRaceMenu();
if(m)
{
m->addMessage(_("New fastest lap"), NULL,
2.0f, 40, 100, 210, 100);
char s[20];
m->TimeToString(time_per_lap, s);
snprintf(m_fastest_lap_message, sizeof(m_fastest_lap_message),
"%s: %s",s, getName().c_str());
m->addMessage(m_fastest_lap_message, NULL,
2.0f, 40, 100, 210, 100);
} // if m
} // if time_per_lap < RaceManager::getWorld()->getFasterstLapTime()
if(isPlayerKart())
{
// Put in in the highscore list???
//printf("Time per lap: %s %f\n", getName().c_str(), time_per_lap);
}
}
m_lap_start_time = RaceManager::getWorld()->getTime();
}
else if ( m_curr_track_coords.getY() > 300.0f && m_last_track_coords[1] < 20.0f)
{
m_race_lap-- ;
// Prevent cheating by setting time to a negative number, indicating
// that the line wasn't crossed properly.
m_lap_start_time = -1.0f;
} else
{ // Switch to fast music in case of follow the leader when only 3 karts are left
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER &&
RaceManager::getWorld()->getCurrentNumKarts()==3)
{
sound_manager->switchToFastMusic();
}
}
} // doLapCounting
//-----------------------------------------------------------------------------
void Kart::raceFinished(float time)
{
@@ -660,7 +536,7 @@ void Kart::update(float dt)
} // if m_rescue
m_attachment.update(dt);
/*smoke drawing control point*/
//smoke drawing control point
if ( user_config->m_smoke )
{
if (m_smoke_system != NULL)
@@ -668,7 +544,7 @@ void Kart::update(float dt)
} // user_config->smoke
updatePhysics(dt);
m_last_track_coords = m_curr_track_coords;
//kart_info.m_last_track_coords = kart_info.m_curr_track_coords;
Moveable::update(dt);
@@ -720,61 +596,11 @@ void Kart::update(float dt)
// Check if any herring was hit.
herring_manager->hitHerring(this);
if(RaceManager::getWorld()->isLinearRace()) updateSectorProgression();
//if(RaceManager::getWorld()->isLinearRace()) updateSectorProgression();
if(!m_finished_race && RaceManager::getWorld()->isLinearRace()) doLapCounting();
//if(!m_finished_race && RaceManager::getWorld()->isLinearRace()) doLapCounting();
processSkidMarks();
}
void Kart::updateSectorProgression()
{
// this method only makes sense for linear races
assert(RaceManager::getWorld()->isLinearRace());
// Save the last valid sector for forced rescue on shortcuts
if(m_track_sector != Track::UNKNOWN_SECTOR &&
!m_rescue )
{
m_shortcut_sector = m_track_sector;
}
int prev_sector = m_track_sector;
if(!m_rescue)
RaceManager::getTrack()->findRoadSector(getXYZ(), &m_track_sector);
// Check if the kart is taking a shortcut (if it's not already doing one):
if(!m_rescue && RaceManager::getTrack()->isShortcut(prev_sector, m_track_sector))
{
forceRescue(/*is rescue*/ true); // bring karts back to where they left the track.
if(isPlayerKart())
{
RaceGUI* m=(RaceGUI*)menu_manager->getRaceMenu();
// Can happen if the option menu is called
if(m)
m->addMessage(_("Invalid short-cut!!"), this, 2.0f, 60);
}
}
if (m_track_sector == Track::UNKNOWN_SECTOR && !m_rescue)
{
m_on_road = false;
if( m_curr_track_coords[0] > 0.0 )
m_track_sector = RaceManager::getTrack()->findOutOfRoadSector(
getXYZ(), Track::RS_RIGHT, prev_sector );
else
m_track_sector = RaceManager::getTrack()->findOutOfRoadSector(
getXYZ(), Track::RS_LEFT, prev_sector );
}
else
{
m_on_road = true;
}
RaceManager::getTrack()->spatialToTrack( m_curr_track_coords,
getXYZ(),
m_track_sector );
} // update
//-----------------------------------------------------------------------------
// Set zipper time, and apply one time additional speed boost
void Kart::handleZipper()
@@ -998,27 +824,17 @@ void Kart::updatePhysics (float dt)
} // updatePhysics
//-----------------------------------------------------------------------------
void Kart::forceRescue(bool is_shortcut)
void Kart::forceRescue()
{
m_rescue=true;
// If rescue is triggered while doing a shortcut, reset the kart to the
// segment where the shortcut started!! And then reset the shortcut
// flag, so that this shortcut is not counted!
if(is_shortcut)
{
m_track_sector = m_shortcut_sector;
}
} // forceRescue
//-----------------------------------------------------------------------------
/** Drops a kart which was rescued back on the track.
*/
void Kart::endRescue()
{
if ( m_track_sector > 0 ) m_track_sector-- ;
setXYZ( RaceManager::getTrack()->trackToSpatial(m_track_sector) );
btQuaternion heading(btVector3(0.0f, 0.0f, 1.0f),
DEGREE_TO_RAD(RaceManager::getTrack()->m_angle[m_track_sector]) );
setRotation(heading);
RaceManager::getWorld()->moveKartAfterRescue(this);
m_rescue = false ;
m_body->setLinearVelocity (btVector3(0.0f,0.0f,0.0f));
@@ -1028,17 +844,18 @@ void Kart::endRescue()
// it feels better if the kart is left where it was. Perhaps
// this code should only be used if a rescue was not triggered
// by the kart being upside down??
// FIXME - why is transform set twice?
/*
btTransform pos;
// A certain epsilon is added here to the Z coordinate (0.1), in case
// that the drivelines are somewhat under the track. Otherwise, the
// kart will be placed a little bit under the track, triggering
// a rescue, ...
pos.setOrigin(getXYZ()+btVector3(0, 0, 0.5f*getKartHeight()+0.1f));
pos.setRotation(btQuaternion(btVector3(0.0f, 0.0f, 1.0f),
DEGREE_TO_RAD(RaceManager::getTrack()->m_angle[m_track_sector])));
m_body->setCenterOfMassTransform(pos);
*/
RaceManager::getWorld()->getPhysics()->addKart(this, m_vehicle);
setTrans(pos);
//setTrans(pos);
} // endRescue
//-----------------------------------------------------------------------------
@@ -1199,27 +1016,4 @@ void Kart::updateGraphics(const Vec3& off_xyz, const Vec3& off_hpr)
Moveable::updateGraphics(center_shift, Vec3(0, offset_pitch, 0));
} // updateGraphics
//-----------------------------------------------------------------------------
float Kart::estimateFinishTime ()
{
// 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.
float distance_covered = getLap()*RaceManager::getTrack()->getTrackLength()
+ getDistanceDownTrack();
// In case that a kart is rescued behind start line, or ...
if(distance_covered<0) distance_covered =1.0f;
float average_speed = distance_covered/RaceManager::getWorld()->getTime();
// Finish time is the time needed for the whole race with
// the average speed computed above.
return race_manager->getNumLaps()*RaceManager::getTrack()->getTrackLength()
/ average_speed;
} // estimateFinishTime
/* EOF */

View File

@@ -41,34 +41,25 @@ class Kart : public TerrainInfo, public Moveable
{
private:
btTransform m_reset_transform; // reset position
Vec3 m_curr_track_coords;
Vec3 m_last_track_coords;
unsigned int m_world_kart_id; // index of kart in world
protected:
bool m_on_road; // true if the kart is on top of the
// road path drawn by the drivelines
Attachment m_attachment;
Collectable m_collectable;
int m_grid_position;
int m_race_position; // current race position (1-numKarts)
int m_initial_position; // initial position of kart
KartControl m_controls; // The position of the karts controls
int m_track_sector; // index in driveline, special values
// e.g. UNKNOWN_SECTOR can be negative!
float m_max_speed; // maximum speed of the kart, computed from
float m_max_gear_rpm; //maximum engine rpm's for the current gear
float m_max_speed_reverse_ratio;
float m_wheelie_angle;
float m_zipper_time_left; // zipper time left
float m_lap_start_time; // Time at start of a new lap
char m_fastest_lap_message[255];
//char m_fastest_lap_message[255];
float m_bounce_back_time; // a short time after a collision acceleration
// is disabled to allow the karts to bounce back
int m_shortcut_sector; // segment on which the shortcut was started
// physics parameters, storing it saves time
btRaycastVehicle::btVehicleTuning *m_tuning;
btCompoundShape m_kart_chassis;
@@ -92,8 +83,7 @@ private:
SkidMark* m_skidmark_left;
SkidMark* m_skidmark_right;
int m_race_lap; // number of finished(!) laps
float m_time_at_last_lap; // time at finishing last lap
float m_finish_time;
bool m_finished_race;
@@ -113,7 +103,7 @@ protected:
and if so assign them to wheel_* variables */
void load_wheels (ssgBranch* obj);
virtual void doLapCounting ();
//virtual void doLapCounting ();
public:
Kart(const std::string& kart_name, int position,
@@ -135,9 +125,9 @@ public:
{ m_race_position = p; }
// these methods only makes sense for linear races
int getSector () const;
float getDistanceDownTrack() const;
float getDistanceToCenter () const;
//int getSector () const;
//float getDistanceDownTrack() const;
//float getDistanceToCenter () const;
Attachment *getAttachment () { return &m_attachment; }
void setAttachmentType (attachmentType t, float time_left=0.0f,
@@ -146,13 +136,12 @@ public:
Collectable *getCollectable () { return &m_collectable; }
int getNumCollectables () const { return m_collectable.getNum();}
int getNumHerring () const { return m_num_herrings_gobbled;}
int getLap () const { return m_race_lap; }
int getPosition () const { return m_race_position; }
int getInitialPosition () const { return m_initial_position; }
float getFinishTime () const { return m_finish_time; }
bool hasFinishedRace () const { return m_finished_race; }
void endRescue ();
float estimateFinishTime ();
//float estimateFinishTime ();
void processSkidMarks ();
void getClosestKart (float *cdist, int *closest);
void updatePhysics (float dt);
@@ -197,8 +186,6 @@ public:
const KartControl&
getControls () const {return m_controls; }
float getMaxSpeed () const {return m_max_speed; }
void setTimeAtLap (float t){m_time_at_last_lap=t; }
float getTimeAtLap () const {return m_time_at_last_lap; }
void createPhysics (ssgEntity *obj);
float getKartLength () const {return m_kart_properties->getKartLength();}
float getKartHeight () const {return m_kart_properties->getKartHeight();}
@@ -220,7 +207,7 @@ public:
bool isRescue () const {return m_rescue;}
void resetBrakes ();
void adjustSpeedWeight(float f);
void forceRescue (bool is_rescue=false);
void forceRescue ();
void handleExplosion (const Vec3& pos, bool direct_hit);
const std::string& getName () const {return m_kart_properties->getName();}
const std::string& getIdent () const {return m_kart_properties->getIdent();}
@@ -233,7 +220,7 @@ public:
virtual void crashed (Kart *k);
virtual void update (float dt);
void updateSectorProgression();
//void updateSectorProgression();
virtual void raceFinished (float time);
};

View File

@@ -19,9 +19,10 @@
#include "unlock_manager.hpp"
#include "gui/menu_manager.hpp"
#include "user_config.hpp"
#include "translation.hpp"
//-----------------------------------------------------------------------------
FollowTheLeaderRace::FollowTheLeaderRace() : World(), Clock::ClockListener()
FollowTheLeaderRace::FollowTheLeaderRace() : LinearWorld(), Clock::ClockListener()
{
m_leader_intervals = stk_config->m_leader_intervals;
@@ -67,7 +68,7 @@ void FollowTheLeaderRace::update(float delta)
{
m_clock.updateClock(delta);
World::update(delta);
LinearWorld::update(delta);
if(!m_clock.isRacePhase()) return;
if(m_clock.getTime() < 0.0f)
@@ -125,4 +126,10 @@ void FollowTheLeaderRace::restartRace()
std::string FollowTheLeaderRace::getInternalCode() const
{
return "FOLLOW_LEADER";
}
//-----------------------------------------------------------------------------
KartIconDisplayInfo* FollowTheLeaderRace::getKartsDisplayInfo(const RaceGUI* caller)
{
LinearWorld::getKartsDisplayInfo(caller);
m_kart_display_info[0].special_title = _("Leader");
}

View File

@@ -18,9 +18,9 @@
#ifndef _follow_the_leader_hpp_
#define _follow_the_leader_hpp_
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
class FollowTheLeaderRace : public World, public Clock::ClockListener
class FollowTheLeaderRace : public LinearWorld, public Clock::ClockListener
{
std::vector<float> m_leader_intervals; // time till elimination in follow leader
public:
@@ -37,6 +37,7 @@ public:
virtual void restartRace();
virtual std::string getInternalCode() const;
virtual bool useFastMusicNearEnd() const { return false; }
virtual KartIconDisplayInfo* getKartsDisplayInfo(const RaceGUI* caller);
};

469
src/modes/linear_world.cpp Normal file
View File

@@ -0,0 +1,469 @@
// 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 "gui/race_gui.hpp"
#include "track.hpp"
#include "gui/menu_manager.hpp"
#include "translation.hpp"
#include "audio/sound_manager.hpp"
//-----------------------------------------------------------------------------
LinearWorld::LinearWorld() : World()
{
const unsigned int kart_amount = m_kart.size();
m_kart_display_info = new KartIconDisplayInfo[kart_amount];
for(unsigned int n=0; n<kart_amount; n++)
{
KartInfo info;
info.m_track_sector = Track::UNKNOWN_SECTOR;
info.m_lap_start_time = -1.0f;
info.m_shortcut_sector = Track::UNKNOWN_SECTOR;
RaceManager::getTrack()->findRoadSector(m_kart[n]->getXYZ(), &info.m_track_sector);
//If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of
//the road, so we have to use another function to find the sector.
if (info.m_track_sector == Track::UNKNOWN_SECTOR )
{
info.m_on_road = false;
info.m_track_sector =
RaceManager::getTrack()->findOutOfRoadSector(m_kart[n]->getXYZ(),
Track::RS_DONT_KNOW,
Track::UNKNOWN_SECTOR );
}
else
{
info.m_on_road = true;
}
RaceManager::getTrack()->spatialToTrack(info.m_curr_track_coords,
m_kart[n]->getXYZ(),
info.m_track_sector );
info.m_race_lap = -1;
info.m_lap_start_time = -1.0f;
info.m_time_at_last_lap = 99999.9f;
m_kart_info.push_back(info);
}// next kart
}
//-----------------------------------------------------------------------------
void LinearWorld::update(float delta)
{
// store previous kart locations
const unsigned int kart_amount = m_kart_info.size();
for(unsigned int n=0; n<kart_amount; n++)
{
m_kart_info[n].m_last_track_coords = m_kart_info[n].m_curr_track_coords;
}
// run generic parent stuff that applies to all modes
World::update(delta);
// ------------- do stuff specific to this subtype of race -----
for(unsigned int n=0; n<kart_amount; n++)
{
KartInfo& kart_info = m_kart_info[n];
Kart* kart = m_kart[n];
// ---------- update rank ------
if(!m_kart[n]->hasFinishedRace()) updateRacePosition(kart, kart_info);
}
for(unsigned int n=0; n<kart_amount; n++)
{
KartInfo& kart_info = m_kart_info[n];
Kart* kart = m_kart[n];
// ---------- deal with sector data ---------
// Save the last valid sector for forced rescue on shortcuts
if(kart_info.m_track_sector != Track::UNKNOWN_SECTOR &&
!kart->isRescue() )
{
kart_info.m_shortcut_sector = kart_info.m_track_sector;
}
int prev_sector = kart_info.m_track_sector;
if(!kart->isRescue())
RaceManager::getTrack()->findRoadSector( kart->getXYZ(), &kart_info.m_track_sector);
// Check if the kart is taking a shortcut (if it's not already doing one):
if(!kart->isRescue() && RaceManager::getTrack()->isShortcut(prev_sector, kart_info.m_track_sector))
{
forceRescue(kart, kart_info, /*is rescue*/ true); // bring karts back to where they left the track.
if(kart->isPlayerKart())
{
RaceGUI* m=(RaceGUI*)menu_manager->getRaceMenu();
// Can happen if the option menu is called
if(m)
m->addMessage(_("Invalid short-cut!!"), kart, 2.0f, 60);
}
}
if (kart_info.m_track_sector == Track::UNKNOWN_SECTOR && !kart->isRescue())
{
kart_info.m_on_road = false;
if( kart_info.m_curr_track_coords[0] > 0.0 )
kart_info.m_track_sector =
RaceManager::getTrack()->findOutOfRoadSector( kart->getXYZ(),
Track::RS_RIGHT,
prev_sector );
else
kart_info.m_track_sector =
RaceManager::getTrack()->findOutOfRoadSector( kart->getXYZ(),
Track::RS_LEFT,
prev_sector );
}
else
{
kart_info.m_on_road = true;
}
RaceManager::getTrack()->spatialToTrack( kart_info.m_curr_track_coords /* out */,
kart->getXYZ(),
kart_info.m_track_sector );
// ------- check the kart isn't going in the wrong way ------
// only relevant for player karts
if(m_kart[n]->isPlayerKart())
{
RaceGUI* m=menu_manager->getRaceMenu();
// This can happen if the option menu is called, since the
// racegui gets deleted
if(!m) return;
// check if the player is going in the wrong direction
if(race_manager->getDifficulty()==RaceManager::RD_EASY)
{
float angle_diff = RAD_TO_DEGREE(kart->getHPR().getHeading()) -
RaceManager::getTrack()->m_angle[kart_info.m_track_sector];
if(angle_diff > 180.0f) angle_diff -= 360.0f;
else if (angle_diff < -180.0f) angle_diff += 360.0f;
// Display a warning message if the kart is going back way (unless
// the kart has already finished the race).
if ((angle_diff > 120.0f || angle_diff < -120.0f) &&
kart->getVelocity().getY() > 0.0f && !kart->hasFinishedRace() )
{
m->addMessage(_("WRONG WAY!"), kart, -1.0f, 60);
} // if angle is too big
} // if difficulty easy
}// end if is player kart
// --------- do lap counting ------
doLapCounting(kart_info, kart);
}// next kart
}
//-----------------------------------------------------------------------------
void LinearWorld::doLapCounting ( KartInfo& kart_info, Kart* kart )
{
bool newLap = kart_info.m_last_track_coords[1] > 300.0f &&
kart_info.m_curr_track_coords.getY() < 20.0f;
if ( newLap )
{
// 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 <= race_manager->getNumLaps())
{
setTimeAtLapForKart( RaceManager::getWorld()->getTime(), kart->getWorldKartId() );
kart_info.m_race_lap++ ;
}
// Race finished
// =============
if(kart_info.m_race_lap >= race_manager->getNumLaps() &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
kart->raceFinished(RaceManager::getWorld()->getTime());
}
// Only do timings if original time was set properly. Driving backwards
// over the start line will cause the lap start time to be set to -1.
if(kart_info.m_lap_start_time>=0.0)
{
float time_per_lap;
if (kart_info.m_race_lap == 1) // just completed first lap
{
time_per_lap=RaceManager::getWorld()->getTime();
}
else //completing subsequent laps
{
time_per_lap=RaceManager::getWorld()->getTime() - kart_info.m_lap_start_time;
}
if(time_per_lap < RaceManager::getWorld()->getFastestLapTime() &&
race_manager->raceHasLaps())
{
RaceManager::getWorld()->setFastestLap(kart, time_per_lap);
RaceGUI* m=(RaceGUI*)menu_manager->getRaceMenu();
if(m)
{
m->addMessage(_("New fastest lap"), NULL,
2.0f, 40, 100, 210, 100);
char s[20];
m->TimeToString(time_per_lap, s);
char m_fastest_lap_message[255];
snprintf(m_fastest_lap_message, sizeof(m_fastest_lap_message),
"%s: %s",s, kart->getName().c_str());
m->addMessage(m_fastest_lap_message, NULL,
2.0f, 40, 100, 210, 100);
} // if m
} // if time_per_lap < RaceManager::getWorld()->getFasterstLapTime()
//if(kart->isPlayerKart())
//{
// Put in in the highscore list???
//printf("Time per lap: %s %f\n", getName().c_str(), time_per_lap);
//}
}
kart_info.m_lap_start_time = RaceManager::getWorld()->getTime();
}
else if ( kart_info.m_curr_track_coords.getY() > 300.0f && kart_info.m_last_track_coords[1] < 20.0f)
{
kart_info.m_race_lap-- ;
// Prevent cheating by setting time to a negative number, indicating
// that the line wasn't crossed properly.
kart_info.m_lap_start_time = -1.0f;
} else
{ // Switch to fast music in case of follow the leader when only 3 karts are left
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER &&
RaceManager::getWorld()->getCurrentNumKarts()==3)
{
sound_manager->switchToFastMusic();
}
}
} // doLapCounting
//-----------------------------------------------------------------------------
int LinearWorld::getSectorForKart(const int kart_id) const
{
return m_kart_info[kart_id].m_track_sector;
}
//-----------------------------------------------------------------------------
float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const
{
return m_kart_info[kart_id].m_curr_track_coords.getY();
}
//-----------------------------------------------------------------------------
float LinearWorld::getDistanceToCenterForKart(const int kart_id) const
{
return m_kart_info[kart_id].m_curr_track_coords.getX();
}
//-----------------------------------------------------------------------------
int LinearWorld::getLapForKart(const int kart_id) const
{
return m_kart_info[kart_id].m_race_lap;
}
//-----------------------------------------------------------------------------
void LinearWorld::setTimeAtLapForKart(float t, const int kart_id)
{
m_kart_info[kart_id].m_time_at_last_lap=t;
}
//-----------------------------------------------------------------------------
float LinearWorld::getTimeAtLapForKart(const int kart_id) const
{
return m_kart_info[kart_id].m_time_at_last_lap;
}
//-----------------------------------------------------------------------------
KartIconDisplayInfo* LinearWorld::getKartsDisplayInfo(const RaceGUI* caller)
{
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 int kart_amount = race_manager->getNumKarts();
for(unsigned int i = 0; i < kart_amount ; i++)
{
KartIconDisplayInfo& rank_info = m_kart_display_info[i];
KartInfo& kart_info = m_kart_info[i];
Kart* kart = m_kart[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;
//rank_info.rank = kart->getPosition();
const int position = kart->getPosition();
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);
}
if(laps_of_leader>0 && // Display position during first lap
(getTime() - getTimeAtLapForKart(kart->getWorldKartId())<5.0f || current_lap!=laps_of_leader) &&
race_manager->raceHasLaps())
{ // Display for 5 seconds
char str[256];
if(position==1)
{
str[0]=' '; str[1]=0;
caller->TimeToString(getTimeAtLapForKart(kart->getWorldKartId()), str+1);
}
else
{
float timeBehind;
timeBehind = (kart_info.m_race_lap==laps_of_leader ? getTimeAtLapForKart(kart->getWorldKartId()) : RaceManager::getWorld()->getTime())
- time_of_leader;
str[0]='+'; str[1]=0;
caller->TimeToString(timeBehind, str+1);
}
rank_info.time = str;
}
else
{
rank_info.time = "";
}
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;
}
//-----------------------------------------------------------------------------
void LinearWorld::terminateRace()
{
World::terminateRace();
// if some karts have not yet finished the race yet, estimate
// their times and use these values to proceed without waiting
const int kart_amount = m_kart.size();
for ( Karts::size_type i = 0; i < kart_amount; ++i)
{
if(!m_kart[i]->hasFinishedRace())
{
const float est_finish_time = estimateFinishTimeForKart(m_kart[i], m_kart_info[i]);
m_kart[i]->raceFinished(est_finish_time);
} // if !hasFinishedRace
} // for i
}
float LinearWorld::estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info)
{
// 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.
float distance_covered = kart_info.m_race_lap * RaceManager::getTrack()->getTrackLength()
+ getDistanceDownTrackForKart(kart->getWorldKartId());
// In case that a kart is rescued behind start line, or ...
if(distance_covered<0) distance_covered =1.0f;
float average_speed = distance_covered/RaceManager::getWorld()->getTime();
// Finish time is the time needed for the whole race with
// the average speed computed above.
return race_manager->getNumLaps()*RaceManager::getTrack()->getTrackLength()
/ average_speed;
} // estimateFinishTime
//-----------------------------------------------------------------------------
void LinearWorld::forceRescue(Kart* kart, KartInfo& kart_info, bool shortcut)
{
// If rescue is triggered while doing a shortcut, reset the kart to the
// segment where the shortcut started!! And then reset the shortcut
// flag, so that this shortcut is not counted!
if(shortcut)
{
kart_info.m_track_sector = kart_info.m_shortcut_sector;
}
kart->forceRescue();
}
//-----------------------------------------------------------------------------
void LinearWorld::moveKartAfterRescue(Kart* kart)
{
KartInfo& info = m_kart_info[kart->getWorldKartId()];
if ( info.m_track_sector > 0 ) info.m_track_sector-- ;
kart->setXYZ( RaceManager::getTrack()->trackToSpatial(info.m_track_sector) );
btQuaternion heading(btVector3(0.0f, 0.0f, 1.0f),
DEGREE_TO_RAD(RaceManager::getTrack()->m_angle[info.m_track_sector]) );
kart->setRotation(heading);
}
//-----------------------------------------------------------------------------
/** Find the position (rank) of 'kart'
*
*/
void LinearWorld::updateRacePosition ( Kart* kart, KartInfo& kart_info )
{
int p = 1 ;
const unsigned int kart_amount = m_kart.size();
for ( int j = 0 ; j < kart_amount ; j++ )
{
if(int(j) == kart->getWorldKartId()) continue; // don't compare a kart with itself
if(m_kart[j]->isEliminated()) continue; // dismiss eliminated karts
// Count karts ahead of the current kart, i.e. kart that are already
// finished (the current kart k has not yet finished!!), have done more
// laps, or the same number of laps, but a greater distance.
if (
/* has already finished */
m_kart[j]->hasFinishedRace() ||
/* has done more lapses */
getLapForKart(m_kart[j]->getWorldKartId()) > getLapForKart(kart->getWorldKartId()) ||
/* is at the same lap but further in it */
(getLapForKart(m_kart[j]->getWorldKartId()) == getLapForKart(kart->getWorldKartId()) &&
getDistanceDownTrackForKart(m_kart[j]->getWorldKartId()) > getDistanceDownTrackForKart(kart->getWorldKartId()) )
)
p++ ;
}//next kart
kart->setPosition(p);
// 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() &&
estimateFinishTimeForKart( kart, m_kart_info[kart->getWorldKartId()] )-getTime()<30.0f )
{
sound_manager->switchToFastMusic();
m_faster_music_active=true;
}
} // updateRacePosition

View File

@@ -0,0 +1,89 @@
// 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 _LINEAR_WORLD_H_
#define _LINEAR_WORLD_H_
#include "modes/world.hpp"
#include <vector>
struct KartIconDisplayInfo;
class RaceGUI;
/** Some additional info that needs to be kept for each kart
* in this kind of race.
*/
struct KartInfo
{
int m_race_lap; // number of finished(!) laps
float m_time_at_last_lap; // time at finishing last lap
float m_lap_start_time; // Time at start of a new lap
int m_shortcut_sector; // segment on which the shortcut was started
int m_track_sector; // index in driveline, special values
// e.g. UNKNOWN_SECTOR can be negative!
Vec3 m_curr_track_coords;
Vec3 m_last_track_coords;
bool m_on_road; // true if the kart is on top of the
// road path drawn by the drivelines
};
/*
* A 'linear world' is a subcategory of world used in 'standard' races, i.e.
* with a start line and a road that loops. This includes management of drivelines
* and lap counting.
*/
class LinearWorld : public World
{
protected:
KartIconDisplayInfo* m_kart_display_info;
/** Linear races can trigger rescues for one additional reason : shortcuts.
* It may need to do some specific world before calling the generic Kart::forceRescue
*/
void forceRescue(Kart* kart, KartInfo& kart_info, bool shortcut);
void doLapCounting ( KartInfo& kart_info, Kart* kart );
public:
LinearWorld();
/** This vector contains an 'KartInfo' struct for every kart in the race.
* This member is not strictly private but try not to use it directly outside
* tightly related classes (e.g. AI)
*/
std::vector<KartInfo> m_kart_info;
virtual void update(float delta);
int getSectorForKart(const int kart_id) const;
float getDistanceDownTrackForKart(const int kart_id) const;
float getDistanceToCenterForKart(const int kart_id) const;
int getLapForKart(const int kart_id) const;
void setTimeAtLapForKart(float t, const int kart_id);
float getTimeAtLapForKart(const int kart_id) const;
virtual KartIconDisplayInfo* getKartsDisplayInfo(const RaceGUI* caller);
virtual void moveKartAfterRescue(Kart* kart);
virtual void terminateRace();
float estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info);
void updateRacePosition ( Kart* kart, KartInfo& kart_info );
};
#endif

View File

@@ -21,7 +21,7 @@
#include "gui/menu_manager.hpp"
//-----------------------------------------------------------------------------
StandardRace::StandardRace() : World(), Clock::ClockListener()
StandardRace::StandardRace() : LinearWorld(), Clock::ClockListener()
{
m_clock.registerEventListener(this);
m_clock.setMode(CHRONO);
@@ -67,7 +67,7 @@ void StandardRace::update(float delta)
{
m_clock.updateClock(delta);
World::update(delta);
LinearWorld::update(delta);
if(!m_clock.isRacePhase()) return;
// All karts are finished

View File

@@ -18,13 +18,13 @@
#ifndef _standard_race_
#define _standard_race_
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
/**
* Represents a standard race, i.e. with a start, end and laps.
* Used in Grand Prix, Quick Race and Time Trial.
*/
class StandardRace : public World, public Clock::ClockListener
class StandardRace : public LinearWorld, public Clock::ClockListener
{
public:
StandardRace();

View File

@@ -203,7 +203,6 @@ void World::terminateRace()
updateHighscores();
m_clock.pause();
menu_manager->pushMenu(MENUID_RACERESULT);
estimateFinishTimes();
unlock_manager->raceFinished();
}
//-----------------------------------------------------------------------------
@@ -276,14 +275,14 @@ void World::update(float dt)
projectile_manager->update(dt);
herring_manager->update(dt);
/*
for ( Karts::size_type i = 0 ; i < kart_amount; ++i)
{
if(m_kart[i]->isEliminated()) continue; // ignore eliminated kart
if(!m_kart[i]->hasFinishedRace()) updateRacePosition((int)i);
if(m_kart[i]->isPlayerKart()) m_kart[i]->addMessages(); // add 'wrong direction'
//if(isLinearRace() && !m_kart[i]->hasFinishedRace()) updateRacePosition((int)i);
//if(m_kart[i]->isPlayerKart()) m_kart[i]->addMessages(); // add 'wrong direction'
}
*/
/* Routine stuff we do even when paused */
callback_manager->update(dt);
}
@@ -354,22 +353,6 @@ void World::updateHighscores()
}
delete []index;
} // updateHighscores
//-----------------------------------------------------------------------------
void World::estimateFinishTimes()
{
const int kart_amount = m_kart.size();
for ( Karts::size_type i = 0; i < kart_amount; ++i)
{
if(!m_kart[i]->hasFinishedRace())
{
const float est_finish_time = m_kart[i]->estimateFinishTime();
m_kart[i]->raceFinished(est_finish_time);
} // if !hasFinishedRace
} // for i
} // estimateFinishTimes
//-----------------------------------------------------------------------------
void World::printProfileResultAndExit()
{
@@ -433,45 +416,6 @@ void World::removeKart(int kart_number)
m_eliminated_karts++;
} // removeKart
//-----------------------------------------------------------------------------
void World::updateRacePosition ( int k )
{
int p = 1 ;
/* Find position of kart 'k' */
const unsigned int kart_amount = m_kart.size();
for ( Karts::size_type j = 0 ; j < kart_amount ; ++j )
{
if(int(j) == k) continue;
if(m_kart[j]->isEliminated()) continue; // eliminated karts
// Count karts ahead of the current kart, i.e. kart that are already
// finished (the current kart k has not yet finished!!), have done more
// laps, or the same number of laps, but a greater distance.
if (m_kart[j]->hasFinishedRace() ||
m_kart[j]->getLap() > m_kart[k]->getLap() ||
(m_kart[j]->getLap() == m_kart[k]->getLap() &&
m_kart[j]->getDistanceDownTrack() > m_kart[k]->getDistanceDownTrack()) )
p++ ;
}
m_kart[k]->setPosition(p);
// 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 &&
m_kart[k]->getLap()==race_manager->getNumLaps()-1 &&
p==1 &&
useFastMusicNearEnd() &&
m_kart[k]->estimateFinishTime()-getTime()<30.0f )
{
sound_manager->switchToFastMusic();
m_faster_music_active=true;
}
} // updateRacePosition
//-----------------------------------------------------------------------------
/** Cleans up old herrings (from a previous race), removes old track specific
* herring models, and loads the actual track.

View File

@@ -34,6 +34,8 @@
#include "modes/clock.hpp"
class SFXBase;
struct KartIconDisplayInfo;
class RaceGUI;
/** This class is responsible for running the actual race. A world is created
* by the race manager on the start of each race (so a new world is created
@@ -75,7 +77,6 @@ class SFXBase;
* RaceManager).
*/
class World
{
public:
@@ -109,7 +110,7 @@ protected:
*/
bool m_use_highscores;
void updateRacePosition(int k);
//void updateRacePosition(int k);
void updateHighscores ();
void loadTrack ();
void resetAllKarts ();
@@ -156,7 +157,7 @@ public:
/** Called when race is over and should be terminated (mostly called by the clock).
*/
void terminateRace();
virtual void terminateRace();
/** Called to determine the default collectibles to give each player for this
* kind of race. Both parameters are of 'out' type.
@@ -191,6 +192,16 @@ public:
* sectors and drivelines are used, etc. This will be off for e.g. battle mode.
*/
bool isLinearRace() const { return m_linear_race; }
/** Called by the code that draws the list of karts on the race GUI
* to know what needs to be drawn in the current mode
*/
virtual KartIconDisplayInfo* getKartsDisplayInfo(const RaceGUI* caller) = 0;
/** Since each mode will have a different way of deciding where a rescued
* kart is dropped, this method will be called and each mode can implement it.
*/
virtual void moveKartAfterRescue(Kart* kart) = 0;
};
#endif

View File

@@ -307,34 +307,3 @@ void PlayerKart::collectedHerring(const Herring &herring, int add_info)
}
} // collectedHerring
//-----------------------------------------------------------------------------
/** This function is called by world to add any messages to the race gui. This
* can't be done (in some cases) in the update() function, since update can be
* called several times per frame, resulting in messages being displayed more
* than once.
**/
void PlayerKart::addMessages()
{
RaceGUI* m=menu_manager->getRaceMenu();
// This can happen if the option menu is called, since the
// racegui gets deleted
if(!m) return;
// 1) check if the player is going in the wrong direction
// ------------------------------------------------------
if(race_manager->getDifficulty()==RaceManager::RD_EASY)
{
float angle_diff = RAD_TO_DEGREE(getHPR().getHeading()) - RaceManager::getTrack()->m_angle[getSector()];
if(angle_diff > 180.0f) angle_diff -= 360.0f;
else if (angle_diff < -180.0f) angle_diff += 360.0f;
// Display a warning message if the kart is going back way (unless
// the kart has already finished the race).
if ((angle_diff > 120.0f || angle_diff < -120.0f) &&
getVelocity().getY() > 0.0f && !hasFinishedRace() )
{
m->addMessage(_("WRONG WAY!"), this, -1.0f, 60);
} // if angle is too big
} // if difficulty easy
} // addMessages
/* EOF */

View File

@@ -58,7 +58,6 @@ public:
int earlyStartPenalty () {return m_penalty_time>0; }
Player *getPlayer () {return m_player; }
void update (float);
void addMessages ();
void action (KartAction action, int value);
void handleZipper ();
void collectedHerring (const Herring &herring, int add_info=-1);

View File

@@ -39,7 +39,7 @@
#include <iostream>
#include "constants.hpp"
#include "scene.hpp"
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
#include "race_manager.hpp"
#include "network/network_manager.hpp"
@@ -162,14 +162,20 @@ void DefaultRobot::handle_braking()
m_controls.brake = true;
return;
}
const float MIN_SPEED = RaceManager::getTrack()->getWidth()[m_track_sector];
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
const float MIN_SPEED = RaceManager::getTrack()->getWidth()[kart_info.m_track_sector];
//We may brake if we are about to get out of the road, but only if the
//kart is on top of the road, and if we won't slow down below a certain
//limit.
if ( m_crashes.m_road && m_on_road && getVelocityLC().getY() > MIN_SPEED)
if ( m_crashes.m_road && kart_info.m_on_road && getVelocityLC().getY() > MIN_SPEED)
{
float kart_ang_diff = RaceManager::getTrack()->m_angle[m_track_sector] -
float kart_ang_diff = RaceManager::getTrack()->m_angle[kart_info.m_track_sector] -
RAD_TO_DEGREE(getHPR().getHeading());
kart_ang_diff = normalize_angle(kart_ang_diff);
kart_ang_diff = fabsf(kart_ang_diff);
@@ -184,7 +190,7 @@ void DefaultRobot::handle_braking()
//if the curve angle is bigger than what the kart can steer, brake
//even if we are in the inside, because the kart would be 'thrown'
//out of the curve.
if(!(getDistanceToCenter() > RaceManager::getTrack()->getWidth()[m_track_sector] *
if(!(lworld->getDistanceToCenterForKart(getWorldKartId()) > RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] *
-CURVE_INSIDE_PERC || m_curve_angle > getMaxSteerAngle()))
{
m_controls.brake = false;
@@ -193,7 +199,7 @@ void DefaultRobot::handle_braking()
}
else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
{
if(!(getDistanceToCenter() < RaceManager::getTrack()->getWidth()[m_track_sector] *
if(!(lworld->getDistanceToCenterForKart( getWorldKartId() ) < RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] *
CURVE_INSIDE_PERC || m_curve_angle < -getMaxSteerAngle()))
{
m_controls.brake = false;
@@ -224,17 +230,22 @@ void DefaultRobot::handle_braking()
//-----------------------------------------------------------------------------
void DefaultRobot::handle_steering()
{
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
const unsigned int DRIVELINE_SIZE = (unsigned int)RaceManager::getTrack()->m_driveline.size();
const size_t NEXT_SECTOR = (unsigned int)m_track_sector + 1 < DRIVELINE_SIZE ?
m_track_sector + 1 : 0;
const size_t NEXT_SECTOR = (unsigned int)kart_info.m_track_sector + 1 < DRIVELINE_SIZE ?
kart_info.m_track_sector + 1 : 0;
float steer_angle = 0.0f;
/*The AI responds based on the information we just gathered, using a
*finite state machine.
*/
//Reaction to being outside of the road
if( fabsf(getDistanceToCenter()) + 0.5 >
RaceManager::getTrack()->getWidth()[m_track_sector] )
if( fabsf(lworld->getDistanceToCenterForKart( getWorldKartId() )) + 0.5 >
RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] )
{
steer_angle = steer_to_point( RaceManager::getTrack()->
m_driveline[NEXT_SECTOR] );
@@ -261,8 +272,8 @@ void DefaultRobot::handle_steering()
}
else
{
if(getDistanceToCenter() > RaceManager::getKart(m_crashes.m_kart)->
getDistanceToCenter())
if(lworld->getDistanceToCenterForKart( getWorldKartId() ) >
lworld->getDistanceToCenterForKart( m_crashes.m_kart ))
{
steer_angle = steer_to_angle( NEXT_SECTOR, -90.0f );
m_start_kart_crash_direction = 1;
@@ -300,7 +311,7 @@ void DefaultRobot::handle_steering()
case FT_AVOID_TRACK_CRASH:
if( m_crashes.m_road )
{
steer_angle = steer_to_angle( m_track_sector, 0.0f );
steer_angle = steer_to_angle( kart_info.m_track_sector, 0.0f );
}
else steer_angle = 0.0f;
@@ -329,6 +340,11 @@ void DefaultRobot::handle_items( const float DELTA, const int STEPS )
return;
}
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
m_time_since_last_shot += DELTA;
if( m_collectable.getType() != COLLECT_NOTHING )
{
@@ -347,7 +363,7 @@ void DefaultRobot::handle_items( const float DELTA, const int STEPS )
case COLLECT_ZIPPER:
{
const float ANGLE_DIFF = fabsf( normalize_angle(
RaceManager::getTrack()->m_angle[m_track_sector]-
RaceManager::getTrack()->m_angle[kart_info.m_track_sector]-
RAD_TO_DEGREE(getHPR().getHeading()) ) );
if( m_time_since_last_shot > 10.0f && ANGLE_DIFF <
@@ -434,6 +450,11 @@ bool DefaultRobot::do_wheelie ( const int STEPS )
if( m_crashes.m_road ) return false;
if( m_crashes.m_kart != -1 ) return false;
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
//We have to be careful with normalizing, because if the source argument
//has both the X and Y axis set to 0, it returns nan to the destiny.
const Vec3 &VEL = getVelocity();
@@ -467,7 +488,7 @@ bool DefaultRobot::do_wheelie ( const int STEPS )
distance = step_track_coord[0] > 0.0f ? step_track_coord[0]
: -step_track_coord[0];
if( distance > RaceManager::getTrack()->getWidth()[m_track_sector] )
if( distance > RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] )
{
return false;
}
@@ -688,8 +709,13 @@ void DefaultRobot::find_non_crashing_point( sgVec2 result )
{
const unsigned int DRIVELINE_SIZE = (unsigned int)RaceManager::getTrack()->m_driveline.size();
unsigned int sector = (unsigned int)m_track_sector + 1 < DRIVELINE_SIZE ?
m_track_sector + 1 : 0;
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
unsigned int sector = (unsigned int)kart_info.m_track_sector + 1 < DRIVELINE_SIZE ?
kart_info.m_track_sector + 1 : 0;
int target_sector;
Vec3 direction;
@@ -925,22 +951,27 @@ float DefaultRobot::get_approx_radius(const int START, const int END) const
*/
void DefaultRobot::find_curve()
{
// FIXME - don't re-create everytime, store at least the lworld
LinearWorld* lworld = dynamic_cast<LinearWorld*>(RaceManager::getWorld());
assert(lworld != NULL);
KartInfo& kart_info = lworld->m_kart_info[ getWorldKartId() ];
const int DRIVELINE_SIZE = (unsigned int)RaceManager::getTrack()->m_driveline.size();
float total_dist = 0.0f;
int next_hint = m_track_sector;
int next_hint = kart_info.m_track_sector;
int i;
for(i = m_track_sector; total_dist < getVelocityLC().getY(); i = next_hint)
for(i = kart_info.m_track_sector; total_dist < getVelocityLC().getY(); i = next_hint)
{
next_hint = i + 1 < DRIVELINE_SIZE ? i + 1 : 0;
total_dist += sgDistanceVec2(RaceManager::getTrack()->m_driveline[i], RaceManager::getTrack()->m_driveline[next_hint]);
}
m_curve_angle = normalize_angle(RaceManager::getTrack()->m_angle[i] - RaceManager::getTrack()->m_angle[m_track_sector]);
m_curve_angle = normalize_angle(RaceManager::getTrack()->m_angle[i] - RaceManager::getTrack()->m_angle[kart_info.m_track_sector]);
m_inner_curve = m_curve_angle > 0.0 ? -1 : 1;
// FIXME: 0.9 is the tire grip - but this was never used. For now this
// 0.9 is left in place to reproduce the same results and AI behaviour,
// but this function should be updated to bullet physics
m_curve_target_speed = sgSqrt(get_approx_radius(m_track_sector, i) * RaceManager::getTrack()->getGravity() * 0.9f);
m_curve_target_speed = sgSqrt(get_approx_radius(kart_info.m_track_sector, i) * RaceManager::getTrack()->getGravity() * 0.9f);
}