Removed unused new AI controller, and created

new skidding ai which is at this stage a copy
of the default AI controller.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11254 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2012-05-21 06:57:05 +00:00
parent d6e3c5dc13
commit 1398184b35
5 changed files with 430 additions and 268 deletions

View File

@ -101,8 +101,8 @@ src/karts/controller/ai_base_controller.cpp
src/karts/controller/controller.cpp
src/karts/controller/default_ai_controller.cpp
src/karts/controller/end_controller.cpp
src/karts/controller/new_ai_controller.cpp
src/karts/controller/player_controller.cpp
src/karts/controller/skidding_ai.cpp
src/karts/explosion_animation.cpp
src/karts/ghost_kart.cpp
src/karts/kart.cpp
@ -346,8 +346,8 @@ src/karts/controller/controller.hpp
src/karts/controller/default_ai_controller.hpp
src/karts/controller/end_controller.hpp
src/karts/controller/kart_control.hpp
src/karts/controller/new_ai_controller.hpp
src/karts/controller/player_controller.hpp
src/karts/controller/skidding_ai.hpp
src/karts/explosion_animation.hpp
src/karts/ghost_kart.hpp
src/karts/kart.hpp

View File

@ -916,11 +916,11 @@
>
</File>
<File
RelativePath="..\..\karts\controller\new_ai_controller.cpp"
RelativePath="..\..\karts\controller\player_controller.cpp"
>
</File>
<File
RelativePath="..\..\karts\controller\player_controller.cpp"
RelativePath="..\..\karts\controller\skidding_ai.cpp"
>
</File>
</Filter>
@ -2102,11 +2102,11 @@
>
</File>
<File
RelativePath="..\..\karts\controller\new_ai_controller.hpp"
RelativePath="..\..\karts\controller\player_controller.hpp"
>
</File>
<File
RelativePath="..\..\karts\controller\player_controller.hpp"
RelativePath="..\..\karts\controller\skidding_ai.hpp"
>
</File>
</Filter>

View File

@ -1,8 +1,8 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2010 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2010 Eduardo Hernandez Munoz
// Copyright (C) 2008-2010 Joerg Henrichs
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2008-2012 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@ -21,9 +21,9 @@
//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.
#define AI_DEBUG
#undef AI_DEBUG
#include "karts/controller/new_ai_controller.hpp"
#include "karts/controller/skidding_ai.hpp"
#ifdef AI_DEBUG
# include "irrlicht.h"
@ -38,13 +38,14 @@
#ifdef AI_DEBUG
# include "graphics/irr_driver.hpp"
#endif
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "graphics/slip_stream.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp"
#include "karts/rescue_animation.hpp"
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "modes/linear_world.hpp"
#include "network/network_manager.hpp"
#include "race/race_manager.hpp"
@ -52,27 +53,33 @@
#include "tracks/track.hpp"
#include "utils/constants.hpp"
NewAIController::NewAIController(AbstractKart *kart) : AIBaseController(kart)
SkiddingAI::SkiddingAI(AbstractKart *kart)
: AIBaseController(kart)
{
// Reset must be called after QuadGraph::get() etc. is set up
reset();
switch( race_manager->getDifficulty())
{
case RaceManager::RD_EASY:
m_wait_for_players = true;
m_max_handicap_accel = 0.9f;
m_make_use_of_slipstream = false;
m_max_handicap_speed = 0.9f;
m_item_tactic = IT_TEN_SECONDS;
m_false_start_probability = 0.08f;
m_min_start_delay = 0.3f;
m_max_start_delay = 0.5f;
m_min_steps = 0;
m_min_steps = 1;
m_nitro_level = NITRO_NONE;
m_handle_bomb = false;
setSkiddingFraction(4.0f);
break;
case RaceManager::RD_MEDIUM:
m_wait_for_players = true;
m_max_handicap_accel = 0.95f;
m_make_use_of_slipstream = false;
m_max_handicap_speed = 0.95f;
m_item_tactic = IT_CALCULATE;
m_false_start_probability = 0.04f;
m_min_start_delay = 0.25f;
m_max_start_delay = 0.4f;
m_min_steps = 1;
m_nitro_level = NITRO_SOME;
@ -81,9 +88,15 @@ NewAIController::NewAIController(AbstractKart *kart) : AIBaseController(kart)
break;
case RaceManager::RD_HARD:
m_wait_for_players = false;
m_max_handicap_accel = 1.0f;
m_make_use_of_slipstream = true;
m_max_handicap_speed = 1.0f;
m_item_tactic = IT_CALCULATE;
m_max_start_delay = 0.1f;
m_false_start_probability = 0.01f;
// See http://www.humanbenchmark.com/tests/reactiontime/stats.php
// Average reaction time is around 0.215 s, so using .15 as minimum
// gives an AI average slightly above the human average
m_min_start_delay = 0.15f;
m_max_start_delay = 0.28f;
m_min_steps = 2;
m_nitro_level = NITRO_ALL;
m_handle_bomb = true;
@ -93,30 +106,63 @@ NewAIController::NewAIController(AbstractKart *kart) : AIBaseController(kart)
#ifdef AI_DEBUG
m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1);
m_debug_left = irr_driver->getSceneManager()->addSphereSceneNode(1);
m_debug_right = irr_driver->getSceneManager()->addSphereSceneNode(1);
#endif
} // NewAIController
} // SkiddingAI
//-----------------------------------------------------------------------------
/** The destructor deletes the shared TrackInfo objects if no more NewAIController
/** The destructor deletes the shared TrackInfo objects if no more SkiddingAI
* instances are around.
*/
NewAIController::~NewAIController()
SkiddingAI::~SkiddingAI()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
irr_driver->removeNode(m_debug_left );
irr_driver->removeNode(m_debug_right );
#endif
} // ~NewAIController
} // ~SkiddingAI
//-----------------------------------------------------------------------------
void SkiddingAI::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
m_curve_target_speed = m_kart->getCurrentMaxSpeed();
m_curve_angle = 0.0;
m_start_delay = -1.0f;
m_time_since_stuck = 0.0f;
m_kart_ahead = NULL;
m_distance_ahead = 0.0f;
m_kart_behind = NULL;
m_distance_behind = 0.0f;
AIBaseController::reset();
m_track_node = QuadGraph::UNKNOWN_SECTOR;
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
fprintf(stderr,
"Invalid starting position for '%s' - not on track"
" - can be ignored.\n",
m_kart->getIdent().c_str());
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
AIBaseController::reset();
} // reset
//-----------------------------------------------------------------------------
const irr::core::stringw& SkiddingAI::getNamePostfix() const
{
// Static to avoid returning the address of a temporary stringq
static irr::core::stringw name="(default)";
return name;
} // getNamePostfix
//-----------------------------------------------------------------------------
/** Returns the pre-computed successor of a graph node.
* \parameter index The index of the graph node for which the successor
* is searched.
*/
unsigned int NewAIController::getNextSector(unsigned int index)
unsigned int SkiddingAI::getNextSector(unsigned int index)
{
return m_successor_index[index];
} // getNextSector
@ -124,20 +170,44 @@ unsigned int NewAIController::getNextSector(unsigned int index)
//-----------------------------------------------------------------------------
//TODO: if the AI is crashing constantly, make it move backwards in a straight
//line, then move forward while turning.
void NewAIController::update(float dt)
void SkiddingAI::update(float dt)
{
AIBaseController::update(dt);
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
// Don't do anything if there is currently a kart animations shown.
if(m_kart->getKartAnimation())
return;
// Having a non-moving AI can be useful for debugging, e.g. aiming
// or slipstreaming.
#undef AI_DOES_NOT_MOVE_FOR_DEBUGGING
#ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING
m_controls->m_accel = 0;
m_controls->m_steer = 0;
return;
#endif
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
AIBaseController::update(dt);
return;
}
// If the kart needs to be rescued, do it now (and nothing else)
if(isStuck() && !m_kart->getKartAnimation())
{
new RescueAnimation(m_kart);
AIBaseController::update(dt);
return;
}
if( m_world->isStartPhase() )
{
handleRaceStart();
AIBaseController::update(dt);
return;
}
@ -185,7 +255,7 @@ void NewAIController::update(float dt)
/*Response handling functions*/
handleAcceleration(dt);
handleSteering(dt);
handleItems(dt, steps);
handleItems(dt);
handleRescue(dt);
handleBraking();
// If a bomb is attached, nitro might already be set.
@ -209,10 +279,13 @@ void NewAIController::update(float dt)
m_controls->m_fire = true;
}
}
/*And obviously general kart stuff*/
AIBaseController::update(dt);
} // update
//-----------------------------------------------------------------------------
void NewAIController::handleBraking()
void SkiddingAI::handleBraking()
{
// 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)
@ -238,7 +311,10 @@ void NewAIController::handleBraking()
kart_ang_diff = normalizeAngle(kart_ang_diff);
kart_ang_diff = fabsf(kart_ang_diff);
const float MIN_TRACK_ANGLE = DEGREE_TO_RAD*190.0f;
// FIXME: The original min_track_angle value of 20 degrees
// resulted in way too much braking. Is this test
// actually necessary at all???
const float MIN_TRACK_ANGLE = DEGREE_TO_RAD*60.0f;
const float CURVE_INSIDE_PERC = 0.25f;
//Brake only if the road does not goes somewhat straight.
@ -250,7 +326,8 @@ void NewAIController::handleBraking()
//out of the curve.
if(!(m_world->getDistanceToCenterForKart(m_kart->getWorldKartId())
> QuadGraph::get()->getNode(m_track_node).getPathWidth() *
-CURVE_INSIDE_PERC || m_curve_angle > RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
-CURVE_INSIDE_PERC ||
m_curve_angle > RAD_TO_DEGREE*m_kart->getMaxSteerAngle()) )
{
m_controls->m_brake = false;
return;
@ -260,7 +337,8 @@ void NewAIController::handleBraking()
{
if(!(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )
< QuadGraph::get()->getNode(m_track_node).getPathWidth() *
CURVE_INSIDE_PERC || m_curve_angle < -RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
CURVE_INSIDE_PERC ||
m_curve_angle < -RAD_TO_DEGREE*m_kart->getMaxSteerAngle()))
{
m_controls->m_brake = false;
return;
@ -286,7 +364,7 @@ void NewAIController::handleBraking()
} // handleBraking
//-----------------------------------------------------------------------------
void NewAIController::handleSteering(float dt)
void SkiddingAI::handleSteering(float dt)
{
const int next = m_next_node_index[m_track_node];
@ -296,14 +374,31 @@ void NewAIController::handleSteering(float dt)
*finite state machine.
*/
//Reaction to being outside of the road
if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) >
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+1.0f )
float side_dist =
m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() );
if( fabsf(side_dist) >
0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f )
{
steer_angle = steerToPoint(m_last_target_point);
// If the speed is negative, the kart is most likely being pushed
// away from a collision with the terrain, and this most likely means
// that the kart is off track. In this case, steer so that the kart
// will rotate towards the center of the track. E.g. if the kart is
// to the right, steer towards the right.
#ifdef XX
if(m_kart->getSpeed()<0)
{
steer_angle = side_dist > 0 ? -m_kart->getMaxSteerAngle()
: m_kart->getMaxSteerAngle();
}
else
#endif
steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next)
.getCenter());
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_last_target_point.toIrrVector());
std::cout << "- Outside of road: steer to last aimed at point." <<
m_debug_sphere->setPosition(QuadGraph::get()->getQuadOfNode(next)
.getCenter().toIrrVector());
std::cout << "- Outside of road: steer to center point." <<
std::endl;
#endif
}
@ -348,21 +443,30 @@ void NewAIController::handleSteering(float dt)
{
m_start_kart_crash_direction = 0;
Vec3 straight_point;
steer_angle = findNonCrashingAngle();
findNonCrashingPoint(&straight_point);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(straight_point.toIrrVector());
#endif
steer_angle = steerToPoint(straight_point);
}
setSteering(steer_angle, dt);
} // handleSteering
//-----------------------------------------------------------------------------
void NewAIController::handleItems( const float DELTA, const int STEPS )
/** Handle all items depending on the chosen strategy: Either (low level AI)
* just use an item after 10 seconds, or do a much better job on higher level
* AI - e.g. aiming at karts ahead/behind, wait an appropriate time before
* using multiple items etc.
*/
void SkiddingAI::handleItems(const float dt)
{
m_controls->m_fire = false;
if(m_kart->getKartAnimation() ||
m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING )
return;
m_time_since_last_shot += DELTA;
m_time_since_last_shot += dt;
// Tactic 1: wait ten seconds, then use item
// -----------------------------------------
@ -380,12 +484,10 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
// -------------------
switch( m_kart->getPowerup()->getType() )
{
case PowerupManager::POWERUP_ZIPPER:
// Do nothing. Further up a zipper is used if nitro should be selected,
// saving the (potential more valuable nitro) for later
break;
case PowerupManager::POWERUP_BUBBLEGUM:
// Avoid dropping all bubble gums one after another
if( m_time_since_last_shot <3.0f) break;
// Either use the bubble gum after 10 seconds, or if the next kart
// behind is 'close' but not too close (too close likely means that the
// kart is not behind but more to the side of this kart and so won't
@ -394,21 +496,36 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
// this approach helps preventing an overtaken kart to overtake us
// again.
m_controls->m_fire = (m_distance_behind < 15.0f &&
m_distance_behind > 3.0f ) ||
m_time_since_last_shot>10.0f;
if(m_distance_behind < 10.0f && m_distance_behind > 2.0f )
m_distance_behind *= 1.0f;
break;
m_distance_behind > 3.0f );
break; // POWERUP_BUBBLEGUM
// All the thrown/fired items might be improved by considering the angle
// towards m_kart_ahead. And some of them can fire backwards, too - which
// isn't yet supported for AI karts.
// towards m_kart_ahead.
case PowerupManager::POWERUP_CAKE:
m_controls->m_fire = (m_kart_ahead && m_distance_ahead < 20.0f) ||
m_time_since_last_shot > 10.0f;
{
// Leave some time between shots
if(m_time_since_last_shot<3.0f) break;
// Since cakes can be fired all around, just use a sane distance
// with a bit of extra for backwards, as enemy will go towards cake
bool fire_backwards = (m_kart_behind && m_kart_ahead &&
m_distance_behind < m_distance_ahead) ||
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = (fire_backwards && distance < 25.0f) ||
(!fire_backwards && distance < 20.0f);
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
} // POWERUP_CAKE
case PowerupManager::POWERUP_BOWLING:
{
// Bowling balls slower, so only fire on closer karts - but when
// Leave more time between bowling balls, since they are
// slower, so it should take longer to hit something which
// can result in changing our target.
if(m_time_since_last_shot < 5.0f) break;
// Bowling balls are slower, so only fire on closer karts - but when
// firing backwards, the kart can be further away, since the ball
// acts a bit like a mine (and the kart is racing towards it, too)
bool fire_backwards = (m_kart_behind && m_kart_ahead &&
@ -416,15 +533,25 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ||
m_time_since_last_shot > 10.0f;
m_controls->m_fire = ( (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ) &&
m_time_since_last_shot > 3.0f;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
} // POWERUP_BOWLING
case PowerupManager::POWERUP_ZIPPER:
// Do nothing. Further up a zipper is used if nitro should be selected,
// saving the (potential more valuable nitro) for later
break; // POWERUP_ZIPPER
case PowerupManager::POWERUP_PLUNGER:
{
// Leave more time after a plunger, since it will take some
// time before a plunger effect becomes obvious.
if(m_time_since_last_shot < 5.0f) break;
// Plungers can be fired backwards and are faster,
// so allow more distance for shooting.
bool fire_backwards = (m_kart_behind && m_kart_ahead &&
@ -437,30 +564,68 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
break;
}
} // POWERUP_PLUNGER
case PowerupManager::POWERUP_SWITCH:
// For now don't use a switch if this kart is first (since it's more
// likely that this kart then gets a good iteam), otherwise use it
// after a waiting an appropriate time
if(m_kart->getPosition()>1 &&
m_time_since_last_shot > stk_config->m_item_switch_time+2.0f)
m_controls->m_fire = true;
break; // POWERUP_SWITCH
case PowerupManager::POWERUP_PARACHUTE:
// Wait one second more than a previous parachute
if(m_time_since_last_shot > stk_config->m_parachute_time_other+1.0f)
m_controls->m_fire = true;
break; // POWERUP_PARACHUTE
case PowerupManager::POWERUP_ANVIL:
// Wait one second more than a previous anvil
if(m_time_since_last_shot < stk_config->m_anvil_time+1.0f) break;
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_controls->m_fire = m_world->getTime()<1.0f && m_kart->getPosition()>2;
m_controls->m_fire = m_world->getTime()<1.0f &&
m_kart->getPosition()>2;
}
else
{
m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1;
}
break; // POWERUP_ANVIL
case PowerupManager::POWERUP_SWATTER:
{
// Squared distance for which the swatter works
float d2 = m_kart->getKartProperties()->getSwatterDistance2();
// Fire if the closest kart ahead or to the back is not already
// squashed and close enough.
// FIXME: this can be improved on, since more than one kart might
// be hit, and a kart ahead might not be at an angle at
// which the glove can be used.
if( ( m_kart_ahead && !m_kart_ahead->isSquashed() &&
(m_kart_ahead->getXYZ()-m_kart->getXYZ()).length2()<d2 &&
m_kart_ahead->getSpeed() < m_kart->getSpeed() ) ||
( m_kart_behind && !m_kart_behind->isSquashed() &&
(m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()<d2) )
m_controls->m_fire = true;
break;
}
case PowerupManager::POWERUP_RUBBERBALL:
// Perhaps some more sophisticated algorithm might be useful.
// For now: fire if there is a kart ahead (which means that
// this kart is certainly not the first kart)
m_controls->m_fire = m_kart_ahead != NULL;
break;
case PowerupManager::POWERUP_SWATTER: // fallthrough
default:
m_controls->m_fire = true;
printf("Invalid or unhandled powerup '%d' in default AI.\n",
m_kart->getPowerup()->getType());
assert(false);
}
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
return;
} // handleItems
//-----------------------------------------------------------------------------
@ -468,7 +633,7 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
* 'closeness' is for now simply based on the position, i.e. if a kart is
* more than one lap behind or ahead, it is not considered to be closest.
*/
void NewAIController::computeNearestKarts()
void SkiddingAI::computeNearestKarts()
{
bool need_to_check = false;
int my_position = m_kart->getPosition();
@ -488,7 +653,7 @@ void NewAIController::computeNearestKarts()
for(unsigned int i=0; i<m_world->getNumKarts(); i++)
{
AbstractKart *k = m_world->getKart(i);
if(k->isEliminated() || k==m_kart) continue;
if(k->isEliminated() || k->hasFinishedRace() || k==m_kart) continue;
if(k->getPosition()==my_position+1)
{
m_kart_behind = k;
@ -508,12 +673,12 @@ void NewAIController::computeNearestKarts()
} // computeNearestKarts
//-----------------------------------------------------------------------------
void NewAIController::handleAcceleration( const float DELTA )
void SkiddingAI::handleAcceleration( const float dt)
{
//Do not accelerate until we have delayed the start enough
if( m_time_till_start > 0.0f )
if( m_start_delay > 0.0f )
{
m_time_till_start -= DELTA;
m_start_delay -= dt;
m_controls->m_accel = 0.0f;
return;
}
@ -533,6 +698,11 @@ void NewAIController::handleAcceleration( const float DELTA )
return;
}
// FIXME: this needs to be rewritten, it doesn't make any sense:
// wait for players triggers the opposite (if a player is ahead
// of this AI, go full speed). Besides, it's going to use full
// speed anyway.
if( m_wait_for_players )
{
//Find if any player is ahead of this kart
@ -546,7 +716,7 @@ void NewAIController::handleAcceleration( const float DELTA )
if( player_winning )
{
m_controls->m_accel = m_max_handicap_accel;
m_controls->m_accel = m_max_handicap_speed;
return;
}
}
@ -555,27 +725,32 @@ void NewAIController::handleAcceleration( const float DELTA )
} // handleAcceleration
//-----------------------------------------------------------------------------
void NewAIController::handleRaceStart()
void SkiddingAI::handleRaceStart()
{
//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 )
if( m_start_delay < 0.0f )
{
// 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;
m_start_delay = m_min_start_delay
+ (float) rand() / RAND_MAX * (m_max_start_delay-m_min_start_delay);
// Now check for a false start. If so, add 1 second penalty time.
if(rand() < RAND_MAX * m_false_start_probability)
{
m_start_delay+=stk_config->m_penalty_time;
return;
}
}
} // handleRaceStart
//-----------------------------------------------------------------------------
void NewAIController::handleRescue(const float DELTA)
void SkiddingAI::handleRescue(const float dt)
{
// check if kart is stuck
if(m_kart->getSpeed()<2.0f && !m_kart->getKartAnimation() &&
!m_world->isStartPhase())
{
m_time_since_stuck += DELTA;
m_time_since_stuck += dt;
if(m_time_since_stuck > 2.0f)
{
new RescueAnimation(m_kart);
@ -591,7 +766,7 @@ void NewAIController::handleRescue(const float DELTA)
//-----------------------------------------------------------------------------
/** Decides wether to use nitro or not.
*/
void NewAIController::handleNitroAndZipper()
void SkiddingAI::handleNitroAndZipper()
{
m_controls->m_nitro = false;
// If we are already very fast, save nitro.
@ -677,7 +852,7 @@ void NewAIController::handleNitroAndZipper()
} // handleNitroAndZipper
//-----------------------------------------------------------------------------
void NewAIController::checkCrashes( const int STEPS, const Vec3& pos )
void SkiddingAI::checkCrashes(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
@ -686,6 +861,20 @@ void NewAIController::checkCrashes( const int STEPS, const Vec3& pos )
//tell when a kart is going to get out of the track so it steers.
m_crashes.clear();
// If slipstream should be handled actively, trigger overtaking the
// kart which gives us slipstream if slipstream is ready
const SlipStream *slip=m_kart->getSlipstream();
if(m_make_use_of_slipstream && slip->isSlipstreamReady() &&
slip->getSlipstreamTarget())
{
//printf("%s overtaking %s\n", m_kart->getIdent().c_str(),
// m_kart->getSlipstreamKart()->getIdent().c_str());
// FIXME: we might define a minimum distance, and if the target kart
// is too close break first - otherwise the AI hits the kart when
// trying to overtake it, actually speeding the other kart up.
m_crashes.m_kart = slip->getSlipstreamTarget()->getWorldKartId();
}
const size_t NUM_KARTS = m_world->getNumKarts();
//Protection against having vel_normal with nan values
@ -700,7 +889,13 @@ void NewAIController::checkCrashes( const int STEPS, const Vec3& pos )
vel_normal/=speed;
int current_node = m_track_node;
for(int i = 1; STEPS > i; ++i)
if(steps<1 || steps>1000)
{
printf("Warning, incorrect STEPS=%d. kart_length %f velocity %f\n",
steps, m_kart_length, m_kart->getVelocityLC().getZ());
steps=1000;
}
for(int i = 1; steps > i; ++i)
{
Vec3 step_coord = pos + vel_normal* m_kart_length * float(i);
@ -741,111 +936,76 @@ void NewAIController::checkCrashes( const int STEPS, const Vec3& pos )
} // checkCrashes
//-----------------------------------------------------------------------------
float NewAIController::findNonCrashingAngle()
/** 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 SkiddingAI::findNonCrashingPoint(Vec3 *result)
{
unsigned int current_sector = m_next_node_index[m_track_node];
const Vec3 &xyz = m_kart->getXYZ();
const Quad &q = QuadGraph::get()->getQuadOfNode(current_sector);
const Vec3 &right = q[2];
const Vec3 &left = q[3];
Vec3 final_right = q[2];
Vec3 final_left = q[3];
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
float sign = 1;
float very_right = -atan2(right.getX()-xyz.getX(),
right.getZ()-xyz.getZ());
float very_left = -atan2(left.getX()-xyz.getX(),
left.getZ()-xyz.getZ());
if(very_left < very_right || very_right>M_PI*0.5f ||
very_left < -M_PI*0.5f)
{
sign = -1;
very_right = -atan2(-right.getX()+xyz.getX(),
-right.getZ()+xyz.getZ());
very_left = -atan2(-left.getX()+xyz.getX(),
-left.getZ()+xyz.getZ());
}
float dist = 0;
Vec3 direction;
Vec3 step_track_coord;
while(dist<40.0f)
// The original while(1) loop is replaced with a for loop to avoid
// infinite loops (which we had once or twice). Usually the number
// of iterations in the while loop is less than 7.
for(unsigned int i=0; i<100; i++)
{
const Quad &q = QuadGraph::get()->getQuadOfNode(current_sector);
const Vec3 &right = q[2];
const Vec3 &left = q[3];
//target_sector is the sector at the longest distance that we can drive
//to without crashing with the track.
target_sector = m_next_node_index[sector];
float angle_right = -atan2(sign*(right.getX()-xyz.getX()),
sign*(right.getZ()-xyz.getZ()));
float angle_left = -atan2(sign*(left.getX()-xyz.getX()),
sign*(left.getZ()-xyz.getZ()));
#ifdef DO_PRINTS
printf("angle %f %f %f %f\n",
very_left, angle_left, angle_right, very_right);
#endif
// Stop if the left and the right beam overlap.
if(angle_left<very_right ||
angle_right>very_left)
{
if(dist<0.1)
break;
break;
//direction is a vector from our kart to the sectors we are testing
direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter()
- m_kart->getXYZ();
float len=direction.length_2d();
unsigned int steps = (unsigned int)( len / m_kart_length );
if( steps < 3 ) steps = 3;
// That shouldn't happen, but since we had one instance of
// STK hanging, add an upper limit here (usually it's at most
// 20 steps)
if( steps>1000) steps = 1000;
//Protection against having vel_normal with nan values
if(len>0.0f) {
direction*= 1.0f/len;
}
if(angle_left <very_left )
Vec3 step_coord;
//Test if we crash if we drive towards the target sector
for(unsigned int i = 2; i < steps; ++i )
{
very_left = angle_left;
final_left = left;
}
if(angle_right>very_right)
{
very_right = angle_right;
final_right = right;
}
dist += QuadGraph::get()->getDistanceToNext(current_sector,
m_successor_index[current_sector]);
current_sector = m_next_node_index[current_sector];
}
m_last_target_point = (final_left+final_right)*0.5f;
float steer_angle = steerToPoint(m_last_target_point);
#ifdef AI_DEBUG
m_debug_left->setPosition(final_left.toIrrVector());
m_debug_right->setPosition(final_right.toIrrVector());
m_debug_sphere->setPosition(m_last_target_point.toIrrVector());
#endif
return steer_angle;
} // findNonCrashingAngle
step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
//-----------------------------------------------------------------------------
void NewAIController::reset()
{
m_time_since_last_shot = 0.0f;
m_start_kart_crash_direction = 0;
m_curve_target_speed = m_kart->getCurrentMaxSpeed();
m_curve_angle = 0.0;
m_time_till_start = -1.0f;
m_crash_time = 0.0f;
m_time_since_stuck = 0.0f;
m_kart_ahead = NULL;
m_distance_ahead = 0.0f;
m_kart_behind = NULL;
m_distance_behind = 0.0f;
m_track_node = QuadGraph::UNKNOWN_SECTOR;
AIBaseController::reset();
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
fprintf(stderr, "Invalid starting position for '%s' - not on track - can be ignored.\n",
m_kart->getIdent().c_str());
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord,
sector );
} // reset
float distance = fabsf(step_track_coord[0]);
//If we are outside, the previous sector is what we are looking for
if ( distance + m_kart_width * 0.5f
> QuadGraph::get()->getNode(sector).getPathWidth() )
{
*result = QuadGraph::get()->getQuadOfNode(sector).getCenter();
return;
}
}
sector = target_sector;
} // for i<100
*result = QuadGraph::get()->getQuadOfNode(sector).getCenter();
} // findNonCrashingPoint
//-----------------------------------------------------------------------------
/** 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 NewAIController::calcSteps()
int SkiddingAI::calcSteps()
{
int steps = int( m_kart->getVelocityLC().getZ() / m_kart_length );
if( steps < m_min_steps ) steps = m_min_steps;
@ -866,7 +1026,9 @@ int NewAIController::calcSteps()
steps += WIDTH_STEPS;
}
#endif
return steps;
// The AI is driving significantly better with more steps, so for now
// add 5 additional steps.
return steps+5;
} // calcSteps
//-----------------------------------------------------------------------------
@ -876,19 +1038,21 @@ int NewAIController::calcSteps()
*
* The number of sectors that form the curve is dependant on the kart's speed.
*/
void NewAIController::findCurve()
void SkiddingAI::findCurve()
{
float total_dist = 0.0f;
int i;
for(i = m_track_node; total_dist < m_kart->getVelocityLC().getZ();
i = m_next_node_index[i])
{
total_dist += QuadGraph::get()->getDistanceToNext(i, m_successor_index[i]);
total_dist += QuadGraph::get()->getDistanceToNext(i,
m_successor_index[i]);
}
m_curve_angle =
normalizeAngle(QuadGraph::get()->getAngleToNext(i, m_successor_index[i])
normalizeAngle(QuadGraph::get()->getAngleToNext(i,
m_successor_index[i])
-QuadGraph::get()->getAngleToNext(m_track_node,
m_successor_index[m_track_node]) );

View File

@ -2,6 +2,7 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2010 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@ -17,17 +18,14 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_NEW_AI_CONTROLLER_HPP
#define HEADER_NEW_AI_CONTROLLER_HPP
#ifndef HEADER_SKIDDING_AI_HPP
#define HEADER_SKIDDING_AI__HPP
#include "karts/controller/ai_base_controller.hpp"
#include "utils/vec3.hpp"
/* third coord won't be used */
class Track;
class LinearWorld;
class QuadGraph;
class Track;
namespace irr
{
@ -40,7 +38,7 @@ namespace irr
/**
* \ingroup controller
*/
class NewAIController : public AIBaseController
class SkiddingAI : public AIBaseController
{
private:
/** How the AI uses nitro. */
@ -64,54 +62,58 @@ private:
} m_crashes;
/*Difficulty handling variables*/
float m_max_start_delay; //Delay before accelerating at the start of each
//race
int m_min_steps; //Minimum number of steps to check. If 0, the AI doesn't
//even has check around the kart, if 1, it checks around
//the kart always, and more than that will check the
//remaining number of steps in front of the kart, always
bool m_wait_for_players; //If true, the acceleration is decreased when
//the AI is in a better position than all the
//human players.
float m_max_handicap_accel; //The allowed maximum speed, in percentage,
//from 0.0 to 1.0. Used only when
//m_wait_for_players == true.
ItemTactic m_item_tactic; //How are items going to be used?
/** Chance of a false start. */
float m_false_start_probability;
/** The minimum delay time before a AI kart starts. */
float m_min_start_delay;
/** The maximum delay time before an AI kart starts. */
float m_max_start_delay;
/** The actual start delay used. */
float m_start_delay;
/** Minimum number of steps to check. If 0, the AI doesn't even has check
* around the kart, if 1, it checks around the kart always, and more
* than that will check the remaining number of steps in front of the
* kart, always. */
int m_min_steps;
/** If true, the acceleration is decreased when the AI is in a better
* position than all the human players. */
bool m_wait_for_players;
/** The allowed maximum speed in percent of the kart's maximum speed. */
float m_max_handicap_speed;
/** How are items going to be used? */
ItemTactic m_item_tactic;
/** True if the kart should try to pass on a bomb to another kart. */
bool m_handle_bomb;
/** True if the AI should avtively try to make use of slipstream. */
bool m_make_use_of_slipstream;
/*General purpose variables*/
//The crash percentage is how much of the time the AI has been crashing,
//if the AI has been crashing for some time, use the rescue.
float m_crash_time;
/** Pointer to the closest kart ahead of this kart. NULL if this
* kart is first. */
AbstractKart *m_kart_ahead;
/** Distance to the kart ahead. */
float m_distance_ahead;
/** Pointer to the closest kart behind this kart. NULL if this kart
* is last. */
AbstractKart *m_kart_behind;
/** Distance to the kard behind. */
float m_distance_behind;
/** Time an item has been collected and not used. */
float m_time_since_last_shot;
float m_time_till_start; //Used to simulate a delay at the start of the
//race, since human players don't accelerate
//at the same time and rarely repeat the a
//previous timing.
float m_curve_target_speed;
float m_curve_angle;
/** The point the kart was aiming at when it was on track last. */
Vec3 m_last_target_point;
float m_time_since_stuck;
int m_start_kart_crash_direction; //-1 = left, 1 = right, 0 = no crash.
@ -119,7 +121,6 @@ private:
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
irr::scene::ISceneNode *m_debug_left, *m_debug_right;
/*Functions called directly from update(). They all represent an action
*that can be done, and end up setting their respective m_controls
@ -127,15 +128,16 @@ private:
*specific action (more like, associated with inaction).
*/
void handleRaceStart();
void handleAcceleration(const float DELTA);
void handleAcceleration(const float dt);
void handleSteering(float dt);
void handleItems(const float DELTA, const int STEPS);
void handleRescue(const float DELTA);
void handleItems(const float dt);
void handleRescue(const float dt);
void handleBraking();
void handleNitroAndZipper();
void computeNearestKarts();
void checkCrashes(const int STEPS, const Vec3& pos);
float findNonCrashingAngle();
void checkCrashes(int steps, const Vec3& pos);
void findNonCrashingPoint(Vec3 *result);
int calcSteps();
void findCurve();
@ -143,15 +145,11 @@ protected:
virtual unsigned int getNextSector(unsigned int index);
public:
NewAIController(AbstractKart *kart);
virtual ~NewAIController();
SkiddingAI(AbstractKart *kart);
~SkiddingAI();
virtual void update (float delta) ;
virtual void reset ();
virtual const irr::core::stringw& getN() const
{
static irr::core::stringw name("(NewAI)");
return name;
} // getName
virtual const irr::core::stringw& getNamePostfix() const;
};
#endif

View File

@ -34,9 +34,9 @@
#include "io/file_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/controller/default_ai_controller.hpp"
#include "karts/controller/new_ai_controller.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/controller/skidding_ai.hpp"
#include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/profile_world.hpp"
@ -251,7 +251,7 @@ Controller* World::loadAIController(AbstractKart *kart)
controller = new DefaultAIController(kart);
break;
case 1:
controller = new NewAIController(kart);
controller = new SkiddingAI(kart);
break;
default:
fprintf(stderr, "Warning: Unknown robot, using default.\n");