AI improvements: better (level dependent) skidding, and using nitro.
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2582 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
582bd86cdd
commit
d6ccd9cd1f
@ -135,10 +135,18 @@ void LinearWorld::update(float delta)
|
||||
|
||||
// ------------- do stuff specific to this subtype of race -----
|
||||
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
for(unsigned int i=0; i<kart_amount; i++)
|
||||
{
|
||||
// ---------- update rank ------
|
||||
if(!m_kart[n]->hasFinishedRace()) updateRacePosition(m_kart[n], m_kart_info[n]);
|
||||
if(!m_kart[i]->hasFinishedRace())
|
||||
{
|
||||
updateRacePosition(m_kart[i], m_kart_info[i]);
|
||||
// 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_kart[i]);
|
||||
}
|
||||
}
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
@ -320,6 +328,16 @@ void LinearWorld::setTimeAtLapForKart(float t, const int kart_id)
|
||||
{
|
||||
m_kart_info[kart_id].m_time_at_last_lap=t;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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(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
|
||||
{
|
||||
@ -423,7 +441,7 @@ void LinearWorld::terminateRace()
|
||||
{
|
||||
if(!m_kart[i]->hasFinishedRace())
|
||||
{
|
||||
const float est_finish_time = estimateFinishTimeForKart(m_kart[i], m_kart_info[i]);
|
||||
const float est_finish_time = m_kart_info[i].m_estimated_finish;
|
||||
m_kart[i]->raceFinished(est_finish_time);
|
||||
} // if !hasFinishedRace
|
||||
} // for i
|
||||
@ -437,7 +455,7 @@ void LinearWorld::raceResultOrder( int* order )
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
float LinearWorld::estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info)
|
||||
float LinearWorld::estimateFinishTimeForKart(Kart* kart)
|
||||
{
|
||||
// Estimate the arrival time of any karts that haven't arrived
|
||||
// yet by using their average speed up to now and the distance
|
||||
@ -446,6 +464,7 @@ float LinearWorld::estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info)
|
||||
// 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.
|
||||
const KartInfo &kart_info = m_kart_info[kart->getWorldKartId()];
|
||||
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 ...
|
||||
@ -535,11 +554,11 @@ void LinearWorld::updateRacePosition ( Kart* kart, KartInfo& kart_info )
|
||||
// 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 &&
|
||||
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 )
|
||||
p==1 &&
|
||||
useFastMusicNearEnd() &&
|
||||
kart_info.m_estimated_finish - getTime() < 30.0f )
|
||||
{
|
||||
sound_manager->switchToFastMusic();
|
||||
m_faster_music_active=true;
|
||||
|
@ -29,11 +29,13 @@ class RaceGUI;
|
||||
*/
|
||||
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_track_sector; // index in driveline, special values
|
||||
// e.g. UNKNOWN_SECTOR can be negative!
|
||||
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. */
|
||||
float m_estimated_finish; /**<During last lap only:
|
||||
* estimated finishing time! */
|
||||
int m_track_sector; /**<Index in driveline, special values
|
||||
* e.g. UNKNOWN_SECTOR can be negative!*/
|
||||
int m_last_valid_sector;
|
||||
Vec3 m_curr_track_coords;
|
||||
Vec3 m_last_track_coords;
|
||||
@ -58,7 +60,7 @@ protected:
|
||||
void forceRescue(Kart* kart, KartInfo& kart_info, bool shortcut);
|
||||
|
||||
void doLapCounting ( KartInfo& kart_info, Kart* kart );
|
||||
float estimateFinishTimeForKart (Kart* kart, KartInfo& kart_info);
|
||||
float estimateFinishTimeForKart(Kart* kart);
|
||||
void updateRacePosition ( Kart* kart, KartInfo& kart_info );
|
||||
public:
|
||||
LinearWorld();
|
||||
@ -79,6 +81,7 @@ public:
|
||||
int getSectorForKart(const int kart_id) const;
|
||||
float getDistanceDownTrackForKart(const int kart_id) const;
|
||||
float getDistanceToCenterForKart(const int kart_id) const;
|
||||
float getEstimatedFinishTime(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;
|
||||
|
@ -28,6 +28,7 @@
|
||||
//won't be erased the next time the function is called.
|
||||
#define SHOW_NON_CRASHING_POINT //If defined, draws a green sphere where the
|
||||
//n farthest non-crashing point is.
|
||||
#define _WINSOCKAPI_
|
||||
#include <plib/ssgAux.h>
|
||||
#endif
|
||||
|
||||
@ -72,24 +73,31 @@ DefaultRobot::DefaultRobot(const std::string& kart_name,
|
||||
m_item_tactic = IT_TEN_SECONDS;
|
||||
m_max_start_delay = 0.5f;
|
||||
m_min_steps = 0;
|
||||
m_skidding_threshold = 4.0f;
|
||||
break;
|
||||
case RaceManager::RD_MEDIUM:
|
||||
m_wait_for_players = true;
|
||||
m_wait_for_players = true;
|
||||
m_max_handicap_accel = 0.95f;
|
||||
m_fallback_tactic = FT_PARALLEL;
|
||||
m_item_tactic = IT_CALCULATE;
|
||||
m_max_start_delay = 0.4f;
|
||||
m_min_steps = 1;
|
||||
m_fallback_tactic = FT_PARALLEL;
|
||||
m_item_tactic = IT_CALCULATE;
|
||||
m_max_start_delay = 0.4f;
|
||||
m_min_steps = 1;
|
||||
m_skidding_threshold = 2.0f;
|
||||
break;
|
||||
case RaceManager::RD_HARD:
|
||||
m_wait_for_players = false;
|
||||
m_wait_for_players = false;
|
||||
m_max_handicap_accel = 1.0f;
|
||||
m_fallback_tactic = FT_FAREST_POINT;
|
||||
m_item_tactic = IT_CALCULATE;
|
||||
m_max_start_delay = 0.1f;
|
||||
m_min_steps = 2;
|
||||
m_fallback_tactic = FT_FAREST_POINT;
|
||||
m_item_tactic = IT_CALCULATE;
|
||||
m_max_start_delay = 0.1f;
|
||||
m_min_steps = 2;
|
||||
m_skidding_threshold = 1.3f;
|
||||
break;
|
||||
}
|
||||
static int count =0;
|
||||
m_skidding_threshold = count==0?4.0f: 1.3f;
|
||||
count = 1-count;
|
||||
|
||||
} // DefaultRobot
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -155,6 +163,7 @@ void DefaultRobot::update( float delta )
|
||||
handleItems( delta, steps );
|
||||
handleRescue( delta );
|
||||
handleBraking();
|
||||
handleNitro();
|
||||
|
||||
/*And obviously general kart stuff*/
|
||||
AutoKart::update( delta );
|
||||
@ -293,6 +302,7 @@ void DefaultRobot::handleSteering()
|
||||
}
|
||||
else
|
||||
{
|
||||
m_start_kart_crash_direction = 0;
|
||||
switch( m_fallback_tactic )
|
||||
{
|
||||
case FT_FAREST_POINT:
|
||||
@ -326,8 +336,7 @@ void DefaultRobot::handleSteering()
|
||||
if (fabsf(steer_angle) < 2.0f*3.1415/180.0f)
|
||||
steer_angle = 0.f;
|
||||
|
||||
m_controls.lr = angleToControl( steer_angle );
|
||||
m_controls.jump = fabsf(m_controls.lr)>0.99;
|
||||
setSteering(steer_angle);
|
||||
} // handleSteering
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -462,7 +471,7 @@ void DefaultRobot::handleRescue(const float DELTA)
|
||||
//TODO: check if we collided against a dynamic object (ej.:kart) or
|
||||
//against the track's static object.
|
||||
//The m_crash_time measures if a kart has been crashing for too long
|
||||
|
||||
#ifdef RESCUE_IF_CRASHES_WITH_KARTS
|
||||
m_crash_time += (m_collided && isOnGround()) ? 3.0f * DELTA : -0.25f * DELTA;
|
||||
if( m_crash_time < 0.0f ) m_crash_time = 0.0f;
|
||||
|
||||
@ -472,7 +481,7 @@ void DefaultRobot::handleRescue(const float DELTA)
|
||||
forceRescue();
|
||||
m_crash_time = 0.0f;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// check if kart is stuck
|
||||
if(getSpeed()<2.0f && !isRescue() && !RaceManager::getWorld()->isStartPhase())
|
||||
@ -490,6 +499,92 @@ void DefaultRobot::handleRescue(const float DELTA)
|
||||
}
|
||||
} // handleRescue
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Decides wether to use nitro or not.
|
||||
*/
|
||||
void DefaultRobot::handleNitro()
|
||||
{
|
||||
m_controls.wheelie = false;
|
||||
// Don't use nitro if the kart doesn't have any, is not on ground,
|
||||
if(getEnergy()==0 || !isOnGround() || hasFinishedRace() ) return;
|
||||
|
||||
// If a parachute or anvil is attached, the nitro doesn't give much
|
||||
// benefit. Better wait till later.
|
||||
const bool has_slowdown_attachment =
|
||||
m_attachment.getType()==ATTACH_PARACHUTE ||
|
||||
m_attachment.getType()==ATTACH_ANVIL;
|
||||
if(has_slowdown_attachment) return;
|
||||
|
||||
// If the kart is very slow (e.g. after rescue), use nitro
|
||||
if(getSpeed()<5)
|
||||
{
|
||||
m_controls.wheelie = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this kart is the last kart, and we have enough
|
||||
// (i.e. more than 2) nitro, use it.
|
||||
// -------------------------------------------------
|
||||
const unsigned int num_karts = race_manager->getNumKarts();
|
||||
if(getPosition()== num_karts && getEnergy()>2.0f)
|
||||
{
|
||||
m_controls.wheelie = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// On the last track shortly before the finishing line, use nitro
|
||||
// anyway. Since the kart is faster with nitro, estimate a 30% time
|
||||
// decrease.
|
||||
if(m_world->getLapForKart(getWorldKartId())==race_manager->getNumLaps()-1)
|
||||
{
|
||||
float finish = m_world->getEstimatedFinishTime(getWorldKartId());
|
||||
if( 1.3f*getEnergy() >= finish - m_world->getTime() )
|
||||
{
|
||||
m_controls.wheelie = true;
|
||||
printf("lasp lap --> nitro.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const float my_dist = m_world->getDistanceDownTrackForKart(getWorldKartId());
|
||||
// A kart within this distance is considered to be overtaking (or to be
|
||||
// overtaken).
|
||||
const float overtake_distance = 10.0f;
|
||||
for(unsigned int i=0; i<num_karts; i++)
|
||||
{
|
||||
Kart *kart = RaceManager::getKart(i);
|
||||
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
|
||||
|
||||
float dist = m_world->getDistanceDownTrackForKart(i);
|
||||
|
||||
// Kart too far behind to be a risk
|
||||
if(dist+overtake_distance<my_dist) continue;
|
||||
|
||||
// Kart too far ahead to try overtake
|
||||
if(dist-overtake_distance>my_dist) continue;
|
||||
|
||||
// Kart behind might overtake this kart
|
||||
// ------------------------------------
|
||||
if(dist<my_dist)
|
||||
{
|
||||
// Kart behind is slower than this kart - no need to use nitro
|
||||
if(kart->getSpeed() < getSpeed()) continue;
|
||||
|
||||
// Nitro doesn't give much benefit - better wait and
|
||||
// see if we can re-overtake once the attachment is gone
|
||||
m_controls.wheelie = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now there is a kart close ahead, try overtaking
|
||||
// -----------------------------------------------
|
||||
if(kart->getSpeed()+5.0f > getSpeed())
|
||||
{
|
||||
m_controls.wheelie = true;
|
||||
}
|
||||
}
|
||||
} // handleNitro
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
float DefaultRobot::steerToAngle (const size_t SECTOR, const float ANGLE)
|
||||
{
|
||||
@ -541,14 +636,12 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
const Vec3 &VEL = getVelocity();
|
||||
vel_normal.setValue(VEL.getX(), VEL.getY(), 0.0);
|
||||
float len=vel_normal.length();
|
||||
if(len>0.0f)
|
||||
{
|
||||
vel_normal/=len;
|
||||
}
|
||||
else
|
||||
{
|
||||
vel_normal.setValue(0.0, 0.0, 0.0);
|
||||
}
|
||||
// If the velocity is zero, no sense in checking for crashes in time
|
||||
if(len==0) return;
|
||||
|
||||
// Time it takes to drive for m_kart_length units.
|
||||
float dt = m_kart_length / len;
|
||||
vel_normal/=len;
|
||||
|
||||
for(int i = 1; STEPS > i; ++i)
|
||||
{
|
||||
@ -563,12 +656,13 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
{
|
||||
const Kart* kart = RaceManager::getKart(j);
|
||||
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
|
||||
const Kart *other_kart = RaceManager::getKart(j);
|
||||
Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt);
|
||||
kart_distance = (step_coord - other_kart_xyz).length_2d();
|
||||
|
||||
kart_distance = (step_coord - RaceManager::getKart(j)->getXYZ()).length_2d();
|
||||
|
||||
if( kart_distance < m_kart_length + 0.125f * i )
|
||||
if( getVelocityLC().getY() > RaceManager::getKart(j)->
|
||||
getVelocityLC().getY() * 0.75f ) m_crashes.m_kart = j;
|
||||
if( kart_distance < m_kart_length &&
|
||||
getVelocityLC().getY() > other_kart->getVelocityLC().getY())
|
||||
m_crashes.m_kart = j;
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,7 +686,7 @@ void DefaultRobot::checkCrashes( const int STEPS, const Vec3& pos )
|
||||
center[1] = step_coord[1];
|
||||
center[2] = pos[2];
|
||||
sphere->setCenter( center );
|
||||
sphere->setSize( m_kart_properties->getKartLength() );
|
||||
sphere->setSize( m_kart_properties->getKartModel()->getLength() );
|
||||
if( m_sector == Track::UNKNOWN_SECTOR )
|
||||
{
|
||||
sgVec4 colour;
|
||||
@ -696,7 +790,7 @@ void DefaultRobot::findNonCrashingPoint( sgVec2 result )
|
||||
sgVec3 center;
|
||||
center[0] = result[0];
|
||||
center[1] = result[1];
|
||||
center[2] = m_curr_pos.xyz[2];
|
||||
center[2] = getXYZ().getZ();
|
||||
sphere->setCenter( center );
|
||||
sphere->setSize( 0.5f );
|
||||
|
||||
@ -777,18 +871,18 @@ int DefaultRobot::calcSteps()
|
||||
} // calcSteps
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Translates coordinates from an angle(in degrees) to values within the range
|
||||
* of -1.0 to 1.0 to use the same format as the KartControl::lr variable.
|
||||
/** Converts the steering angle to a lr steering in the range of -1 to 1.
|
||||
* If the steering angle is too great, it will also trigger skidding.
|
||||
*/
|
||||
float DefaultRobot::angleToControl( float angle ) const
|
||||
void DefaultRobot::setSteering( float angle )
|
||||
{
|
||||
angle = angle / getMaxSteerAngle();
|
||||
m_controls.jump = fabsf(angle)>m_skidding_threshold;
|
||||
|
||||
if(angle > 1.0f) return 1.0f;
|
||||
else if(angle < -1.0f) return -1.0f;
|
||||
|
||||
return angle;
|
||||
} // angleToControl
|
||||
if (angle > 1.0f) m_controls.lr = 1.0f;
|
||||
else if(angle < -1.0f) m_controls.lr = -1.0f;
|
||||
else m_controls.lr = angle;
|
||||
} // setSteering
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Finds the approximate radius of a track's curve. It needs two arguments,
|
||||
|
@ -122,6 +122,12 @@ private:
|
||||
* deallocated. */
|
||||
static int m_num_of_track_info_instances;
|
||||
|
||||
/** The minimum steering angle at which the AI adds skidding. Lower values
|
||||
* tend to improve the line the AI is driving. This is used to adjust for
|
||||
* different AI levels.
|
||||
*/
|
||||
float m_skidding_threshold;
|
||||
|
||||
int m_sector;
|
||||
|
||||
/*Functions called directly from update(). They all represent an action
|
||||
@ -135,6 +141,7 @@ private:
|
||||
void handleItems(const float DELTA, const int STEPS);
|
||||
void handleRescue(const float DELTA);
|
||||
void handleBraking();
|
||||
void handleNitro();
|
||||
|
||||
/*Lower level functions not called directly from update()*/
|
||||
float steerToAngle(const size_t SECTOR, const float ANGLE);
|
||||
@ -145,7 +152,7 @@ private:
|
||||
|
||||
float normalizeAngle(float angle);
|
||||
int calcSteps();
|
||||
float angleToControl(float angle) const;
|
||||
void setSteering(float angle);
|
||||
float getApproxRadius(const int START, const int END) const;
|
||||
void findCurve();
|
||||
|
||||
|
@ -59,8 +59,8 @@ TrackInfo::DirectionType TrackInfo::computeDirection(int i)
|
||||
if( diff > M_PI ) diff -= 2*M_PI;
|
||||
else if( diff < -M_PI ) diff+= 2*M_PI;
|
||||
|
||||
|
||||
const float curve_degree = 15*M_PI/180.0f;
|
||||
// Consider a difference of up to 5 degrees as 'straight'.
|
||||
const float curve_degree = 5*M_PI/180.0f;
|
||||
DirectionType t = DIR_STRAIGHT;
|
||||
if (diff <-curve_degree)
|
||||
t = DIR_LEFT;
|
||||
|
Loading…
Reference in New Issue
Block a user