stk-code_catmod/src/robots/default_robot.cpp

978 lines
32 KiB
C++
Executable File

// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
//
// 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.
//The AI debugging works best with just 1 AI kart, so set the number of karts
//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
#undef AI_DEBUG
#ifdef AI_DEBUG
#define SHOW_FUTURE_PATH //If defined, it will put a bunch of spheres when it
//checks for crashes with the outside of the track.
#define ERASE_PATH //If not defined, the spheres drawn in the future path
//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.
#include <plib/ssgAux.h>
#endif
#include "default_robot.hpp"
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <iostream>
#include "constants.hpp"
#include "scene.hpp"
#include "modes/linear_world.hpp"
#include "race_manager.hpp"
#include "network/network_manager.hpp"
DefaultRobot::DefaultRobot(const std::string& kart_name,
int position, const btTransform& init_pos ) :
AutoKart( kart_name, position, init_pos )
{
reset();
switch( race_manager->getDifficulty())
{
case RaceManager::RD_EASY:
m_wait_for_players = true;
m_max_handicap_accel = 0.9f;
m_fallback_tactic = FT_AVOID_TRACK_CRASH;
m_use_wheelies = false;
m_wheelie_check_dist = 0.0f;
m_item_tactic = IT_TEN_SECONDS;
m_max_start_delay = 0.5f;
m_min_steps = 0;
break;
case RaceManager::RD_MEDIUM:
m_wait_for_players = true;
m_max_handicap_accel = 0.95f;
m_fallback_tactic = FT_PARALLEL;
m_use_wheelies = true;
m_wheelie_check_dist = 0.8f;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.4f;
m_min_steps = 1;
break;
case RaceManager::RD_HARD:
case RaceManager::RD_SKIDDING:
m_wait_for_players = false;
m_max_handicap_accel = 1.0f;
m_fallback_tactic = FT_FAREST_POINT;
m_use_wheelies = true;
m_wheelie_check_dist = 1.0f;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.1f;
m_min_steps = 2;
break;
}
}
//-----------------------------------------------------------------------------
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void DefaultRobot::update( float delta )
{
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
AutoKart::update( delta );
return;
}
if( RaceManager::getWorld()->getClock().isStartPhase() )
{
handle_race_start();
AutoKart::update( delta );
return;
}
/*Get information that is needed by more than 1 of the handling funcs*/
//Detect if we are going to crash with the track and/or kart
int steps = 0;
// This should not happen (anymore :) ), but it keeps the game running
// in case that m_future_sector becomes undefined.
if(m_future_sector == Track::UNKNOWN_SECTOR )
{
#ifdef DEBUG
fprintf(stderr,"DefaultRobot: m_future_sector is undefined.\n");
fprintf(stderr,"This shouldn't happen, but can be ignored.\n");
#endif
forceRescue();
m_future_sector = 0;
}
else
{
steps = calc_steps();
}
check_crashes( steps, getXYZ() );
find_curve();
/*Response handling functions*/
handle_acceleration( delta );
handle_steering();
handle_items( delta, steps );
handle_rescue( delta );
handle_wheelie( steps );
handle_braking();
//TODO:handle jumping
/*And obviously general kart stuff*/
AutoKart::update( delta );
m_collided = false;
} // update
//-----------------------------------------------------------------------------
void DefaultRobot::handle_wheelie( const int STEPS )
{
if( m_use_wheelies )
{
m_controls.wheelie = do_wheelie( STEPS );
}
}
//-----------------------------------------------------------------------------
void DefaultRobot::handle_braking()
{
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
getPosition() < RaceManager::getKart(0)->getPosition() &&
getInitialPosition()>1 )
{
m_controls.brake = true;
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() ];
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 && kart_info.m_on_road && getVelocityLC().getY() > MIN_SPEED)
{
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);
const float MIN_TRACK_ANGLE = 20.0f;
const float CURVE_INSIDE_PERC = 0.25f;
//Brake only if the road does not goes somewhat straight.
if(m_curve_angle > MIN_TRACK_ANGLE) //Next curve is left
{
//Avoid braking if the kart is in the inside of the curve, but
//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(!(lworld->getDistanceToCenterForKart(getWorldKartId()) > RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] *
-CURVE_INSIDE_PERC || m_curve_angle > getMaxSteerAngle()))
{
m_controls.brake = false;
return;
}
}
else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
{
if(!(lworld->getDistanceToCenterForKart( getWorldKartId() ) < RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] *
CURVE_INSIDE_PERC || m_curve_angle < -getMaxSteerAngle()))
{
m_controls.brake = false;
return;
}
}
//Brake if the kart's speed is bigger than the speed we need
//to go through the curve at the widest angle, or if the kart
//is not going straight in relation to the road.
float angle_adjust = RaceManager::getTrack()->getAIAngleAdjustment();
float speed_adjust = RaceManager::getTrack()->getAICurveSpeedAdjustment();
if(getVelocityLC().getY() > speed_adjust*m_curve_target_speed ||
kart_ang_diff > angle_adjust*MIN_TRACK_ANGLE )
{
#ifdef AI_DEBUG
std::cout << "BRAKING" << std::endl;
#endif
m_controls.brake = true;
return;
}
}
m_controls.brake = false;
}
//-----------------------------------------------------------------------------
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)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(lworld->getDistanceToCenterForKart( getWorldKartId() )) + 0.5 >
RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] )
{
steer_angle = steer_to_point( RaceManager::getTrack()->
m_driveline[NEXT_SECTOR] );
#ifdef AI_DEBUG
std::cout << "- Outside of road: steer to center point." <<
std::endl;
#endif
}
//If we are going to crash against a kart, avoid it if it doesn't
//drives the kart out of the road
else if( m_crashes.m_kart != -1 && !m_crashes.m_road )
{
//-1 = left, 1 = right, 0 = no crash.
if( m_start_kart_crash_direction == 1 )
{
steer_angle = steer_to_angle( NEXT_SECTOR, -90.0f );
m_start_kart_crash_direction = 0;
}
else if(m_start_kart_crash_direction == -1)
{
steer_angle = steer_to_angle( NEXT_SECTOR, 90.0f );
m_start_kart_crash_direction = 0;
}
else
{
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;
}
else
{
steer_angle = steer_to_angle( NEXT_SECTOR, 90.0f );
m_start_kart_crash_direction = -1;
}
}
#ifdef AI_DEBUG
std::cout << "- Velocity vector crashes with kart and doesn't " <<
"crashes with road : steer 90 degrees away from kart." <<
std::endl;
#endif
}
else
{
switch( m_fallback_tactic )
{
case FT_FAREST_POINT:
{
sgVec2 straight_point;
find_non_crashing_point( straight_point );
steer_angle = steer_to_point( straight_point );
}
break;
case FT_PARALLEL:
steer_angle = steer_to_angle( NEXT_SECTOR, 0.0f );
break;
case FT_AVOID_TRACK_CRASH:
if( m_crashes.m_road )
{
steer_angle = steer_to_angle( kart_info.m_track_sector, 0.0f );
}
else steer_angle = 0.0f;
break;
}
#ifdef AI_DEBUG
std::cout << "- Fallback." << std::endl;
#endif
}
// avoid steer vibrations
if (fabsf(steer_angle) < 2.0f)
steer_angle = 0.f;
m_controls.lr = angle_to_control( steer_angle );
}
//-----------------------------------------------------------------------------
void DefaultRobot::handle_items( const float DELTA, const int STEPS )
{
m_controls.fire = false;
if(isRescue() )
{
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 )
{
switch( m_item_tactic )
{
case IT_TEN_SECONDS:
if( m_time_since_last_shot > 10.0f )
{
m_controls.fire = true;
m_time_since_last_shot = 0.0f;
}
break;
case IT_CALCULATE:
switch( m_collectable.getType() )
{
case COLLECT_ZIPPER:
{
const float ANGLE_DIFF = fabsf( normalize_angle(
RaceManager::getTrack()->m_angle[kart_info.m_track_sector]-
RAD_TO_DEGREE(getHPR().getHeading()) ) );
if( m_time_since_last_shot > 10.0f && ANGLE_DIFF <
15.0f && !m_crashes.m_road && STEPS > 8 )
{
m_controls.fire = true;
m_time_since_last_shot = 0.0f;
}
}
break;
case COLLECT_MISSILE:
case COLLECT_HOMING:
if( m_time_since_last_shot > 5.0f && m_crashes.m_kart != -1 )
{
if( (getXYZ()-RaceManager::getKart(m_crashes.m_kart)->getXYZ() ).length_2d() >
m_kart_properties->getKartLength() * 2.5f )
{
m_controls.fire = true;
m_time_since_last_shot = 0.0f;
}
}
break;
case COLLECT_BOWLING:
if ( m_time_since_last_shot > 3.0f && m_crashes.m_kart != -1 )
{
m_controls.fire = true;
m_time_since_last_shot = 0.0f;
}
break;
default:
m_controls.fire = true;
m_time_since_last_shot = 0.0f;
return;
}
break;
}
}
return;
} // handle_items
//-----------------------------------------------------------------------------
void DefaultRobot::handle_acceleration( const float DELTA )
{
//Do not accelerate until we have delayed the start enough
if( m_time_till_start > 0.0f )
{
m_time_till_start -= DELTA;
m_controls.accel = 0.0f;
return;
}
if( m_controls.brake == true )
{
m_controls.accel = 0.0f;
return;
}
if( m_wait_for_players )
{
//Find if any player is ahead of this kart
bool player_winning = false;
for(unsigned int i = 0; i < race_manager->getNumPlayers(); ++i )
if( m_race_position > RaceManager::getPlayerKart(i)->getPosition() )
{
player_winning = true;
break;
}
if( player_winning )
{
m_controls.accel = m_max_handicap_accel;
return;
}
}
m_controls.accel = 1.0f;
}
//-----------------------------------------------------------------------------
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();
Vec3 vel_normal(VEL.getX(), VEL.getY(), 0.0);
float len = vel_normal.length();
// Too slow for wheelies, and it avoids normalisation problems.
if(len<getMaxSpeed()*getWheelieMaxSpeedRatio()) return false;
vel_normal/=len;
Vec3 step_coord;
Vec3 step_track_coord;
float distance;
//FIXME: instead of using 1.5, it should find out how much time it
//will pass to stop doing the wheelie completely from the current state.
const float CHECK_DIST = 1.5f * m_wheelie_check_dist;
/* The following method of finding if a position is outside of the track
is less accurate than calling findRoadSector(), but a lot faster.
*/
const int WHEELIE_STEPS = int(( getVelocityLC().getY() * CHECK_DIST )/
m_kart_properties->getKartLength() );
for( int i = WHEELIE_STEPS; i > STEPS - 1; --i )
{
step_coord = getXYZ()+vel_normal* m_kart_properties->getKartLength() * float(i);
RaceManager::getTrack()->spatialToTrack(step_track_coord, step_coord,
m_future_sector );
distance = step_track_coord[0] > 0.0f ? step_track_coord[0]
: -step_track_coord[0];
if( distance > RaceManager::getTrack()->getWidth()[kart_info.m_track_sector] )
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
void DefaultRobot::handle_race_start()
{
//FIXME: make karts able to get a penalty for accelerating too soon
//like players, should happen to about 20% of the karts in easy,
//5% in medium and less than 1% of the karts in hard.
if( m_time_till_start < 0.0f )
{
srand(( unsigned ) time( 0 ));
//Each kart starts at a different, random time, and the time is
//smaller depending on the difficulty.
m_time_till_start = ( float ) rand() / RAND_MAX * m_max_start_delay;
}
}
//-----------------------------------------------------------------------------
void DefaultRobot::handle_rescue(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
m_crash_time += (m_collided && isOnGround()) ? 3.0f * DELTA : -0.25f * DELTA;
if( m_crash_time < 0.0f ) m_crash_time = 0.0f;
//Reaction to being stuck
if( m_crash_time > 3.0f )
{
forceRescue();
m_crash_time = 0.0f;
}
// check if kart is stuck
if(getSpeed()<2.0f && !isRescue() && !RaceManager::getWorld()->getClock().isStartPhase())
{
m_time_since_stuck += DELTA;
if(m_time_since_stuck > 2.0f)
{
forceRescue();
m_time_since_stuck=0.0f;
} // m_time_since_stuck > 2.0f
}
else
{
m_time_since_stuck = 0.0f;
}
}
//-----------------------------------------------------------------------------
float DefaultRobot::steer_to_angle (const size_t SECTOR, const float ANGLE)
{
float angle = RaceManager::getTrack()->m_angle[SECTOR];
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - RAD_TO_DEGREE(getHPR().getHeading());
steer_angle += ANGLE;
steer_angle = normalize_angle( steer_angle );
return steer_angle;
}
//-----------------------------------------------------------------------------
float DefaultRobot::steer_to_point( const sgVec2 POINT )
{
const SGfloat ADJACENT_LINE = POINT[0] - getXYZ().getX();
const SGfloat OPPOSITE_LINE = POINT[1] - getXYZ().getY();
SGfloat theta;
//Protection from division by zero
if( ADJACENT_LINE > 0.0000001 || ADJACENT_LINE < -0.0000001 )
{
theta = sgATan( OPPOSITE_LINE / ADJACENT_LINE );
}
else theta = 0;
//The real value depends on the side of the track that the kart is
theta += ADJACENT_LINE < 0.0f ? 90.0f : -90.0f;
float steer_angle = theta - RAD_TO_DEGREE(getHPR().getHeading());
steer_angle = normalize_angle( steer_angle );
return steer_angle;
}
//-----------------------------------------------------------------------------
void DefaultRobot::check_crashes( const int STEPS, const Vec3& pos )
{
//Right now there are 2 kind of 'crashes': with other karts and another
//with the track. The sight line is used to find if the karts crash with
//each other, but the first step is twice as big as other steps to avoid
//having karts too close in any direction. The crash with the track can
//tell when a kart is going to get out of the track so it steers.
Vec3 vel_normal;
//in this func we use it as a 2d-vector, but later on it is passed
//to RaceManager::getTrack()->findRoadSector, there it is used as a 3d-vector
//to find distance to plane, so z must be initialized to zero
Vec3 step_coord;
SGfloat kart_distance;
step_coord.setZ(0.0);
m_crashes.clear();
const size_t NUM_KARTS = race_manager->getNumKarts();
//Protection against having vel_normal with nan values
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);
}
for(int i = 1; STEPS > i; ++i)
{
step_coord = pos + vel_normal* m_kart_properties->getKartLength() * float(i);
/* Find if we crash with any kart, as long as we haven't found one
* yet
*/
if( m_crashes.m_kart == -1 )
{
for( unsigned int j = 0; j < NUM_KARTS; ++j )
{
const Kart* kart = RaceManager::getKart(j);
if(kart==this||kart->isEliminated()) continue; // ignore eliminated karts
kart_distance = (step_coord - RaceManager::getKart(j)->getXYZ()).length_2d();
if( kart_distance < m_kart_properties->getKartLength() + 0.125f * i )
if( getVelocityLC().getY() > RaceManager::getKart(j)->
getVelocityLC().getY() * 0.75f ) m_crashes.m_kart = j;
}
}
/*Find if we crash with the drivelines*/
RaceManager::getTrack()->findRoadSector(step_coord, &m_sector);
#ifdef SHOW_FUTURE_PATH
ssgaSphere *sphere = new ssgaSphere;
#ifdef ERASE_PATH
static ssgaSphere *last_sphere = 0;
if( last_sphere ) scene->remove( last_sphere );
last_sphere = sphere;
#endif
sgVec3 center;
center[0] = step_coord[0];
center[1] = step_coord[1];
center[2] = pos[2];
sphere->setCenter( center );
sphere->setSize( m_kart_properties->getKartLength() );
if( m_sector == Track::UNKNOWN_SECTOR )
{
sgVec4 colour;
colour[0] = colour[3] = 255;
colour[1] = colour[2] = 0;
sphere->setColour(colour);
}
else if( i == 1 )
{
sgVec4 colour;
colour[0] = colour[1] = colour[2] = 0;
colour[3] = 255;
sphere->setColour( colour );
}
scene->add( sphere );
#endif
m_future_location[0] = step_coord[0]; m_future_location[1] =
step_coord[1];
if( m_sector == Track::UNKNOWN_SECTOR )
{
m_future_sector = RaceManager::getTrack()->findOutOfRoadSector( step_coord,
Track::RS_DONT_KNOW, m_future_sector );
m_crashes.m_road = true;
break;
}
else
{
m_future_sector = m_sector;
}
}
}
//-----------------------------------------------------------------------------
/** Find the sector that at the longest distance from the kart, that can be
* driven to without crashing with the track, then find towards which of
* the two edges of the track is closest to the next curve after wards,
* and return the position of that edge.
*/
void DefaultRobot::find_non_crashing_point( sgVec2 result )
{
const unsigned int DRIVELINE_SIZE = (unsigned int)RaceManager::getTrack()->m_driveline.size();
// 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;
Vec3 step_track_coord;
SGfloat distance;
int steps;
//We exit from the function when we have found a solution
while( 1 )
{
//target_sector is the sector at the longest distance that we can drive
//to without crashing with the track.
target_sector = sector + 1 < DRIVELINE_SIZE ? sector + 1 : 0;
//direction is a vector from our kart to the sectors we are testing
direction = RaceManager::getTrack()->m_driveline[target_sector] - getXYZ();
float len=direction.length_2d();
steps = int( len / m_kart_properties->getKartLength() );
if( steps < 3 ) steps = 3;
//Protection against having vel_normal with nan values
if(len>0.0f) {
direction*= 1.0f/len;
}
Vec3 step_coord;
//Test if we crash if we drive towards the target sector
for( int i = 2; i < steps; ++i )
{
step_coord = getXYZ()+direction*m_kart_properties->getKartLength() * float(i);
RaceManager::getTrack()->spatialToTrack( step_track_coord, step_coord,
sector );
distance = step_track_coord[0] > 0.0f ? step_track_coord[0]
: -step_track_coord[0];
//If we are outside, the previous sector is what we are looking for
if ( distance + m_kart_properties->getKartLength() * 0.5f > RaceManager::getTrack()->getWidth()[sector] )
{
sgCopyVec2( result, RaceManager::getTrack()->m_driveline[sector] );
#ifdef SHOW_NON_CRASHING_POINT
ssgaSphere *sphere = new ssgaSphere;
static ssgaSphere *last_sphere = 0;
if(last_sphere) scene->remove( last_sphere );
last_sphere = sphere;
sgVec3 center;
center[0] = result[0];
center[1] = result[1];
center[2] = m_curr_pos.xyz[2];
sphere->setCenter( center );
sphere->setSize( 0.5f );
sgVec4 colour;
colour[1] = colour[3] = 255;
colour[0] = colour[2] = 0;
sphere->setColour( colour );
scene->add( sphere );
#endif
return;
}
}
sector = target_sector;
}
}
//-----------------------------------------------------------------------------
void DefaultRobot::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
m_sector = Track::UNKNOWN_SECTOR;
m_inner_curve = 0;
m_curve_target_speed = getMaxSpeed();
m_curve_angle = 0.0;
m_future_location[0] = 0.0;
m_future_location[1] = 0.0;
m_future_sector = 0;
m_time_till_start = -1.0f;
m_crash_time = 0.0f;
m_collided = false;
m_time_since_stuck = 0.0f;
AutoKart::reset();
} // reset
//-----------------------------------------------------------------------------
inline float DefaultRobot::normalize_angle( float angle )
{
while( angle > 360.0 ) angle -= 360;
while( angle < -360.0 ) angle += 360;
if( angle > 180.0 ) angle -= 360.0;
else if( angle < -180.0 ) angle += 360.0;
return angle;
}
//-----------------------------------------------------------------------------
/** calc_steps() divides the velocity vector by the lenght of the kart,
* and gets the number of steps to use for the sight line of the kart.
* The calling sequence guarantees that m_future_sector is not UNKNOWN.
*/
int DefaultRobot::calc_steps()
{
int steps = int( getVelocityLC().getY() / m_kart_properties->getKartLength() );
if( steps < m_min_steps ) steps = m_min_steps;
//Increase the steps depending on the width, if we steering hard,
//mostly for curves.
if( fabsf(m_controls.lr) > 0.95 )
{
const int WIDTH_STEPS =
(int)( RaceManager::getTrack()->getWidth()[m_future_sector]
/( m_kart_properties->getKartLength() * 2.0 ) );
steps += WIDTH_STEPS;
}
return steps;
}
//-----------------------------------------------------------------------------
/** 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.
*/
float DefaultRobot::angle_to_control( float angle ) const
{
angle *= 180.0f / ( getMaxSteerAngle() * M_PI ) ;
if(angle > 1.0f) return 1.0f;
else if(angle < -1.0f) return -1.0f;
return angle;
}
//-----------------------------------------------------------------------------
/** Finds the approximate radius of a track's curve. It needs two arguments,
* the number of the drivepoint that marks the beginning of the curve, and
* the number of the drivepoint that marks the ending of the curve.
*
* Based on that you can construct any circle out of 3 points in it, we use
* the two arguments to use the drivelines as the first and last point; the
* middle sector is averaged.
*/
float DefaultRobot::get_approx_radius(const int START, const int END) const
{
const int MIDDLE = (START + END) / 2;
//If the START and END sectors are very close, their average will be one
//of them, and using twice the same point just generates a huge radius
//(too big to be of any use) but it also can generate a division by zero,
//so here is some special handling for that case.
if (MIDDLE == START || MIDDLE == END ) return 99999.0f;
float X1, Y1, X2, Y2, X3, Y3;
//The next line is just to avoid compiler warnings.
X1 = X2 = X3 = Y1 = Y2 = Y3 = 0.0;
if(m_inner_curve == -1)
{
X1 = RaceManager::getTrack()->m_left_driveline[START][0];
Y1 = RaceManager::getTrack()->m_left_driveline[START][1];
X2 = RaceManager::getTrack()->m_left_driveline[MIDDLE][0];
Y2 = RaceManager::getTrack()->m_left_driveline[MIDDLE][1];
X3 = RaceManager::getTrack()->m_left_driveline[END][0];
Y3 = RaceManager::getTrack()->m_left_driveline[END][1];
}
else if (m_inner_curve == 0)
{
X1 = RaceManager::getTrack()->m_driveline[START][0];
Y1 = RaceManager::getTrack()->m_driveline[START][1];
X2 = RaceManager::getTrack()->m_driveline[MIDDLE][0];
Y2 = RaceManager::getTrack()->m_driveline[MIDDLE][1];
X3 = RaceManager::getTrack()->m_driveline[END][0];
Y3 = RaceManager::getTrack()->m_driveline[END][1];
}
else if (m_inner_curve == 1)
{
X1 = RaceManager::getTrack()->m_right_driveline[START][0];
Y1 = RaceManager::getTrack()->m_right_driveline[START][1];
X2 = RaceManager::getTrack()->m_right_driveline[MIDDLE][0];
Y2 = RaceManager::getTrack()->m_right_driveline[MIDDLE][1];
X3 = RaceManager::getTrack()->m_right_driveline[END][0];
Y3 = RaceManager::getTrack()->m_right_driveline[END][1];
}
const float A = X2 - X1;
const float B = Y2 - Y1;
const float C = X3 - X1;
const float D = Y3 - Y1;
const float E = A * ( X1 + X2) + B * (Y1 + Y2);
const float F = C * ( X1 + X3) + D * (Y1 + Y3);
const float G = 2.0f * ( A*( Y3-Y2 ) - B*( X3 - X2 ) );
const float pX = ( D*E - B*F) / G;
const float pY = ( A*F - C*E) / G;
const float radius = sqrt( ( X1 - pX) * ( X1 - pX) + (Y1 - pY) * (Y1 - pY) );
return radius;
}
//-----------------------------------------------------------------------------
/**Find_curve() gathers info about the closest sectors ahead: the curve
* angle, the direction of the next turn, and the optimal speed at which the
* curve can be travelled at it's widest angle.
*
* The number of sectors that form the curve is dependant on the kart's speed.
*/
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 = kart_info.m_track_sector;
int i;
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[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(kart_info.m_track_sector, i) * RaceManager::getTrack()->getGravity() * 0.9f);
}