Split AIBaseController to AIBaseLapController and AIBaseController.

AIBaseLapController inherits from AIBaseController. 

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/battleAI@14223 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
nixt 2013-10-09 07:26:55 +00:00
parent 5b98a8bcf0
commit d3f92542df
12 changed files with 680 additions and 592 deletions

View File

@ -229,6 +229,7 @@
<ClCompile Include="..\..\karts\abstract_kart.cpp" />
<ClCompile Include="..\..\karts\abstract_kart_animation.cpp" />
<ClCompile Include="..\..\karts\cannon_animation.cpp" />
<ClCompile Include="..\..\karts\controller\ai_base_controller.cpp" />
<ClCompile Include="..\..\karts\controller\ai_properties.cpp" />
<ClCompile Include="..\..\karts\controller\skidding_ai.cpp" />
<ClCompile Include="..\..\karts\explosion_animation.cpp" />
@ -357,7 +358,7 @@
<ClCompile Include="..\..\karts\kart_properties_manager.cpp" />
<ClCompile Include="..\..\karts\max_speed.cpp" />
<ClCompile Include="..\..\karts\moveable.cpp" />
<ClCompile Include="..\..\karts\controller\ai_base_controller.cpp" />
<ClCompile Include="..\..\karts\controller\ai_base_lap_controller.cpp" />
<ClCompile Include="..\..\karts\controller\controller.cpp" />
<ClCompile Include="..\..\karts\controller\end_controller.cpp" />
<ClCompile Include="..\..\karts\controller\player_controller.cpp" />
@ -480,6 +481,7 @@
<ClInclude Include="..\..\karts\abstract_kart.hpp" />
<ClInclude Include="..\..\karts\abstract_kart_animation.hpp" />
<ClInclude Include="..\..\karts\cannon_animation.hpp" />
<ClInclude Include="..\..\karts\controller\ai_base_controller.hpp" />
<ClInclude Include="..\..\karts\controller\ai_properties.hpp" />
<ClInclude Include="..\..\karts\controller\skidding_ai.hpp" />
<ClInclude Include="..\..\karts\explosion_animation.hpp" />
@ -633,7 +635,7 @@
<ClInclude Include="..\..\karts\kart_properties_manager.hpp" />
<ClInclude Include="..\..\karts\max_speed.hpp" />
<ClInclude Include="..\..\karts\moveable.hpp" />
<ClInclude Include="..\..\karts\controller\ai_base_controller.hpp" />
<ClInclude Include="..\..\karts\controller\ai_base_lap_controller.hpp" />
<ClInclude Include="..\..\karts\controller\controller.hpp" />
<ClInclude Include="..\..\karts\controller\end_controller.hpp" />
<ClInclude Include="..\..\karts\controller\player_controller.hpp" />

View File

@ -372,9 +372,6 @@
<ClCompile Include="..\..\karts\moveable.cpp">
<Filter>Source Files\karts</Filter>
</ClCompile>
<ClCompile Include="..\..\karts\controller\ai_base_controller.cpp">
<Filter>Source Files\karts\controller</Filter>
</ClCompile>
<ClCompile Include="..\..\karts\controller\controller.cpp">
<Filter>Source Files\karts\controller</Filter>
</ClCompile>
@ -882,6 +879,12 @@
<ClCompile Include="..\..\tracks\battle_graph.cpp">
<Filter>Source Files\tracks</Filter>
</ClCompile>
<ClCompile Include="..\..\karts\controller\ai_base_lap_controller.cpp">
<Filter>Source Files\karts\controller</Filter>
</ClCompile>
<ClCompile Include="..\..\karts\controller\ai_base_controller.cpp">
<Filter>Source Files\karts\controller</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\main_loop.hpp">
@ -1175,9 +1178,6 @@
<ClInclude Include="..\..\karts\moveable.hpp">
<Filter>Header Files\karts</Filter>
</ClInclude>
<ClInclude Include="..\..\karts\controller\ai_base_controller.hpp">
<Filter>Header Files\karts\controller</Filter>
</ClInclude>
<ClInclude Include="..\..\karts\controller\controller.hpp">
<Filter>Header Files\karts\controller</Filter>
</ClInclude>
@ -1688,5 +1688,11 @@
<ClInclude Include="..\..\tracks\battle_graph.hpp">
<Filter>Header Files\tracks</Filter>
</ClInclude>
<ClInclude Include="..\..\karts\controller\ai_base_lap_controller.hpp">
<Filter>Header Files\karts\controller</Filter>
</ClInclude>
<ClInclude Include="..\..\karts\controller\ai_base_controller.hpp">
<Filter>Header Files\karts\controller</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -23,496 +23,13 @@
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "karts/skidding_properties.hpp"
#include "karts/controller/ai_properties.hpp"
#include "modes/linear_world.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
bool AIBaseController::m_ai_debug = false;
/**
This is the base class for all AIs. At this stage there are two similar
AIs: one is the SkiddingAI, which is the AI used in lap based races
(including follow-the-leader mode), the other one is the end controller,
I.e. the controller that takes over from a player (or AI) when the race is
finished.
This base class defines some basic operations:
- It takes care on which part of the QuadGraph the AI currently is.
- It determines which path the AI should take (in case of shortcuts
or forks in the road).
At race start and every time a new lap is started, the AI will compute the
path the kart is taking this lap (computePath). At this stage the decision
which road in case of shortcut to take is purely random. It stores the
information in two arrays:
m_successor_index[i] stores which successor to take from node i.
The successor is a number between 0 and number_of_successors - 1.
m_next_node_index[i] stores the actual index of the graph node that
follows after node i.
Depending on operation one of the other data is more useful, so this
class stores both information to avoid looking it up over and over.
Once this is done (still in computePath), the array m_all_look_aheads is
computed. This array stores for each quad a list of the next (atm) 10 quads.
This is used when the AI is selecting where to drive next, and it will just
pass the list of next quads to findRoadSector.
Note that the quad graph information is stored for every quad in the quad
graph, even if the quad is not on the path chosen. This is necessary since
it can happen that a kart ends up on a path not choses (e.g. perhaps it was
pushed on that part, or couldn't get a sharp corner).
In update(), which gets called one per frame per AI, this object will
determine the quad the kart is currently on (which is then used to determine
where the kart will be driving to). This uses the m_all_look_aheads to
speed up this process (since the kart is likely to be either on the same
quad as it was before, or the next quad in the m_all_look_aheads list).
It will also check if the kart is stuck:
this is done by maintaining a list of times when the kart hits the track. If
(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered
stuck and will trigger a rescue (due to the pushback from the track it will
take some time if a kart is really stuck before it will hit the track again).
This base class also contains some convenience functions which are useful
in all AIs, e.g.:
- steerToPoint: determine the steering angle to use depending on the
current location and the point the kart is driving to.
- normalizeAngle: To normalise the steering angle to be in [-PI,PI].
- setSteering: Converts the steering angle into a steering fraction
in [-1,1].
*/
AIBaseController::AIBaseController(AbstractKart *kart,
StateManager::ActivePlayer *player)
: Controller(kart, player)
{
m_kart = kart;
m_kart_length = m_kart->getKartLength();
m_kart_width = m_kart->getKartWidth();
m_ai_properties =
m_kart->getKartProperties()->getAIPropertiesForDifficulty();
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
computePath();
}
else
{
// Those variables are not defined in a battle mode (m_world is
// a linear world, since it assumes the existance of drivelines)
m_world = NULL;
m_track = NULL;
m_next_node_index.clear();
m_all_look_aheads.clear();
m_successor_index.clear();
} // if battle mode
// Don't call our own setControllerName, since this will add a
// billboard showing 'aibasecontroller' to the kar.
Controller::setControllerName("AIBaseController");
} // AIBaseController
//-----------------------------------------------------------------------------
void AIBaseController::reset()
{
m_stuck_trigger_rescue = false;
m_collision_times.clear();
} // reset
//-----------------------------------------------------------------------------
/** In debug mode when the user specified --ai-debug on the command line set
* the name of the controller as on-screen text, so that the different AI
* controllers can be distinguished.
* \param name Name of the controller.
*/
void AIBaseController::setControllerName(const std::string &name)
{
#ifdef DEBUG
if(m_ai_debug && !UserConfigParams::m_camera_debug)
m_kart->setOnScreenText(core::stringw(name.c_str()).c_str());
#endif
Controller::setControllerName(name);
} // setControllerName
//-----------------------------------------------------------------------------
/** Triggers a recomputation of the path to use, so that the AI does not
* always use the same way.
*/
void AIBaseController::newLap(int lap)
{
if(lap>0)
{
computePath();
}
} // newLap
//-----------------------------------------------------------------------------
/** Computes a path for the AI to follow. This function is called at race
* start and every time a new lap is started. Recomputing the path every
* time will mean that the kart will not always take the same path, but
* (potentially) vary from lap to lap. At this stage the decision is done
* randomly. The AI could be improved by collecting more information about
* each branch of a track, and selecting the 'appropriate' one (e.g. if the
* AI is far ahead, chose a longer/slower path).
*/
void AIBaseController::computePath()
{
m_next_node_index.resize(QuadGraph::get()->getNumNodes());
m_successor_index.resize(QuadGraph::get()->getNumNodes());
std::vector<unsigned int> next;
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
next.clear();
// Get all successors the AI is allowed to take.
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
// In case of short cuts hidden for the AI it can be that a node
// might not have a successor (since the first and last edge of
// a hidden shortcut is ignored). Since in the case that the AI
// ends up on a short cut (e.g. by accident) and doesn't have an
// allowed way to drive, it should still be able to drive, so add
// the non-AI successors of that node in this case.
if(next.size()==0)
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
// For now pick one part on random, which is not adjusted during the
// race. Long term statistics might be gathered to determine the
// best way, potentially depending on race position etc.
int r = rand();
int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() );
// In case of rounding errors0
if(indx>=(int)next.size()) indx--;
m_successor_index[i] = indx;
assert(indx <(int)next.size() && indx>=0);
m_next_node_index[i] = next[indx];
}
const unsigned int look_ahead=10;
// Now compute for each node in the graph the list of the next 'look_ahead'
// graph nodes. This is the list of node that is tested in checkCrashes.
// If the look_ahead is too big, the AI can skip loops (see
// QuadGraph::findRoadSector for details), if it's too short the AI won't
// find too good a driveline. Note that in general this list should
// be computed recursively, but since the AI for now is using only
// (randomly picked) path this is fine
m_all_look_aheads.resize(QuadGraph::get()->getNumNodes());
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
std::vector<int> l;
int current = i;
for(unsigned int j=0; j<look_ahead; j++)
{
assert(current < (int)m_next_node_index.size());
l.push_back(m_next_node_index[current]);
current = m_next_node_index[current];
} // for j<look_ahead
m_all_look_aheads[i] = l;
}
} // computePath
//-----------------------------------------------------------------------------
/** Updates the ai base controller each time step. Note that any calls to
* isStuck() must be done before update is called, since update will reset
* the isStuck flag!
* \param dt Time step size.
*/
void AIBaseController::update(float dt)
{
m_stuck_trigger_rescue = false;
if(QuadGraph::get())
{
// Update the current node:
int old_node = m_track_node;
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
}
// If we can't find a proper place on the track, to a broader search
// on off-track locations.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
m_next_node_index[m_track_node]==-1)
m_track_node = old_node;
}
} // update
//-----------------------------------------------------------------------------
/** This is called when the kart crashed with the terrain. This subroutine
* tries to detect if the AI is stuck by determining if a certain number
* of collisions happened in a certain amount of time, and if so rescues
* the kart.
* \paran m Pointer to the material that was hit (NULL if no specific
* material was used for the part of the track that was hit).
*/
void AIBaseController::crashed(const Material *m)
{
// Defines how many collision in what time will trigger a rescue.
// Note that typically it takes ~0.5 seconds for the AI to hit
// the track again if it is stuck (i.e. time for the push back plus
// time for the AI to accelerate and hit the terrain again).
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
float time = World::getWorld()->getTime();
if(m_collision_times.size()==0)
{
m_collision_times.push_back(time);
return;
}
// Filter out multiple collisions report caused by single collision
// (bullet can report a collision more than once per frame, and
// resolving it can take a few frames as well, causing more reported
// collisions to happen). The time of 0.2 seconds was experimentally
// found, typically it takes 0.5 seconds for a kart to be pushed back
// from the terrain and accelerate to hit the same terrain again.
if(time - m_collision_times.back() < 0.2f)
return;
// Remove all outdated entries, i.e. entries that are older than the
// collision time plus 1 second. Older entries must be deleted,
// otherwise a collision that happened (say) 10 seconds ago could
// contribute to a stuck condition.
while(m_collision_times.size()>0 &&
time - m_collision_times[0] > 1.0f+COLLISION_TIME)
m_collision_times.erase(m_collision_times.begin());
m_collision_times.push_back(time);
// Now detect if there are enough collision records in the
// specified time interval.
if(time - m_collision_times.front() > COLLISION_TIME
&& m_collision_times.size()>=NUM_COLLISION)
{
// We can't call m_kart->forceRescue here, since crased() is
// called during physics processing, and forceRescue() removes the
// chassis from the physics world, which would then cause
// inconsistencies and potentially a crash during the physics
// processing. So only set a flag, which is tested during update.
m_stuck_trigger_rescue = true;
}
} // crashed(Material)
//-----------------------------------------------------------------------------
/** Returns the next sector of the given sector index. This is used
* for branches in the quad graph to select which way the AI kart should
* go. This is a very simple implementation that always returns the first
* successor, but it can be overridden to allow a better selection.
* \param index Index of the graph node for which the successor is searched.
* \return Returns the successor of this graph node.
*/
unsigned int AIBaseController::getNextSector(unsigned int index)
{
std::vector<unsigned int> successors;
QuadGraph::get()->getSuccessors(index, successors);
return successors[0];
} // getNextSector
//-----------------------------------------------------------------------------
/** This function steers towards a given angle. It also takes a plunger
** attached to this kart into account by modifying the actual steer angle
* somewhat to simulate driving without seeing.
*/
float AIBaseController::steerToAngle(const unsigned int sector,
const float add_angle)
{
float angle = QuadGraph::get()->getAngleToNext(sector,
getNextSector(sector));
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - m_kart->getHeading();
if(m_kart->getBlockedByPlungerTime()>0)
steer_angle += add_angle*0.2f;
else
steer_angle += add_angle;
steer_angle = normalizeAngle( steer_angle );
return steer_angle;
} // steerToAngle
//-----------------------------------------------------------------------------
/** Computes the steering angle to reach a certain point. The function will
* request steering by setting the steering angle to maximum steer angle
* times skidding factor.
* \param point Point to steer towards.
* \param skidding_factor Increase factor for steering when skidding.
* \return Steer angle to use to reach this point.
*/
float AIBaseController::steerToPoint(const Vec3 &point)
{
// First translate and rotate the point the AI is aiming
// at into the kart's local coordinate system.
btQuaternion q(btVector3(0,1,0), -m_kart->getHeading());
Vec3 p = point - m_kart->getXYZ();
Vec3 lc = quatRotate(q, p);
// The point the kart is aiming at can be reached 'incorrectly' if the
// point is below the y=x line: Instead of aiming at that point directly
// the point will be reached on its way 'back' after a more than 90
// degree turn in the circle, i.e.:
// | So the point p (belolw the y=x line) can not be
// | ---\ reached on any circle directly, so it is reached
// | / \ on the indicated way. Since this is not the way
// |/ p we expect a kart to drive (it will result in the
// +-------------- kart doing slaloms, not driving straight), the
// kart will trigger skidding to allow for sharper turns, and hopefully
// the situation will change so that the point p can then be reached
// with a normal turn (it usually works out this way quite easily).
if(fabsf(lc.getX()) > fabsf(lc.getZ()))
{
// Explicitely set the steering angle high enough to that the
// steer function will request skidding. 0.1 is added in case
// of floating point errors.
if(lc.getX()>0)
return m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold+0.1f;
else
return -m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold-0.1f;
}
// Now compute the nexessary radius for the turn. After getting the
// kart local coordinates for the point to aim at, the kart is at
// (0,0) facing straight ahead. The center of the rotation is then
// on the X axis and can be computed by the fact that the distance
// to the kart and to the point to aim at must be the same:
// r*r = (r-x)*(r-x) + y*y
// where r is the radius (= position on the X axis), and x, y are the
// local coordinates of the point to aim at. Solving for r
// results in r = (x*x+y*y)/2x
float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ())
/ (2.0f*lc.getX());
// sin(steern_angle) = wheel_base / radius:
float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius;
// If the wheel base is too long (i.e. the minimum radius is too large
// to actually reach the target), make sure that skidding is used
if(sin_steer_angle <= -1.0f)
return -m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f)
return m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold+0.1f;
float steer_angle = asin(sin_steer_angle);
// After doing the exact computation, we now return an 'oversteered'
// value. This actually helps in making tighter turns, and also in
// very tight turns on narrow roads (where following the circle might
// actually take the kart off track) it forces smaller turns.
// It does not actually hurt to steer too much, since the steering
// will be adjusted every frame.
return steer_angle*2.0f;
} // steerToPoint
//-----------------------------------------------------------------------------
/** Normalises an angle to be between -pi and _ pi.
* \param angle Angle to normalise.
* \return Normalised angle.
*/
float AIBaseController::normalizeAngle(float angle)
{
// Add an assert here since we had cases in which an invalid angle
// was given, resulting in an endless loop (floating point precision,
// e.g.: 1E17 - 2*M_PI = 1E17
assert(angle >= -4*M_PI && angle <= 4*M_PI);
while( angle > 2*M_PI ) angle -= 2*M_PI;
while( angle < -2*M_PI ) angle += 2*M_PI;
if( angle > M_PI ) angle -= 2*M_PI;
else if( angle < -M_PI ) angle += 2*M_PI;
return angle;
} // normalizeAngle
//-----------------------------------------------------------------------------
/** 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. This
* function uses a 'time till full steer' value specifying the time it takes
* for the wheel to reach full left/right steering similar to player karts
* when using a digital input device. The parameter is defined in the kart
* properties and helps somewhat to make AI karts more 'pushable' (since
* otherwise the karts counter-steer to fast).
* It also takes the effect of a plunger into account by restricting the
* actual steer angle to 50% of the maximum.
* \param angle Steering angle.
* \param dt Time step.
*/
void AIBaseController::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
if(!doSkid(steer_fraction))
m_controls->m_skid = KartControl::SC_NONE;
else
m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT
: KartControl::SC_LEFT;
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
if(m_kart->getBlockedByPlungerTime()>0)
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_ai_properties->m_time_full_steer;
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
// ----------------------------------------------------------------------------
/** Determines if the kart should skid. The base implementation enables
* skidding if a sharp turn is needed (which is for the old skidding
* implementation).
* \param steer_fraction The steering fraction as computed by the
* AIBaseController.
* \return True if the kart should skid.
*/
bool AIBaseController::doSkid(float steer_fraction)
{
// Disable skidding when a plunger is in the face
if(m_kart->getBlockedByPlungerTime()>0) return false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkiddingProperties()
->getSkidVisualTime()>0)
return false;
// Otherwise return if we need a sharp turn (which is
// for the old skidding implementation).
return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold;
} // doSkid
// ------------------------------------------------------------------------
/** Certain AI levels will not receive a slipstream bonus in order to
* be not as hard.
*/
bool AIBaseController::disableSlipstreamBonus() const
{
return m_ai_properties->disableSlipstreamUsage();
} // disableSlipstreamBonus
}

View File

@ -24,98 +24,22 @@
class AIProperties;
class LinearWorld;
class QuadGraph;
class BattleGraph;
class Track;
class Vec3;
/** A base class for all AI karts. This class basically provides some
* common low level functions.
* \ingroup controller
*/
class AIBaseController : public Controller
{
private:
/** Stores the last N times when a collision happened. This is used
* to detect when the AI is stuck, i.e. N collisions happened in
* a certain period of time. */
std::vector<float> m_collision_times;
/** A flag that is set during the physics processing to indicate that
* this kart is stuck and needs to be rescued. */
bool m_stuck_trigger_rescue;
protected:
/** Length of the kart, storing it here saves many function calls. */
float m_kart_length;
/** Cache width of kart. */
float m_kart_width;
/** Keep a pointer to the track to reduce calls */
Track *m_track;
/** Keep a pointer to world. */
LinearWorld *m_world;
/** A pointer to the AI properties for this kart. */
const AIProperties *m_ai_properties;
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** Which of the successors of a node was selected by the AI. */
std::vector<int> m_successor_index;
/** For each node in the graph this list contains the chosen next node.
* For normal lap track without branches we always have
* m_next_node_index[i] = (i+1) % size;
* but if a branch is possible, the AI will select one option here.
* If the node is not used, m_next_node_index will be -1. */
std::vector<int> m_next_node_index;
/** For each graph node this list contains a list of the next X
* graph nodes. */
std::vector<std::vector<int> > m_all_look_aheads;
virtual void update (float delta) ;
virtual unsigned int getNextSector(unsigned int index);
virtual void newLap (int lap);
virtual void setControllerName(const std::string &name);
virtual void setSteering (float angle, float dt);
float steerToAngle (const unsigned int sector, const float angle);
float steerToPoint (const Vec3 &point);
float normalizeAngle(float angle);
void computePath();
virtual bool doSkid(float steer_fraction);
// ------------------------------------------------------------------------
/** Nothing special to do when the race is finished. */
virtual void raceFinished() {};
// ------------------------------------------------------------------------
/** This can be called to detect if the kart is stuck (i.e. repeatedly
* hitting part of the track). */
bool isStuck() const { return m_stuck_trigger_rescue; }
static bool m_ai_debug;
public:
AIBaseController(AbstractKart *kart,
AIBaseController(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
virtual ~AIBaseController() {};
virtual void reset();
static void enableDebug() {m_ai_debug = true; }
virtual void crashed(const AbstractKart *k) {};
virtual void crashed(const Material *m);
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isNetworkController() const { return false; }
virtual bool isPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
virtual bool disableSlipstreamBonus() const;
}; // AIBaseController
#endif
/* EOF */
};
#endif

View File

@ -0,0 +1,518 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2009 Eduardo Hernandez Munoz
// Copyright (C) 2009, 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
// 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 "karts/controller/ai_base_lap_controller.hpp"
#include <assert.h>
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "karts/skidding_properties.hpp"
#include "karts/controller/ai_properties.hpp"
#include "modes/linear_world.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
bool AIBaseLapController::m_ai_debug = false;
/**
This is the base class for all AIs. At this stage there are two similar
AIs: one is the SkiddingAI, which is the AI used in lap based races
(including follow-the-leader mode), the other one is the end controller,
I.e. the controller that takes over from a player (or AI) when the race is
finished.
This base class defines some basic operations:
- It takes care on which part of the QuadGraph the AI currently is.
- It determines which path the AI should take (in case of shortcuts
or forks in the road).
At race start and every time a new lap is started, the AI will compute the
path the kart is taking this lap (computePath). At this stage the decision
which road in case of shortcut to take is purely random. It stores the
information in two arrays:
m_successor_index[i] stores which successor to take from node i.
The successor is a number between 0 and number_of_successors - 1.
m_next_node_index[i] stores the actual index of the graph node that
follows after node i.
Depending on operation one of the other data is more useful, so this
class stores both information to avoid looking it up over and over.
Once this is done (still in computePath), the array m_all_look_aheads is
computed. This array stores for each quad a list of the next (atm) 10 quads.
This is used when the AI is selecting where to drive next, and it will just
pass the list of next quads to findRoadSector.
Note that the quad graph information is stored for every quad in the quad
graph, even if the quad is not on the path chosen. This is necessary since
it can happen that a kart ends up on a path not choses (e.g. perhaps it was
pushed on that part, or couldn't get a sharp corner).
In update(), which gets called one per frame per AI, this object will
determine the quad the kart is currently on (which is then used to determine
where the kart will be driving to). This uses the m_all_look_aheads to
speed up this process (since the kart is likely to be either on the same
quad as it was before, or the next quad in the m_all_look_aheads list).
It will also check if the kart is stuck:
this is done by maintaining a list of times when the kart hits the track. If
(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered
stuck and will trigger a rescue (due to the pushback from the track it will
take some time if a kart is really stuck before it will hit the track again).
This base class also contains some convenience functions which are useful
in all AIs, e.g.:
- steerToPoint: determine the steering angle to use depending on the
current location and the point the kart is driving to.
- normalizeAngle: To normalise the steering angle to be in [-PI,PI].
- setSteering: Converts the steering angle into a steering fraction
in [-1,1].
*/
AIBaseLapController::AIBaseLapController(AbstractKart *kart,
StateManager::ActivePlayer *player)
: AIBaseController(kart, player)
{
m_kart = kart;
m_kart_length = m_kart->getKartLength();
m_kart_width = m_kart->getKartWidth();
m_ai_properties =
m_kart->getKartProperties()->getAIPropertiesForDifficulty();
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
computePath();
}
else
{
// Those variables are not defined in a battle mode (m_world is
// a linear world, since it assumes the existance of drivelines)
m_world = NULL;
m_track = NULL;
m_next_node_index.clear();
m_all_look_aheads.clear();
m_successor_index.clear();
} // if battle mode
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseLapController' to the kar.
Controller::setControllerName("AIBaseLapController");
} // AIBaseLapController
//-----------------------------------------------------------------------------
void AIBaseLapController::reset()
{
m_stuck_trigger_rescue = false;
m_collision_times.clear();
} // reset
//-----------------------------------------------------------------------------
/** In debug mode when the user specified --ai-debug on the command line set
* the name of the controller as on-screen text, so that the different AI
* controllers can be distinguished.
* \param name Name of the controller.
*/
void AIBaseLapController::setControllerName(const std::string &name)
{
#ifdef DEBUG
if(m_ai_debug && !UserConfigParams::m_camera_debug)
m_kart->setOnScreenText(core::stringw(name.c_str()).c_str());
#endif
Controller::setControllerName(name);
} // setControllerName
//-----------------------------------------------------------------------------
/** Triggers a recomputation of the path to use, so that the AI does not
* always use the same way.
*/
void AIBaseLapController::newLap(int lap)
{
if(lap>0)
{
computePath();
}
} // newLap
//-----------------------------------------------------------------------------
/** Computes a path for the AI to follow. This function is called at race
* start and every time a new lap is started. Recomputing the path every
* time will mean that the kart will not always take the same path, but
* (potentially) vary from lap to lap. At this stage the decision is done
* randomly. The AI could be improved by collecting more information about
* each branch of a track, and selecting the 'appropriate' one (e.g. if the
* AI is far ahead, chose a longer/slower path).
*/
void AIBaseLapController::computePath()
{
m_next_node_index.resize(QuadGraph::get()->getNumNodes());
m_successor_index.resize(QuadGraph::get()->getNumNodes());
std::vector<unsigned int> next;
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
next.clear();
// Get all successors the AI is allowed to take.
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
// In case of short cuts hidden for the AI it can be that a node
// might not have a successor (since the first and last edge of
// a hidden shortcut is ignored). Since in the case that the AI
// ends up on a short cut (e.g. by accident) and doesn't have an
// allowed way to drive, it should still be able to drive, so add
// the non-AI successors of that node in this case.
if(next.size()==0)
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
// For now pick one part on random, which is not adjusted during the
// race. Long term statistics might be gathered to determine the
// best way, potentially depending on race position etc.
int r = rand();
int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() );
// In case of rounding errors0
if(indx>=(int)next.size()) indx--;
m_successor_index[i] = indx;
assert(indx <(int)next.size() && indx>=0);
m_next_node_index[i] = next[indx];
}
const unsigned int look_ahead=10;
// Now compute for each node in the graph the list of the next 'look_ahead'
// graph nodes. This is the list of node that is tested in checkCrashes.
// If the look_ahead is too big, the AI can skip loops (see
// QuadGraph::findRoadSector for details), if it's too short the AI won't
// find too good a driveline. Note that in general this list should
// be computed recursively, but since the AI for now is using only
// (randomly picked) path this is fine
m_all_look_aheads.resize(QuadGraph::get()->getNumNodes());
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
std::vector<int> l;
int current = i;
for(unsigned int j=0; j<look_ahead; j++)
{
assert(current < (int)m_next_node_index.size());
l.push_back(m_next_node_index[current]);
current = m_next_node_index[current];
} // for j<look_ahead
m_all_look_aheads[i] = l;
}
} // computePath
//-----------------------------------------------------------------------------
/** Updates the ai base controller each time step. Note that any calls to
* isStuck() must be done before update is called, since update will reset
* the isStuck flag!
* \param dt Time step size.
*/
void AIBaseLapController::update(float dt)
{
m_stuck_trigger_rescue = false;
if(QuadGraph::get())
{
// Update the current node:
int old_node = m_track_node;
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
}
// If we can't find a proper place on the track, to a broader search
// on off-track locations.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
m_next_node_index[m_track_node]==-1)
m_track_node = old_node;
}
} // update
//-----------------------------------------------------------------------------
/** This is called when the kart crashed with the terrain. This subroutine
* tries to detect if the AI is stuck by determining if a certain number
* of collisions happened in a certain amount of time, and if so rescues
* the kart.
* \paran m Pointer to the material that was hit (NULL if no specific
* material was used for the part of the track that was hit).
*/
void AIBaseLapController::crashed(const Material *m)
{
// Defines how many collision in what time will trigger a rescue.
// Note that typically it takes ~0.5 seconds for the AI to hit
// the track again if it is stuck (i.e. time for the push back plus
// time for the AI to accelerate and hit the terrain again).
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
float time = World::getWorld()->getTime();
if(m_collision_times.size()==0)
{
m_collision_times.push_back(time);
return;
}
// Filter out multiple collisions report caused by single collision
// (bullet can report a collision more than once per frame, and
// resolving it can take a few frames as well, causing more reported
// collisions to happen). The time of 0.2 seconds was experimentally
// found, typically it takes 0.5 seconds for a kart to be pushed back
// from the terrain and accelerate to hit the same terrain again.
if(time - m_collision_times.back() < 0.2f)
return;
// Remove all outdated entries, i.e. entries that are older than the
// collision time plus 1 second. Older entries must be deleted,
// otherwise a collision that happened (say) 10 seconds ago could
// contribute to a stuck condition.
while(m_collision_times.size()>0 &&
time - m_collision_times[0] > 1.0f+COLLISION_TIME)
m_collision_times.erase(m_collision_times.begin());
m_collision_times.push_back(time);
// Now detect if there are enough collision records in the
// specified time interval.
if(time - m_collision_times.front() > COLLISION_TIME
&& m_collision_times.size()>=NUM_COLLISION)
{
// We can't call m_kart->forceRescue here, since crased() is
// called during physics processing, and forceRescue() removes the
// chassis from the physics world, which would then cause
// inconsistencies and potentially a crash during the physics
// processing. So only set a flag, which is tested during update.
m_stuck_trigger_rescue = true;
}
} // crashed(Material)
//-----------------------------------------------------------------------------
/** Returns the next sector of the given sector index. This is used
* for branches in the quad graph to select which way the AI kart should
* go. This is a very simple implementation that always returns the first
* successor, but it can be overridden to allow a better selection.
* \param index Index of the graph node for which the successor is searched.
* \return Returns the successor of this graph node.
*/
unsigned int AIBaseLapController::getNextSector(unsigned int index)
{
std::vector<unsigned int> successors;
QuadGraph::get()->getSuccessors(index, successors);
return successors[0];
} // getNextSector
//-----------------------------------------------------------------------------
/** This function steers towards a given angle. It also takes a plunger
** attached to this kart into account by modifying the actual steer angle
* somewhat to simulate driving without seeing.
*/
float AIBaseLapController::steerToAngle(const unsigned int sector,
const float add_angle)
{
float angle = QuadGraph::get()->getAngleToNext(sector,
getNextSector(sector));
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - m_kart->getHeading();
if(m_kart->getBlockedByPlungerTime()>0)
steer_angle += add_angle*0.2f;
else
steer_angle += add_angle;
steer_angle = normalizeAngle( steer_angle );
return steer_angle;
} // steerToAngle
//-----------------------------------------------------------------------------
/** Computes the steering angle to reach a certain point. The function will
* request steering by setting the steering angle to maximum steer angle
* times skidding factor.
* \param point Point to steer towards.
* \param skidding_factor Increase factor for steering when skidding.
* \return Steer angle to use to reach this point.
*/
float AIBaseLapController::steerToPoint(const Vec3 &point)
{
// First translate and rotate the point the AI is aiming
// at into the kart's local coordinate system.
btQuaternion q(btVector3(0,1,0), -m_kart->getHeading());
Vec3 p = point - m_kart->getXYZ();
Vec3 lc = quatRotate(q, p);
// The point the kart is aiming at can be reached 'incorrectly' if the
// point is below the y=x line: Instead of aiming at that point directly
// the point will be reached on its way 'back' after a more than 90
// degree turn in the circle, i.e.:
// | So the point p (belolw the y=x line) can not be
// | ---\ reached on any circle directly, so it is reached
// | / \ on the indicated way. Since this is not the way
// |/ p we expect a kart to drive (it will result in the
// +-------------- kart doing slaloms, not driving straight), the
// kart will trigger skidding to allow for sharper turns, and hopefully
// the situation will change so that the point p can then be reached
// with a normal turn (it usually works out this way quite easily).
if(fabsf(lc.getX()) > fabsf(lc.getZ()))
{
// Explicitely set the steering angle high enough to that the
// steer function will request skidding. 0.1 is added in case
// of floating point errors.
if(lc.getX()>0)
return m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold+0.1f;
else
return -m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold-0.1f;
}
// Now compute the nexessary radius for the turn. After getting the
// kart local coordinates for the point to aim at, the kart is at
// (0,0) facing straight ahead. The center of the rotation is then
// on the X axis and can be computed by the fact that the distance
// to the kart and to the point to aim at must be the same:
// r*r = (r-x)*(r-x) + y*y
// where r is the radius (= position on the X axis), and x, y are the
// local coordinates of the point to aim at. Solving for r
// results in r = (x*x+y*y)/2x
float radius = (lc.getX()*lc.getX() + lc.getZ()*lc.getZ())
/ (2.0f*lc.getX());
// sin(steern_angle) = wheel_base / radius:
float sin_steer_angle = m_kart->getKartProperties()->getWheelBase()/radius;
// If the wheel base is too long (i.e. the minimum radius is too large
// to actually reach the target), make sure that skidding is used
if(sin_steer_angle <= -1.0f)
return -m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold-0.1f;
if(sin_steer_angle >= 1.0f)
return m_kart->getMaxSteerAngle()
*m_ai_properties->m_skidding_threshold+0.1f;
float steer_angle = asin(sin_steer_angle);
// After doing the exact computation, we now return an 'oversteered'
// value. This actually helps in making tighter turns, and also in
// very tight turns on narrow roads (where following the circle might
// actually take the kart off track) it forces smaller turns.
// It does not actually hurt to steer too much, since the steering
// will be adjusted every frame.
return steer_angle*2.0f;
} // steerToPoint
//-----------------------------------------------------------------------------
/** Normalises an angle to be between -pi and _ pi.
* \param angle Angle to normalise.
* \return Normalised angle.
*/
float AIBaseLapController::normalizeAngle(float angle)
{
// Add an assert here since we had cases in which an invalid angle
// was given, resulting in an endless loop (floating point precision,
// e.g.: 1E17 - 2*M_PI = 1E17
assert(angle >= -4*M_PI && angle <= 4*M_PI);
while( angle > 2*M_PI ) angle -= 2*M_PI;
while( angle < -2*M_PI ) angle += 2*M_PI;
if( angle > M_PI ) angle -= 2*M_PI;
else if( angle < -M_PI ) angle += 2*M_PI;
return angle;
} // normalizeAngle
//-----------------------------------------------------------------------------
/** 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. This
* function uses a 'time till full steer' value specifying the time it takes
* for the wheel to reach full left/right steering similar to player karts
* when using a digital input device. The parameter is defined in the kart
* properties and helps somewhat to make AI karts more 'pushable' (since
* otherwise the karts counter-steer to fast).
* It also takes the effect of a plunger into account by restricting the
* actual steer angle to 50% of the maximum.
* \param angle Steering angle.
* \param dt Time step.
*/
void AIBaseLapController::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
if(!doSkid(steer_fraction))
m_controls->m_skid = KartControl::SC_NONE;
else
m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT
: KartControl::SC_LEFT;
float old_steer = m_controls->m_steer;
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
if(m_kart->getBlockedByPlungerTime()>0)
{
if (steer_fraction > 0.5f) steer_fraction = 0.5f;
else if(steer_fraction < -0.5f) steer_fraction = -0.5f;
}
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_ai_properties->m_time_full_steer;
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
}
} // setSteering
// ----------------------------------------------------------------------------
/** Determines if the kart should skid. The base implementation enables
* skidding if a sharp turn is needed (which is for the old skidding
* implementation).
* \param steer_fraction The steering fraction as computed by the
* AIBaseLapController.
* \return True if the kart should skid.
*/
bool AIBaseLapController::doSkid(float steer_fraction)
{
// Disable skidding when a plunger is in the face
if(m_kart->getBlockedByPlungerTime()>0) return false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkiddingProperties()
->getSkidVisualTime()>0)
return false;
// Otherwise return if we need a sharp turn (which is
// for the old skidding implementation).
return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold;
} // doSkid
// ------------------------------------------------------------------------
/** Certain AI levels will not receive a slipstream bonus in order to
* be not as hard.
*/
bool AIBaseLapController::disableSlipstreamBonus() const
{
return m_ai_properties->disableSlipstreamUsage();
} // disableSlipstreamBonus

View File

@ -0,0 +1,121 @@
//
// SuperTuxKart - a fun racing game with go-kart
// 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
// 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 HEADER_AI_BASE_LAP_CONTROLLER_HPP
#define HEADER_AI_BASE_LAP_CONTROLLER_HPP
#include "karts/controller/ai_base_controller.hpp"
#include "states_screens/state_manager.hpp"
class AIProperties;
class LinearWorld;
class QuadGraph;
class Track;
class Vec3;
/** A base class for all AI karts. This class basically provides some
* common low level functions.
* \ingroup controller
*/
class AIBaseLapController : public AIBaseController
{
private:
/** Stores the last N times when a collision happened. This is used
* to detect when the AI is stuck, i.e. N collisions happened in
* a certain period of time. */
std::vector<float> m_collision_times;
/** A flag that is set during the physics processing to indicate that
* this kart is stuck and needs to be rescued. */
bool m_stuck_trigger_rescue;
protected:
/** Length of the kart, storing it here saves many function calls. */
float m_kart_length;
/** Cache width of kart. */
float m_kart_width;
/** Keep a pointer to the track to reduce calls */
Track *m_track;
/** Keep a pointer to world. */
LinearWorld *m_world;
/** A pointer to the AI properties for this kart. */
const AIProperties *m_ai_properties;
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** Which of the successors of a node was selected by the AI. */
std::vector<int> m_successor_index;
/** For each node in the graph this list contains the chosen next node.
* For normal lap track without branches we always have
* m_next_node_index[i] = (i+1) % size;
* but if a branch is possible, the AI will select one option here.
* If the node is not used, m_next_node_index will be -1. */
std::vector<int> m_next_node_index;
/** For each graph node this list contains a list of the next X
* graph nodes. */
std::vector<std::vector<int> > m_all_look_aheads;
virtual void update (float delta) ;
virtual unsigned int getNextSector(unsigned int index);
virtual void newLap (int lap);
virtual void setControllerName(const std::string &name);
virtual void setSteering (float angle, float dt);
float steerToAngle (const unsigned int sector, const float angle);
float steerToPoint (const Vec3 &point);
float normalizeAngle(float angle);
void computePath();
virtual bool doSkid(float steer_fraction);
// ------------------------------------------------------------------------
/** Nothing special to do when the race is finished. */
virtual void raceFinished() {};
// ------------------------------------------------------------------------
/** This can be called to detect if the kart is stuck (i.e. repeatedly
* hitting part of the track). */
bool isStuck() const { return m_stuck_trigger_rescue; }
static bool m_ai_debug;
public:
AIBaseLapController(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
virtual ~AIBaseLapController() {};
virtual void reset();
static void enableDebug() {m_ai_debug = true; }
virtual void crashed(const AbstractKart *k) {};
virtual void crashed(const Material *m);
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isNetworkController() const { return false; }
virtual bool isPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
virtual bool disableSlipstreamBonus() const;
}; // AIBaseLapController
#endif
/* EOF */

View File

@ -42,7 +42,7 @@ public:
//LEAK_CHECK();
protected:
// Give them access to the members
friend class AIBaseController;
friend class AIBaseLapController;
friend class SkiddingAI;
/** Used to check that all values are defined in the xml file. */

View File

@ -53,13 +53,13 @@
EndController::EndController(AbstractKart *kart, StateManager::ActivePlayer *player,
Controller *prev_controller)
: AIBaseController(kart, player)
: AIBaseLapController(kart, player)
{
m_previous_controller = prev_controller;
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
// Overwrite the random selected default path from AIBaseController
// Overwrite the random selected default path from AIBaseLapController
// with a path that always picks the first branch (i.e. it follows
// the main driveline).
std::vector<unsigned int> next;
@ -125,7 +125,7 @@ EndController::~EndController()
//-----------------------------------------------------------------------------
void EndController::reset()
{
AIBaseController::reset();
AIBaseLapController::reset();
m_crash_time = 0.0f;
m_time_since_stuck = 0.0f;
@ -180,7 +180,7 @@ void EndController::update(float dt)
m_controls->m_brake = false;
m_controls->m_accel = 1.0f;
AIBaseController::update(dt);
AIBaseLapController::update(dt);
// In case of battle mode: don't do anything
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES ||

View File

@ -21,7 +21,7 @@
#ifndef HEADER_END_CONTROLLER_HPP
#define HEADER_END_CONTROLLER_HPP
#include "karts/controller/ai_base_controller.hpp"
#include "karts/controller/ai_base_lap_controller.hpp"
class Camera;
class LinearWorld;
@ -40,7 +40,7 @@ namespace irr
/**
* \ingroup controller
*/
class EndController : public AIBaseController
class EndController : public AIBaseLapController
{
private:
/** Stores the type of the previous controller. This is necessary so that

View File

@ -78,7 +78,7 @@
#include <iostream>
SkiddingAI::SkiddingAI(AbstractKart *kart)
: AIBaseController(kart)
: AIBaseLapController(kart)
{
reset();
// Determine if this AI has superpowers, which happens e.g.
@ -195,7 +195,7 @@ void SkiddingAI::reset()
m_skid_probability_state = SKID_PROBAB_NOT_YET;
m_last_item_random = NULL;
AIBaseController::reset();
AIBaseLapController::reset();
m_track_node = QuadGraph::UNKNOWN_SECTOR;
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
@ -207,7 +207,7 @@ void SkiddingAI::reset()
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
AIBaseController::reset();
AIBaseLapController::reset();
} // reset
//-----------------------------------------------------------------------------
@ -300,7 +300,7 @@ void SkiddingAI::update(float dt)
// The client does not do any AI computations.
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
AIBaseController::update(dt);
AIBaseLapController::update(dt);
return;
}
@ -308,14 +308,14 @@ void SkiddingAI::update(float dt)
if(isStuck() && !m_kart->getKartAnimation())
{
new RescueAnimation(m_kart);
AIBaseController::update(dt);
AIBaseLapController::update(dt);
return;
}
if( m_world->isStartPhase() )
{
handleRaceStart();
AIBaseController::update(dt);
AIBaseLapController::update(dt);
return;
}
@ -390,7 +390,7 @@ void SkiddingAI::update(float dt)
}
/*And obviously general kart stuff*/
AIBaseController::update(dt);
AIBaseLapController::update(dt);
} // update
//-----------------------------------------------------------------------------
@ -2152,7 +2152,7 @@ void SkiddingAI::handleCurve()
/** Determines if the kart should skid. The base implementation enables
* skidding
* \param steer_fraction The steering fraction as computed by the
* AIBaseController.
* AIBaseLapController.
* \return True if the kart should skid.
*/
bool SkiddingAI::doSkid(float steer_fraction)

View File

@ -22,7 +22,7 @@
#ifndef HEADER_SKIDDING_AI_HPP
#define HEADER_SKIDDING_AI__HPP
#include "karts/controller/ai_base_controller.hpp"
#include "karts/controller/ai_base_lap_controller.hpp"
#include "race/race_manager.hpp"
#include "tracks/graph_node.hpp"
#include "utils/random_generator.hpp"
@ -90,7 +90,7 @@ the AI does the following steps:
\ingroup controller
*/
class SkiddingAI : public AIBaseController
class SkiddingAI : public AIBaseLapController
{
private:

View File

@ -162,7 +162,7 @@
#include "items/attachment_manager.hpp"
#include "items/item_manager.hpp"
#include "items/projectile_manager.hpp"
#include "karts/controller/ai_base_controller.hpp"
#include "karts/controller/ai_base_lap_controller.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/demo_world.hpp"
@ -678,7 +678,7 @@ int handleCmdLine(int argc, char **argv)
}
else if(!strcmp(argv[i], "--ai-debug"))
{
AIBaseController::enableDebug();
AIBaseLapController::enableDebug();
}
else if(sscanf(argv[i], "--server=%d",&n)==1)
{