First playable version of battle AI
TODO: backwards firing better handling to obstacles ai karts number selection win/lose screen handling Please use a better navmesh to test
This commit is contained in:
parent
f4b723de82
commit
62f5be0d9d
@ -39,23 +39,24 @@ AIBaseController::AIBaseController(AbstractKart *kart,
|
||||
m_kart = kart;
|
||||
m_kart_length = m_kart->getKartLength();
|
||||
m_kart_width = m_kart->getKartWidth();
|
||||
m_ai_properties =
|
||||
m_kart->getKartProperties()->getAIPropertiesForDifficulty();
|
||||
|
||||
m_ai_properties = m_kart->getKartProperties()->getAIPropertiesForDifficulty();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AIBaseController::reset()
|
||||
{
|
||||
m_stuck = false;
|
||||
m_collision_times.clear();
|
||||
m_stuck = false;
|
||||
m_collision_times.clear();
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AIBaseController::update(float dt)
|
||||
{
|
||||
m_stuck = false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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
|
||||
@ -243,13 +244,12 @@ bool AIBaseController::doSkid(float steer_fraction)
|
||||
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
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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
|
||||
@ -260,51 +260,50 @@ bool AIBaseController::doSkid(float steer_fraction)
|
||||
*/
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
// 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());
|
||||
|
||||
// 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);
|
||||
|
||||
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 = true;
|
||||
}
|
||||
// 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 = true;
|
||||
}
|
||||
|
||||
} // crashed(Material)
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
|
||||
|
||||
class AIProperties;
|
||||
class LinearWorld;
|
||||
class ThreeStrikesBattle;
|
||||
@ -31,9 +30,12 @@ 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
|
||||
@ -44,70 +46,42 @@ private:
|
||||
* this kart is stuck and needs to be rescued. */
|
||||
bool m_stuck;
|
||||
|
||||
|
||||
protected:
|
||||
/** Length of the kart, storing it here saves many function calls. */
|
||||
/** 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;
|
||||
|
||||
|
||||
|
||||
/** A pointer to the AI properties for this kart. */
|
||||
const AIProperties *m_ai_properties;
|
||||
|
||||
<<<<<<< HEAD
|
||||
/** 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;
|
||||
=======
|
||||
>>>>>>> origin/battleAI
|
||||
|
||||
static bool m_ai_debug;
|
||||
|
||||
|
||||
virtual void update (float delta) ;
|
||||
virtual void setSteering (float angle, float dt);
|
||||
void setControllerName(const std::string &name);
|
||||
float steerToPoint(const Vec3 &point);
|
||||
float normalizeAngle(float angle);
|
||||
virtual bool doSkid(float steer_fraction);
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** 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; }
|
||||
|
||||
public:
|
||||
|
||||
AIBaseController(AbstractKart *kart,
|
||||
AIBaseController(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
virtual ~AIBaseController() {};
|
||||
virtual void reset();
|
||||
static void enableDebug() {m_ai_debug = true; }
|
||||
virtual bool disableSlipstreamBonus() const;
|
||||
|
||||
virtual void crashed(const Material *m);
|
||||
virtual void crashed(const Material *m);
|
||||
}; // AIBaseController
|
||||
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* EOF */
|
||||
|
@ -1,266 +1,263 @@
|
||||
//
|
||||
// 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"
|
||||
|
||||
|
||||
/**
|
||||
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)
|
||||
{
|
||||
|
||||
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()
|
||||
{
|
||||
AIBaseController::reset();
|
||||
} // reset
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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 call
|
||||
* AIBaseController::update() which will reset the isStuck flag!
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void AIBaseLapController::update(float dt)
|
||||
{
|
||||
AIBaseController::update(dt);
|
||||
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
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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
|
||||
|
||||
|
||||
//
|
||||
// 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"
|
||||
|
||||
|
||||
/**
|
||||
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)
|
||||
{
|
||||
|
||||
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()
|
||||
{
|
||||
AIBaseController::reset();
|
||||
} // reset
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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 call
|
||||
* AIBaseController::update() which will reset the isStuck flag!
|
||||
* \param dt Time step size.
|
||||
*/
|
||||
void AIBaseLapController::update(float dt)
|
||||
{
|
||||
AIBaseController::update(dt);
|
||||
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
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** 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
|
||||
|
@ -1,96 +1,87 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Keep a pointer to world. */
|
||||
LinearWorld *m_world;
|
||||
|
||||
/** 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);
|
||||
|
||||
float steerToAngle (const unsigned int sector, const float angle);
|
||||
|
||||
|
||||
void computePath();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Nothing special to do when the race is finished. */
|
||||
virtual void raceFinished() {};
|
||||
|
||||
|
||||
public:
|
||||
AIBaseLapController(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
virtual ~AIBaseLapController() {};
|
||||
virtual void reset();
|
||||
virtual void crashed(const AbstractKart *k) {};
|
||||
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() {};
|
||||
|
||||
}; // AIBaseLapController
|
||||
|
||||
#endif
|
||||
|
||||
/* EOF */
|
||||
//
|
||||
// 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
|
||||
{
|
||||
protected:
|
||||
/** 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;
|
||||
|
||||
/** Keep a pointer to world. */
|
||||
LinearWorld *m_world;
|
||||
|
||||
/** 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);
|
||||
|
||||
float steerToAngle (const unsigned int sector, const float angle);
|
||||
|
||||
void computePath();
|
||||
// ------------------------------------------------------------------------
|
||||
/** Nothing special to do when the race is finished. */
|
||||
virtual void raceFinished() {};
|
||||
|
||||
public:
|
||||
AIBaseLapController(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
virtual ~AIBaseLapController() {};
|
||||
virtual void reset();
|
||||
virtual void crashed(const AbstractKart *k) {};
|
||||
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() {};
|
||||
}; // AIBaseLapController
|
||||
|
||||
#endif
|
||||
|
@ -45,6 +45,7 @@ protected:
|
||||
friend class AIBaseController;
|
||||
friend class AIBaseLapController;
|
||||
friend class SkiddingAI;
|
||||
friend class BattleAI;
|
||||
|
||||
/** Used to check that all values are defined in the xml file. */
|
||||
static float UNDEFINED;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,126 +1,127 @@
|
||||
|
||||
//
|
||||
// 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
|
||||
// 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_BATTLE_AI_HPP
|
||||
#define HEADER_BATTLE_AI__HPP
|
||||
|
||||
#include "karts/controller/ai_base_controller.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
class AIProperties;
|
||||
class ThreeStrikesBattle;
|
||||
class BattleGraph;
|
||||
class Track;
|
||||
class Vec3;
|
||||
class Item;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; }
|
||||
namespace video { class ITexture; }
|
||||
}
|
||||
|
||||
class BattleAI : public AIBaseController
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** Holds the current position of the AI on the battle graph. Sets to
|
||||
* BattleGraph::UNKNOWN_POLY if the location is unknown. This variable is
|
||||
* updated in ThreeStrikesBattle::updateKartNodes() */
|
||||
int m_current_node;
|
||||
|
||||
/** Holds the next node the kart is expected to drive to. Currently unused. */
|
||||
int m_next_node;
|
||||
|
||||
/** The node(poly) at which the target point lies in. */
|
||||
int m_target_node;
|
||||
|
||||
/** The target point. */
|
||||
Vec3 m_target_point;
|
||||
|
||||
/** The steering angle required to reach the target point. */
|
||||
float m_target_angle;
|
||||
|
||||
/** Holds the set of portals that the kart will cross when moving through
|
||||
* polygon channel. See findPortals() */
|
||||
std::vector<std::pair<Vec3,Vec3> > m_portals;
|
||||
|
||||
/** Holds the corner points computed using the funnel algorithm that the AI
|
||||
* will eventaully move through. See stringPull() */
|
||||
std::vector<Vec3> m_path_corners;
|
||||
|
||||
/** This is a timer that counts down when the kart is reversing to get unstuck */
|
||||
float m_time_since_stuck;
|
||||
|
||||
/** Indicates that the kart is currently reversing, and m_time_since_stuck is
|
||||
* counting down. */
|
||||
bool m_currently_reversing;
|
||||
|
||||
const Item *m_item_to_collect;
|
||||
|
||||
float determineTurnRadius(std::vector<Vec3>& points);
|
||||
void findPortals(int start, int end);
|
||||
void stringPull(const Vec3&, const Vec3&);
|
||||
void handleAcceleration(const float dt) ;
|
||||
void handleSteering(const float dt);
|
||||
void handleBraking();
|
||||
void handleGetUnstuck(const float dt);
|
||||
void handleItems(const float dt);
|
||||
void handleItemCollection(Vec3*, int*);
|
||||
|
||||
protected:
|
||||
|
||||
/** Keep a pointer to world. */
|
||||
ThreeStrikesBattle *m_world;
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
/** For debugging purpose: a sphere indicating where the AI
|
||||
* is targeting at. */
|
||||
irr::scene::ISceneNode *m_debug_sphere;
|
||||
#endif
|
||||
|
||||
public:
|
||||
BattleAI(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
~BattleAI();
|
||||
unsigned int getCurrentNode() const { return m_current_node; }
|
||||
void setCurrentNode(int i) { m_current_node = i; }
|
||||
virtual void update (float delta);
|
||||
virtual void reset ();
|
||||
|
||||
virtual void crashed(const AbstractKart *k) {};
|
||||
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 {return 0;}
|
||||
virtual void newLap(int lap) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// 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
|
||||
// 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_BATTLE_AI_HPP
|
||||
#define HEADER_BATTLE_AI_HPP
|
||||
|
||||
#include "karts/controller/ai_base_controller.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
class AIProperties;
|
||||
class ThreeStrikesBattle;
|
||||
class BattleGraph;
|
||||
class Track;
|
||||
class Vec3;
|
||||
class Item;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; }
|
||||
namespace video { class ITexture; }
|
||||
}
|
||||
|
||||
class BattleAI : public AIBaseController
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** Holds the current position of the AI on the battle graph. Sets to
|
||||
* BattleGraph::UNKNOWN_POLY if the location is unknown. This variable is
|
||||
* updated in ThreeStrikesBattle::updateKartNodes() */
|
||||
int m_current_node;
|
||||
|
||||
/** The node(poly) at which the target point lies in. */
|
||||
int m_target_node;
|
||||
|
||||
/** The target point. */
|
||||
Vec3 m_target_point;
|
||||
|
||||
/** Holds the set of portals that the kart will cross when moving through
|
||||
* polygon channel. See findPortals() */
|
||||
std::vector<std::pair<Vec3,Vec3> > m_portals;
|
||||
|
||||
/** Holds the corner points computed using the funnel algorithm that the AI
|
||||
* will eventaully move through. See stringPull() */
|
||||
std::vector<Vec3> m_path_corners;
|
||||
|
||||
/** Time an item has been collected and not used. */
|
||||
float m_time_since_last_shot;
|
||||
|
||||
/** This is a timer that counts down when the kart is reversing to get unstuck */
|
||||
float m_time_since_stuck;
|
||||
|
||||
/** Indicates that the kart is currently reversing, and m_time_since_stuck is
|
||||
* counting down. */
|
||||
bool m_currently_reversing;
|
||||
|
||||
float m_closest_kart_distance;
|
||||
|
||||
const Item *m_item_to_collect;
|
||||
|
||||
float determineTurnRadius(std::vector<Vec3>& points);
|
||||
void findPortals(int start, int end);
|
||||
void stringPull(const Vec3&, const Vec3&);
|
||||
void handleAcceleration(const float dt);
|
||||
void handleSteering(const float dt);
|
||||
void handleBraking();
|
||||
void handleGetUnstuck(const float dt);
|
||||
void handleItems(const float dt);
|
||||
|
||||
void handleItemCollection(Vec3*, int*);
|
||||
void findClosestKart(Vec3*, int*);
|
||||
|
||||
protected:
|
||||
|
||||
/** Keep a pointer to world. */
|
||||
ThreeStrikesBattle *m_world;
|
||||
|
||||
#ifdef AI_DEBUG
|
||||
/** For debugging purpose: a sphere indicating where the AI
|
||||
* is targeting at. */
|
||||
irr::scene::ISceneNode *m_debug_sphere;
|
||||
#endif
|
||||
|
||||
public:
|
||||
BattleAI(AbstractKart *kart,
|
||||
StateManager::ActivePlayer *player=NULL);
|
||||
~BattleAI();
|
||||
unsigned int getCurrentNode() const { return m_current_node; }
|
||||
void setCurrentNode(int i) { m_current_node = i; }
|
||||
virtual void update (float delta);
|
||||
virtual void reset ();
|
||||
|
||||
virtual void crashed(const AbstractKart *k) {};
|
||||
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 {return 0; }
|
||||
virtual void newLap(int lap) {};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
void collectedItem (const Item &item, int add_info=-1,
|
||||
float previous_energy=0);
|
||||
unsigned int getCurrentNode() const { return m_current_node; }
|
||||
void setCurrentNode(int i) { m_current_node = i; }
|
||||
void setCurrentNode(int i) { m_current_node = i; }
|
||||
virtual void skidBonusTriggered();
|
||||
virtual void setPosition (int p);
|
||||
virtual bool isPlayerController() const {return true;}
|
||||
|
@ -60,7 +60,7 @@
|
||||
#include <iostream>
|
||||
|
||||
SkiddingAI::SkiddingAI(AbstractKart *kart)
|
||||
: AIBaseController(kart)
|
||||
: AIBaseLapController(kart)
|
||||
{
|
||||
reset();
|
||||
// Determine if this AI has superpowers, which happens e.g.
|
||||
@ -179,7 +179,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)
|
||||
@ -191,7 +191,7 @@ void SkiddingAI::reset()
|
||||
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
|
||||
}
|
||||
|
||||
AIBaseController::reset();
|
||||
AIBaseLapController::reset();
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -296,14 +296,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;
|
||||
}
|
||||
|
||||
@ -378,7 +378,7 @@ void SkiddingAI::update(float dt)
|
||||
}
|
||||
|
||||
/*And obviously general kart stuff*/
|
||||
AIBaseController::update(dt);
|
||||
AIBaseLapController::update(dt);
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -2194,7 +2194,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)
|
||||
|
@ -57,14 +57,6 @@ void ThreeStrikesBattle::init()
|
||||
{
|
||||
WorldWithRank::init();
|
||||
m_display_rank = false;
|
||||
|
||||
// check for possible problems if AI karts were incorrectly added
|
||||
// FIXME : remove this bit of code in future since ai will be added
|
||||
/* if(getNumKarts() > race_manager->getNumPlayers())
|
||||
{
|
||||
Log::fatal("[Three Strikes Battle]", "No AI exists for this game mode");
|
||||
}
|
||||
*/
|
||||
m_kart_info.resize(m_karts.size());
|
||||
} // ThreeStrikesBattle
|
||||
|
||||
@ -300,8 +292,8 @@ void ThreeStrikesBattle::update(float dt)
|
||||
WorldWithRank::update(dt);
|
||||
WorldWithRank::updateTrack(dt);
|
||||
|
||||
PhysicalObject::BodyTypes body_shape;
|
||||
updateKartNodes(); // insert blown away tire(s) now if was requested
|
||||
|
||||
while (m_insert_tire > 0)
|
||||
{
|
||||
std::string tire;
|
||||
@ -435,7 +427,7 @@ bool ThreeStrikesBattle::isRaceOver()
|
||||
} // isRaceOver
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Updates the m_current_node value of each kart controller to localize it
|
||||
/** Updates the m_current_node value of each kart controller to localize it
|
||||
* on the navigation mesh.
|
||||
*/
|
||||
void ThreeStrikesBattle::updateKartNodes()
|
||||
@ -444,58 +436,54 @@ void ThreeStrikesBattle::updateKartNodes()
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
if(m_karts[i]->isEliminated()) continue;
|
||||
|
||||
|
||||
const AbstractKart* kart = m_karts[i];
|
||||
|
||||
|
||||
if(!kart->getController()->isPlayerController())
|
||||
{
|
||||
BattleAI* controller = (BattleAI*)(kart->getController());
|
||||
|
||||
|
||||
int saved_current_node = controller->getCurrentNode();
|
||||
|
||||
if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY)
|
||||
if (saved_current_node != BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
//check if the kart is still on the same node
|
||||
const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode());
|
||||
if(p.pointInPoly(kart->getXYZ())) continue;
|
||||
|
||||
//if not then check all adjacent polys
|
||||
const std::vector<int>& adjacents =
|
||||
const std::vector<int>& adjacents =
|
||||
NavMesh::get()->getAdjacentPolys(controller->getCurrentNode());
|
||||
|
||||
|
||||
// Set m_current_node to unknown so that if no adjacent poly checks true
|
||||
// we look everywhere the next time updateCurrentNode is called. This is
|
||||
// useful in cases when you are "teleported" to some other poly, ex. rescue
|
||||
controller->setCurrentNode(BattleGraph::UNKNOWN_POLY);
|
||||
|
||||
|
||||
|
||||
for(unsigned int i=0; i<adjacents.size(); i++)
|
||||
{
|
||||
const NavPoly& p_temp =
|
||||
const NavPoly& p_temp =
|
||||
BattleGraph::get()->getPolyOfNode(adjacents[i]);
|
||||
if(p_temp.pointInPoly(kart->getXYZ()))
|
||||
if(p_temp.pointInPoly(kart->getXYZ()))
|
||||
controller->setCurrentNode(adjacents[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Current node is still unkown
|
||||
if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY)
|
||||
if (saved_current_node == BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
bool flag = 0;
|
||||
unsigned int max_count = BattleGraph::get()->getNumNodes();
|
||||
//float min_dist = 9999.99f;
|
||||
for(unsigned int i =0; i<max_count; i++)
|
||||
for(unsigned int i=0; i<max_count; i++)
|
||||
{
|
||||
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
|
||||
if((p.pointInPoly(kart->getXYZ())))
|
||||
{
|
||||
controller->setCurrentNode(i);
|
||||
flag = 1;
|
||||
//min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(flag == 0) controller->setCurrentNode(saved_current_node);
|
||||
}
|
||||
|
||||
@ -503,40 +491,37 @@ void ThreeStrikesBattle::updateKartNodes()
|
||||
else
|
||||
{
|
||||
PlayerController* controller = (PlayerController*)(kart->getController());
|
||||
|
||||
|
||||
int saved_current_node = controller->getCurrentNode();
|
||||
|
||||
if(controller->getCurrentNode()!= BattleGraph::UNKNOWN_POLY)
|
||||
if (saved_current_node != BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
//check if the kart is still on the same node
|
||||
const NavPoly& p = BattleGraph::get()->getPolyOfNode(controller->getCurrentNode());
|
||||
if(p.pointInPoly(kart->getXYZ())) continue;
|
||||
|
||||
//if not then check all adjacent polys
|
||||
const std::vector<int>& adjacents =
|
||||
const std::vector<int>& adjacents =
|
||||
NavMesh::get()->getAdjacentPolys(controller->getCurrentNode());
|
||||
|
||||
|
||||
// Set m_current_node to unknown so that if no adjacent poly checks true
|
||||
// we look everywhere the next time updateCurrentNode is called. This is
|
||||
// useful in cases when you are "teleported" to some other poly, ex. rescue
|
||||
controller->setCurrentNode(BattleGraph::UNKNOWN_POLY);
|
||||
|
||||
|
||||
|
||||
for(unsigned int i=0; i<adjacents.size(); i++)
|
||||
{
|
||||
const NavPoly& p_temp =
|
||||
const NavPoly& p_temp =
|
||||
BattleGraph::get()->getPolyOfNode(adjacents[i]);
|
||||
if(p_temp.pointInPoly(kart->getXYZ()))
|
||||
if(p_temp.pointInPoly(kart->getXYZ()))
|
||||
controller->setCurrentNode(adjacents[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(controller->getCurrentNode() == BattleGraph::UNKNOWN_POLY)
|
||||
if (saved_current_node == BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
bool flag = 0;
|
||||
unsigned int max_count = BattleGraph::get()->getNumNodes();
|
||||
//float min_dist = 9999.99f;
|
||||
for(unsigned int i =0; i<max_count; i++)
|
||||
{
|
||||
const NavPoly& p = BattleGraph::get()->getPolyOfNode(i);
|
||||
@ -544,17 +529,15 @@ void ThreeStrikesBattle::updateKartNodes()
|
||||
{
|
||||
controller->setCurrentNode(i);
|
||||
flag = 1;
|
||||
//min_dist = (p.getCenter() - m_kart->getXYZ()).length_2d();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(flag == 0) controller->setCurrentNode(saved_current_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when the race finishes, i.e. after playing (if necessary) an
|
||||
* end of race animation. It updates the time for all karts still racing,
|
||||
|
@ -98,7 +98,6 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con
|
||||
{
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
UserConfigParams::m_game_mode = CONFIG_CODE_3STRIKES;
|
||||
race_manager->setNumKarts( race_manager->getNumLocalPlayers() ); // no AI karts;
|
||||
ArenasScreen::getInstance()->push();
|
||||
}
|
||||
else if (selectedMode == IDENT_EASTER)
|
||||
@ -223,17 +222,14 @@ void RaceSetupScreen::init()
|
||||
w2->addItem(name3, IDENT_FTL, RaceManager::getIconOf(RaceManager::MINOR_MODE_FOLLOW_LEADER), false);
|
||||
}
|
||||
|
||||
if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_mode)
|
||||
{
|
||||
irr::core::stringw name4 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_3_STRIKES)) + L"\n";
|
||||
//FIXME: avoid duplicating descriptions from the help menu!
|
||||
name4 += _("Hit others with weapons until they lose all their lives (only in multiplayer games).");
|
||||
w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_3_STRIKES));
|
||||
}
|
||||
irr::core::stringw name4 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_3_STRIKES)) + L"\n";
|
||||
//FIXME: avoid duplicating descriptions from the help menu!
|
||||
name4 += _("Hit others with weapons until they lose all their lives (only in multiplayer games).");
|
||||
w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_3_STRIKES));
|
||||
|
||||
#ifdef ENABLE_SOCCER_MODE
|
||||
if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_mode)
|
||||
if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_mode)
|
||||
{
|
||||
irr::core::stringw name5 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_SOCCER)) + L"\n";
|
||||
@ -244,6 +240,7 @@ if (race_manager->getNumLocalPlayers() > 1 || UserConfigParams::m_artist_debug_m
|
||||
|
||||
#define ENABLE_EASTER_EGG_MODE
|
||||
#ifdef ENABLE_EASTER_EGG_MODE
|
||||
if(race_manager->getNumLocalPlayers() == 1)
|
||||
{
|
||||
irr::core::stringw name1 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_EASTER_EGG)) + L"\n";
|
||||
|
@ -1,278 +1,279 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#include "tracks/battle_graph.hpp"
|
||||
|
||||
#include <IMesh.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
const int BattleGraph::UNKNOWN_POLY = -1;
|
||||
BattleGraph * BattleGraph::m_battle_graph = NULL;
|
||||
|
||||
/** Constructor, Creates a navmesh, builds a graph from the navmesh and
|
||||
* then runs shortest path algorithm to find and store paths to be used
|
||||
* by the AI. */
|
||||
BattleGraph::BattleGraph(const std::string &navmesh_file_name)
|
||||
{
|
||||
NavMesh::create(navmesh_file_name);
|
||||
m_navmesh_file = navmesh_file_name;
|
||||
buildGraph(NavMesh::get());
|
||||
computeFloydWarshall();
|
||||
findItemsOnGraphNodes(ItemManager::get());
|
||||
|
||||
} // BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Builds a graph from an existing NavMesh. The graph is stored as an adjacency
|
||||
* matrix. */
|
||||
void BattleGraph::buildGraph(NavMesh* navmesh)
|
||||
{
|
||||
unsigned int n_polys = navmesh->getNumberOfPolys();
|
||||
|
||||
m_distance_matrix = std::vector< std::vector<float> > (n_polys, std::vector<float>(n_polys, 9999.9f));
|
||||
for(unsigned int i=0; i<n_polys; i++)
|
||||
{
|
||||
NavPoly currentPoly = navmesh->getNavPoly(i);
|
||||
std::vector<int> adjacents = navmesh->getAdjacentPolys(i);
|
||||
for(unsigned int j=0; j<adjacents.size(); j++)
|
||||
{
|
||||
Vec3 adjacentPolyCenter = navmesh->getCenterOfPoly(adjacents[j]);
|
||||
float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d();
|
||||
|
||||
m_distance_matrix[i][adjacents[j]] = distance;
|
||||
//m_distance_matrix[adjacents[j]][i] = distance;
|
||||
|
||||
}
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
} // buildGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** computeFloydWarshall() computes the shortest distance between any two nodes.
|
||||
* At the end of the computation, m_distance_matrix[i][j] stores the shortest path
|
||||
* distance from i to j and m_parent_poly[i][j] stores the last vertex visited on the
|
||||
* shortest path from i to j before visiting j. Suppose the shortest path from i to j is
|
||||
* i->......->k->j then m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeFloydWarshall()
|
||||
{
|
||||
int n = getNumNodes();
|
||||
|
||||
// initialize m_parent_poly with unknown_poly so that if no path is found b/w i and j
|
||||
// then m_parent_poly[i][j] = -1 (UNKNOWN_POLY)
|
||||
// AI must check this
|
||||
m_parent_poly = std::vector< std::vector<int> > (n, std::vector<int>(n,BattleGraph::UNKNOWN_POLY));
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j]>=9899.9f) m_parent_poly[i][j]=-1;
|
||||
else m_parent_poly[i][j] = i;
|
||||
}
|
||||
|
||||
for(unsigned int k=0; k<n; k++)
|
||||
{
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if( (m_distance_matrix[i][k] + m_distance_matrix[k][j]) < m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] = m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_parent_poly[i][j] = m_parent_poly[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // computeFloydWarshall
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Destructor, destroys NavMesh and the debug mesh if it exists */
|
||||
BattleGraph::~BattleGraph(void)
|
||||
{
|
||||
NavMesh::destroy();
|
||||
|
||||
if(UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
} // ~BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the actual mesh that is used by createDebugMesh() */
|
||||
void BattleGraph::createMesh(bool enable_transparency,
|
||||
const video::SColor *track_color)
|
||||
{
|
||||
// The debug track will not be lighted or culled.
|
||||
video::SMaterial m;
|
||||
m.BackfaceCulling = false;
|
||||
m.Lighting = false;
|
||||
if(enable_transparency)
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
m_mesh = irr_driver->createQuadMesh(&m);
|
||||
m_mesh_buffer = m_mesh->getMeshBuffer(0);
|
||||
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
|
||||
|
||||
|
||||
// Eps is used to raise the track debug quads a little bit higher than
|
||||
// the ground, so that they are actually visible.
|
||||
core::vector3df eps(0, 0.4f, 0);
|
||||
video::SColor defaultColor(255, 255, 0, 0), c;
|
||||
|
||||
// Declare vector to hold new converted vertices, vertices are copied over
|
||||
// for each polygon, although it results in redundant vertex copies in the
|
||||
// final vector, this is the only way I know to make each poly have different color.
|
||||
std::vector<video::S3DVertex> new_v;
|
||||
|
||||
// Declare vector to hold indices
|
||||
std::vector<irr::u16> ind;
|
||||
|
||||
// Now add all polygons
|
||||
int i=0;
|
||||
for(unsigned int count=0; count<getNumNodes(); count++)
|
||||
{
|
||||
///compute colors
|
||||
if(!track_color)
|
||||
{
|
||||
c.setAlpha(178);
|
||||
//c.setRed ((i%2) ? 255 : 0);
|
||||
//c.setBlue((i%3) ? 0 : 255);
|
||||
c.setRed(7*i%256);
|
||||
c.setBlue((2*i)%256);
|
||||
c.setGreen((3*i)%256);
|
||||
}
|
||||
|
||||
NavPoly poly = NavMesh::get()->getNavPoly(count);
|
||||
|
||||
//std::vector<int> vInd = poly.getVerticesIndex();
|
||||
const std::vector<Vec3>& v = poly.getVertices();
|
||||
|
||||
// Number of triangles in the triangle fan
|
||||
unsigned int numberOfTriangles = v.size() -2 ;
|
||||
|
||||
// Set up the indices for the triangles
|
||||
|
||||
for( unsigned int count = 1; count<=numberOfTriangles; count++)
|
||||
{
|
||||
video::S3DVertex v1,v2,v3;
|
||||
v1.Pos=v[0].toIrrVector() + eps;
|
||||
v2.Pos=v[count].toIrrVector() + eps;
|
||||
v3.Pos=v[count+1].toIrrVector() + eps;
|
||||
|
||||
v1.Color = c;
|
||||
v2.Color = c;
|
||||
v3.Color = c;
|
||||
|
||||
core::triangle3df tri(v1.Pos, v2.Pos, v3.Pos);
|
||||
core::vector3df normal = tri.getNormal();
|
||||
normal.normalize();
|
||||
v1.Normal = normal;
|
||||
v2.Normal = normal;
|
||||
v3.Normal = normal;
|
||||
|
||||
new_v.push_back(v1);
|
||||
new_v.push_back(v2);
|
||||
new_v.push_back(v3);
|
||||
|
||||
ind.push_back(i++);
|
||||
ind.push_back(i++);
|
||||
ind.push_back(i++);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_mesh_buffer->append(new_v.data(), new_v.size(), ind.data(), ind.size());
|
||||
|
||||
// Instead of setting the bounding boxes, we could just disable culling,
|
||||
// since the debug track should always be drawn.
|
||||
//m_node->setAutomaticCulling(scene::EAC_OFF);
|
||||
m_mesh_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());
|
||||
|
||||
} // createMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the debug mesh to display the quad graph on top of the track
|
||||
* model. */
|
||||
void BattleGraph::createDebugMesh()
|
||||
{
|
||||
if(getNumNodes()<=0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*enable_transparency*/true);
|
||||
m_node = irr_driver->addMesh(m_mesh);
|
||||
#ifdef DEBUG
|
||||
m_node->setName("track-debug-mesh");
|
||||
#endif
|
||||
|
||||
} // createDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Cleans up the debug mesh */
|
||||
void BattleGraph::cleanupDebugMesh()
|
||||
{
|
||||
if(m_node != NULL)
|
||||
irr_driver->removeNode(m_node);
|
||||
|
||||
m_node = NULL;
|
||||
// No need to call irr_driber->removeMeshFromCache, since the mesh
|
||||
// was manually made and so never added to the mesh cache.
|
||||
m_mesh->drop();
|
||||
m_mesh = NULL;
|
||||
}
|
||||
|
||||
|
||||
void BattleGraph::findItemsOnGraphNodes(ItemManager * item_manager)
|
||||
{
|
||||
unsigned int item_count = item_manager->getNumberOfItems();
|
||||
|
||||
for (unsigned int i = 0; i < item_count; ++i)
|
||||
{
|
||||
Item* item = item_manager->getItem(i);
|
||||
Vec3 xyz = item->getXYZ();
|
||||
int polygon = BattleGraph::UNKNOWN_POLY;
|
||||
float min_dist = 999999.9f;
|
||||
|
||||
for (unsigned int j = 0; j < this->getNumNodes(); ++j)
|
||||
{
|
||||
if (NavMesh::get()->getNavPoly(j).pointInPoly(xyz))
|
||||
{
|
||||
float dist = xyz.getY() - NavMesh::get()->getCenterOfPoly(j).getY();
|
||||
if (dist < min_dist && dist>-1.0f)
|
||||
{
|
||||
polygon = j;
|
||||
min_dist = dist;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
m_items_on_graph.push_back(std::make_pair(item, polygon));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#include "tracks/battle_graph.hpp"
|
||||
|
||||
#include <IMesh.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
const int BattleGraph::UNKNOWN_POLY = -1;
|
||||
BattleGraph * BattleGraph::m_battle_graph = NULL;
|
||||
|
||||
/** Constructor, Creates a navmesh, builds a graph from the navmesh and
|
||||
* then runs shortest path algorithm to find and store paths to be used
|
||||
* by the AI. */
|
||||
BattleGraph::BattleGraph(const std::string &navmesh_file_name)
|
||||
{
|
||||
NavMesh::create(navmesh_file_name);
|
||||
m_navmesh_file = navmesh_file_name;
|
||||
buildGraph(NavMesh::get());
|
||||
computeFloydWarshall();
|
||||
findItemsOnGraphNodes(ItemManager::get());
|
||||
|
||||
} // BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Builds a graph from an existing NavMesh. The graph is stored as an adjacency
|
||||
* matrix. */
|
||||
void BattleGraph::buildGraph(NavMesh* navmesh)
|
||||
{
|
||||
unsigned int n_polys = navmesh->getNumberOfPolys();
|
||||
|
||||
m_distance_matrix = std::vector< std::vector<float> > (n_polys, std::vector<float>(n_polys, 9999.9f));
|
||||
for(unsigned int i=0; i<n_polys; i++)
|
||||
{
|
||||
NavPoly currentPoly = navmesh->getNavPoly(i);
|
||||
std::vector<int> adjacents = navmesh->getAdjacentPolys(i);
|
||||
for(unsigned int j=0; j<adjacents.size(); j++)
|
||||
{
|
||||
Vec3 adjacentPolyCenter = navmesh->getCenterOfPoly(adjacents[j]);
|
||||
float distance = Vec3(adjacentPolyCenter - currentPoly.getCenter()).length_2d();
|
||||
|
||||
m_distance_matrix[i][adjacents[j]] = distance;
|
||||
//m_distance_matrix[adjacents[j]][i] = distance;
|
||||
|
||||
}
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
} // buildGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** computeFloydWarshall() computes the shortest distance between any two nodes.
|
||||
* At the end of the computation, m_distance_matrix[i][j] stores the shortest path
|
||||
* distance from i to j and m_parent_poly[i][j] stores the last vertex visited on the
|
||||
* shortest path from i to j before visiting j. Suppose the shortest path from i to j is
|
||||
* i->......->k->j then m_parent_poly[i][j] = k
|
||||
*/
|
||||
void BattleGraph::computeFloydWarshall()
|
||||
{
|
||||
unsigned int n = getNumNodes();
|
||||
|
||||
// initialize m_parent_poly with unknown_poly so that if no path is found b/w i and j
|
||||
// then m_parent_poly[i][j] = -1 (UNKNOWN_POLY)
|
||||
// AI must check this
|
||||
m_parent_poly = std::vector< std::vector<int> > (n, std::vector<int>(n,BattleGraph::UNKNOWN_POLY));
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j]>=9899.9f) m_parent_poly[i][j]=-1;
|
||||
else m_parent_poly[i][j] = i;
|
||||
}
|
||||
|
||||
for(unsigned int k=0; k<n; k++)
|
||||
{
|
||||
for(unsigned int i=0; i<n; i++)
|
||||
{
|
||||
for(unsigned int j=0; j<n; j++)
|
||||
{
|
||||
if( (m_distance_matrix[i][k] + m_distance_matrix[k][j]) < m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] = m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_parent_poly[i][j] = m_parent_poly[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // computeFloydWarshall
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Destructor, destroys NavMesh and the debug mesh if it exists */
|
||||
BattleGraph::~BattleGraph(void)
|
||||
{
|
||||
NavMesh::destroy();
|
||||
|
||||
if(UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
} // ~BattleGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the actual mesh that is used by createDebugMesh() */
|
||||
void BattleGraph::createMesh(bool enable_transparency,
|
||||
const video::SColor *track_color)
|
||||
{
|
||||
// The debug track will not be lighted or culled.
|
||||
video::SMaterial m;
|
||||
m.BackfaceCulling = false;
|
||||
m.Lighting = false;
|
||||
if(enable_transparency)
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
m_mesh = irr_driver->createQuadMesh(&m);
|
||||
m_mesh_buffer = m_mesh->getMeshBuffer(0);
|
||||
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
|
||||
|
||||
// Eps is used to raise the track debug quads a little bit higher than
|
||||
// the ground, so that they are actually visible.
|
||||
core::vector3df eps(0, 0.4f, 0);
|
||||
video::SColor c = video::SColor(255, 255, 0, 0);
|
||||
|
||||
// Declare vector to hold new converted vertices, vertices are copied over
|
||||
// for each polygon, although it results in redundant vertex copies in the
|
||||
// final vector, this is the only way I know to make each poly have different color.
|
||||
std::vector<video::S3DVertex> new_v;
|
||||
|
||||
// Declare vector to hold indices
|
||||
std::vector<irr::u16> ind;
|
||||
|
||||
// Now add all polygons
|
||||
int i=0;
|
||||
for(unsigned int count=0; count<getNumNodes(); count++)
|
||||
{
|
||||
///compute colors
|
||||
if(!track_color)
|
||||
{
|
||||
c.setAlpha(178);
|
||||
//c.setRed ((i%2) ? 255 : 0);
|
||||
//c.setBlue((i%3) ? 0 : 255);
|
||||
c.setRed(7*i%256);
|
||||
c.setBlue((2*i)%256);
|
||||
c.setGreen((3*i)%256);
|
||||
}
|
||||
|
||||
NavPoly poly = NavMesh::get()->getNavPoly(count);
|
||||
|
||||
//std::vector<int> vInd = poly.getVerticesIndex();
|
||||
const std::vector<Vec3>& v = poly.getVertices();
|
||||
|
||||
// Number of triangles in the triangle fan
|
||||
unsigned int numberOfTriangles = v.size() -2 ;
|
||||
|
||||
// Set up the indices for the triangles
|
||||
|
||||
for( unsigned int count = 1; count<=numberOfTriangles; count++)
|
||||
{
|
||||
video::S3DVertex v1,v2,v3;
|
||||
v1.Pos=v[0].toIrrVector() + eps;
|
||||
v2.Pos=v[count].toIrrVector() + eps;
|
||||
v3.Pos=v[count+1].toIrrVector() + eps;
|
||||
|
||||
v1.Color = c;
|
||||
v2.Color = c;
|
||||
v3.Color = c;
|
||||
|
||||
core::triangle3df tri(v1.Pos, v2.Pos, v3.Pos);
|
||||
core::vector3df normal = tri.getNormal();
|
||||
normal.normalize();
|
||||
v1.Normal = normal;
|
||||
v2.Normal = normal;
|
||||
v3.Normal = normal;
|
||||
|
||||
new_v.push_back(v1);
|
||||
new_v.push_back(v2);
|
||||
new_v.push_back(v3);
|
||||
|
||||
ind.push_back(i++);
|
||||
ind.push_back(i++);
|
||||
ind.push_back(i++);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_mesh_buffer->append(new_v.data(), new_v.size(), ind.data(), ind.size());
|
||||
|
||||
// Instead of setting the bounding boxes, we could just disable culling,
|
||||
// since the debug track should always be drawn.
|
||||
//m_node->setAutomaticCulling(scene::EAC_OFF);
|
||||
m_mesh_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());
|
||||
|
||||
} // createMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the debug mesh to display the quad graph on top of the track
|
||||
* model. */
|
||||
void BattleGraph::createDebugMesh()
|
||||
{
|
||||
if(getNumNodes()<=0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*enable_transparency*/false);
|
||||
m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh");
|
||||
#ifdef DEBUG
|
||||
// m_node->setName("track-debug-mesh");
|
||||
#endif
|
||||
|
||||
} // createDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Cleans up the debug mesh */
|
||||
void BattleGraph::cleanupDebugMesh()
|
||||
{
|
||||
if(m_node != NULL)
|
||||
irr_driver->removeNode(m_node);
|
||||
|
||||
m_node = NULL;
|
||||
// No need to call irr_driber->removeMeshFromCache, since the mesh
|
||||
// was manually made and so never added to the mesh cache.
|
||||
m_mesh->drop();
|
||||
m_mesh = NULL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void BattleGraph::findItemsOnGraphNodes(ItemManager * item_manager)
|
||||
{
|
||||
unsigned int item_count = item_manager->getNumberOfItems();
|
||||
|
||||
for (unsigned int i = 0; i < item_count; ++i)
|
||||
{
|
||||
Item* item = item_manager->getItem(i);
|
||||
Vec3 xyz = item->getXYZ();
|
||||
int polygon = BattleGraph::UNKNOWN_POLY;
|
||||
|
||||
for (unsigned int j = 0; j < this->getNumNodes(); ++j)
|
||||
{
|
||||
if (NavMesh::get()->getNavPoly(j).pointInPoly(xyz))
|
||||
polygon = j;
|
||||
}
|
||||
|
||||
if (polygon != BattleGraph::UNKNOWN_POLY)
|
||||
{
|
||||
m_items_on_graph.push_back(std::make_pair(item, polygon));
|
||||
Log::debug("BattleGraph","item number %d is on polygon %d", i, polygon);
|
||||
}
|
||||
else
|
||||
Log::debug("BattleGraph","Can't map item number %d with a suitable polygon", i);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const int & BattleGraph::getNextShortestPathPoly(int i, int j) const
|
||||
{
|
||||
if (i == BattleGraph::UNKNOWN_POLY || j == BattleGraph::UNKNOWN_POLY)
|
||||
return BattleGraph::UNKNOWN_POLY;
|
||||
return m_parent_poly[j][i];
|
||||
}
|
||||
|
@ -1,132 +1,128 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#ifndef HEADER_BATTLE_GRAPH_HPP
|
||||
#define HEADER_BATTLE_GRAPH_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
|
||||
class Navmesh;
|
||||
class Item;
|
||||
class ItemManager;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
|
||||
namespace video { class ITexture; }
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*
|
||||
* \brief This class stores a graph constructed from the navigatoin mesh.
|
||||
* It uses a 'simplified singleton' design pattern: it has a static create
|
||||
* function to create exactly one instance, a destroy function, and a get
|
||||
* function (that does not have the side effect of the 'normal singleton'
|
||||
* design pattern to create an instance).
|
||||
\ingroup tracks
|
||||
*/
|
||||
class BattleGraph
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
static BattleGraph *m_battle_graph;
|
||||
|
||||
/** The actual graph data structure, it is an adjacency matrix */
|
||||
std::vector< std::vector< float > > m_distance_matrix;
|
||||
/** The matrix that is used to store computed shortest paths */
|
||||
std::vector< std::vector< int > > m_parent_poly;
|
||||
/** For debug mode only: the node of the debug mesh. */
|
||||
scene::ISceneNode *m_node;
|
||||
/** For debug only: the mesh of the debug mesh. */
|
||||
scene::IMesh *m_mesh;
|
||||
/** For debug only: the actual mesh buffer storing the quads. */
|
||||
scene::IMeshBuffer *m_mesh_buffer;
|
||||
|
||||
/** Stores the name of the file containing the NavMesh data */
|
||||
std::string m_navmesh_file;
|
||||
|
||||
std::vector< std::pair<Item*, int> > m_items_on_graph;
|
||||
|
||||
void buildGraph(NavMesh*);
|
||||
void computeFloydWarshall();
|
||||
void createMesh(bool enable_transparency=false,
|
||||
const video::SColor *track_color=NULL);
|
||||
void findItemsOnGraphNodes(ItemManager*);
|
||||
|
||||
BattleGraph(const std::string &navmesh_file_name);
|
||||
~BattleGraph(void);
|
||||
|
||||
public:
|
||||
|
||||
static const int UNKNOWN_POLY;
|
||||
|
||||
/** Returns the one instance of this object. */
|
||||
static BattleGraph *get() { return m_battle_graph; }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Asserts that no BattleGraph instance exists. Then
|
||||
* creates a BattleGraph instance. */
|
||||
static void create(const std::string &navmesh_file_name)
|
||||
{
|
||||
assert(m_battle_graph==NULL);
|
||||
m_battle_graph = new BattleGraph(navmesh_file_name);
|
||||
|
||||
} // create
|
||||
// ----------------------------------------------------------------------
|
||||
/** Cleans up the BattleGraph instance if it exists */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_battle_graph)
|
||||
{
|
||||
delete m_battle_graph;
|
||||
m_battle_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the number of nodes in the BattleGraph (equal to the number of
|
||||
* polygons in the NavMesh */
|
||||
unsigned int getNumNodes() const { return m_distance_matrix.size(); }
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the NavPoly corresponding to the i-th node of the BattleGraph */
|
||||
const NavPoly& getPolyOfNode(int i) const
|
||||
{ return NavMesh::get()->getNavPoly(i); }
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the next polygon on the shortest path from i to j.
|
||||
* Note: m_parent_poly[j][i] contains the parent of i on path from j to i,
|
||||
* which is the next node on the path from i to j (undirected graph) */
|
||||
const int & getNextShortestPathPoly(int i, int j) const
|
||||
{ return m_parent_poly[j][i]; }
|
||||
|
||||
const std::vector< std::pair<Item*, int> >& getItemList()
|
||||
{ return m_items_on_graph; }
|
||||
|
||||
void createDebugMesh();
|
||||
void cleanupDebugMesh();
|
||||
}; //BattleGraph
|
||||
|
||||
#endif
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#ifndef HEADER_BATTLE_GRAPH_HPP
|
||||
#define HEADER_BATTLE_GRAPH_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
class Navmesh;
|
||||
class Item;
|
||||
class ItemManager;
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
|
||||
namespace video { class ITexture; }
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*
|
||||
* \brief This class stores a graph constructed from the navigatoin mesh.
|
||||
* It uses a 'simplified singleton' design pattern: it has a static create
|
||||
* function to create exactly one instance, a destroy function, and a get
|
||||
* function (that does not have the side effect of the 'normal singleton'
|
||||
* design pattern to create an instance).
|
||||
\ingroup tracks
|
||||
*/
|
||||
class BattleGraph
|
||||
{
|
||||
|
||||
private:
|
||||
static BattleGraph *m_battle_graph;
|
||||
|
||||
/** The actual graph data structure, it is an adjacency matrix */
|
||||
std::vector< std::vector< float > > m_distance_matrix;
|
||||
/** The matrix that is used to store computed shortest paths */
|
||||
std::vector< std::vector< int > > m_parent_poly;
|
||||
/** For debug mode only: the node of the debug mesh. */
|
||||
scene::ISceneNode *m_node;
|
||||
/** For debug only: the mesh of the debug mesh. */
|
||||
scene::IMesh *m_mesh;
|
||||
/** For debug only: the actual mesh buffer storing the quads. */
|
||||
scene::IMeshBuffer *m_mesh_buffer;
|
||||
|
||||
/** Stores the name of the file containing the NavMesh data */
|
||||
std::string m_navmesh_file;
|
||||
|
||||
std::vector< std::pair<Item*, int> > m_items_on_graph;
|
||||
|
||||
void buildGraph(NavMesh*);
|
||||
void computeFloydWarshall();
|
||||
void createMesh(bool enable_transparency=false,
|
||||
const video::SColor *track_color=NULL);
|
||||
void findItemsOnGraphNodes(ItemManager*);
|
||||
|
||||
BattleGraph(const std::string &navmesh_file_name);
|
||||
~BattleGraph(void);
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_POLY;
|
||||
|
||||
/** Returns the one instance of this object. */
|
||||
static BattleGraph *get() { return m_battle_graph; }
|
||||
// ----------------------------------------------------------------------
|
||||
/** Asserts that no BattleGraph instance exists. Then
|
||||
* creates a BattleGraph instance. */
|
||||
static void create(const std::string &navmesh_file_name)
|
||||
{
|
||||
assert(m_battle_graph==NULL);
|
||||
m_battle_graph = new BattleGraph(navmesh_file_name);
|
||||
|
||||
} // create
|
||||
// ----------------------------------------------------------------------
|
||||
/** Cleans up the BattleGraph instance if it exists */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_battle_graph)
|
||||
{
|
||||
delete m_battle_graph;
|
||||
m_battle_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the number of nodes in the BattleGraph (equal to the number of
|
||||
* polygons in the NavMesh */
|
||||
unsigned int getNumNodes() const { return m_distance_matrix.size(); }
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the NavPoly corresponding to the i-th node of the BattleGraph */
|
||||
const NavPoly& getPolyOfNode(int i) const
|
||||
{ return NavMesh::get()->getNavPoly(i); }
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/** Returns the next polygon on the shortest path from i to j.
|
||||
* Note: m_parent_poly[j][i] contains the parent of i on path from j to i,
|
||||
* which is the next node on the path from i to j (undirected graph) */
|
||||
const int & getNextShortestPathPoly(int i, int j) const;
|
||||
|
||||
const std::vector< std::pair<Item*, int> >& getItemList()
|
||||
{ return m_items_on_graph; }
|
||||
|
||||
void createDebugMesh();
|
||||
void cleanupDebugMesh();
|
||||
}; //BattleGraph
|
||||
|
||||
#endif
|
||||
|
@ -1,82 +1,81 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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 "tracks/nav_poly.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
/** Constructor that takes a vector of points and a vector of adjacnet polygons */
|
||||
NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
const std::vector<int> &adjacentPolygonIndices)
|
||||
{
|
||||
m_vertices = polygonVertIndices;
|
||||
|
||||
m_adjacents = adjacentPolygonIndices;
|
||||
|
||||
std::vector<Vec3> xyz_points = getVertices();
|
||||
|
||||
Vec3 temp(0.0f,0.0f,0.0f);
|
||||
for(unsigned int i=0; i<xyz_points.size(); i++)
|
||||
temp = temp + xyz_points[i];
|
||||
|
||||
m_center = (temp)*( 1.0f / xyz_points.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
const std::vector<Vec3> NavPoly::getVertices()
|
||||
{
|
||||
std::vector<Vec3> points;
|
||||
for(unsigned int i=0; i<m_vertices.size(); i++)
|
||||
points.push_back(NavMesh::get()->getVertex(m_vertices[i]));
|
||||
return points;
|
||||
}
|
||||
|
||||
bool NavPoly::pointInPoly(const Vec3& p) const
|
||||
{
|
||||
std::vector<Vec3> points;
|
||||
for(unsigned int i=0; i<m_vertices.size(); i++)
|
||||
points.push_back(NavMesh::get()->getVertex(m_vertices[i]));
|
||||
|
||||
// The point is on which side of the first edge
|
||||
float side = p.sideOfLine2D(points[0],points[1]);
|
||||
|
||||
|
||||
// The point is inside the polygon if it is on the same side for all edges
|
||||
for(unsigned int i=1; i<points.size(); i++)
|
||||
{
|
||||
// If it is on different side then product is < 0 , return false
|
||||
if(p.sideOfLine2D(points[i % points.size()],
|
||||
points[(i+1)% points.size()]) * side < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const Vec3& NavPoly::operator[](int i) const
|
||||
{
|
||||
return NavMesh::get()->getVertex(m_vertices[i]);
|
||||
}
|
||||
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 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 "tracks/nav_poly.hpp"
|
||||
#include "tracks/navmesh.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
/** Constructor that takes a vector of points and a vector of adjacent polygons */
|
||||
NavPoly::NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
const std::vector<int> &adjacentPolygonIndices)
|
||||
{
|
||||
m_vertices = polygonVertIndices;
|
||||
|
||||
m_adjacents = adjacentPolygonIndices;
|
||||
|
||||
std::vector<Vec3> xyz_points = getVertices();
|
||||
|
||||
Vec3 temp(0.0f,0.0f,0.0f);
|
||||
for(unsigned int i=0; i<xyz_points.size(); i++)
|
||||
temp = temp + xyz_points[i];
|
||||
|
||||
m_center = (temp)*( 1.0f / xyz_points.size());
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const std::vector<Vec3> NavPoly::getVertices()
|
||||
{
|
||||
std::vector<Vec3> points;
|
||||
for(unsigned int i=0; i<m_vertices.size(); i++)
|
||||
points.push_back(NavMesh::get()->getVertex(m_vertices[i]));
|
||||
return points;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool NavPoly::pointInPoly(const Vec3& p) const
|
||||
{
|
||||
std::vector<Vec3> points;
|
||||
for(unsigned int i=0; i<m_vertices.size(); i++)
|
||||
points.push_back(NavMesh::get()->getVertex(m_vertices[i]));
|
||||
|
||||
// The point is on which side of the first edge
|
||||
float side = p.sideOfLine2D(points[0],points[1]);
|
||||
|
||||
// The point is inside the polygon if it is on the same side for all edges
|
||||
for(unsigned int i=1; i<points.size(); i++)
|
||||
{
|
||||
// If it is on different side then product is < 0 , return false
|
||||
if(p.sideOfLine2D(points[i % points.size()],
|
||||
points[(i+1)% points.size()]) * side < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const Vec3& NavPoly::operator[](int i) const
|
||||
{
|
||||
return NavMesh::get()->getVertex(m_vertices[i]);
|
||||
}
|
||||
|
@ -1,72 +1,70 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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_NAV_POLY_HPP
|
||||
#define HEADER_NAV_POLY_HPP
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <SColor.h>
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class NavPoly
|
||||
{
|
||||
private:
|
||||
|
||||
/** Holds the index of vertices for a polygon **/
|
||||
std::vector<int> m_vertices;
|
||||
|
||||
/** Center of this polygon. **/
|
||||
Vec3 m_center;
|
||||
|
||||
/** Holds the index of adjacent polyogns **/
|
||||
std::vector<int> m_adjacents;
|
||||
|
||||
public:
|
||||
NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
const std::vector<int> &adjacentPolygonIndices);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center point of a polygon. */
|
||||
const Vec3& getCenter() const {return m_center;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the adjacent polygons of a polygon. */
|
||||
const std::vector<int>& getAdjacents() const {return m_adjacents;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the vertices(Vec3) of this polygon. */
|
||||
const std::vector<Vec3> getVertices();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the indices of the vertices of this polygon */
|
||||
const std::vector<int> getVerticesIndex() const {return m_vertices;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if a given point lies in this polygon. */
|
||||
bool pointInPoly(const Vec3& p) const;
|
||||
|
||||
const Vec3& operator[](int i) const ;
|
||||
|
||||
}; // class NavPoly
|
||||
|
||||
#endif
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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_NAV_POLY_HPP
|
||||
#define HEADER_NAV_POLY_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <SColor.h>
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class NavPoly
|
||||
{
|
||||
private:
|
||||
/** Holds the index of vertices for a polygon **/
|
||||
std::vector<int> m_vertices;
|
||||
|
||||
/** Center of this polygon. **/
|
||||
Vec3 m_center;
|
||||
|
||||
/** Holds the index of adjacent polyogns **/
|
||||
std::vector<int> m_adjacents;
|
||||
|
||||
public:
|
||||
NavPoly(const std::vector<int> &polygonVertIndices,
|
||||
const std::vector<int> &adjacentPolygonIndices);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the center point of a polygon. */
|
||||
const Vec3& getCenter() const {return m_center;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the adjacent polygons of a polygon. */
|
||||
const std::vector<int>& getAdjacents() const {return m_adjacents;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the vertices(Vec3) of this polygon. */
|
||||
const std::vector<Vec3> getVertices();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the indices of the vertices of this polygon */
|
||||
const std::vector<int> getVerticesIndex() const {return m_vertices;}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if a given point lies in this polygon. */
|
||||
bool pointInPoly(const Vec3& p) const;
|
||||
|
||||
const Vec3& operator[](int i) const ;
|
||||
|
||||
}; // class NavPoly
|
||||
|
||||
#endif
|
||||
|
@ -1,133 +1,130 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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 "tracks/navmesh.hpp"
|
||||
#include "tracks/nav_poly.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <S3DVertex.h>
|
||||
#include <triangle3d.h>
|
||||
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
NavMesh *NavMesh::m_nav_mesh = NULL;
|
||||
|
||||
|
||||
/** Constructor, loads the mesh information from a given set of polygons
|
||||
* from a navmesh.xml file.
|
||||
* \param filename Name of the file containing all polygons
|
||||
*/
|
||||
NavMesh::NavMesh(const std::string &filename)
|
||||
{
|
||||
|
||||
m_n_verts=0;
|
||||
m_n_polys=0;
|
||||
|
||||
XMLNode *xml = file_manager->createXMLTree(filename);
|
||||
if(!xml || xml->getName()!="navmesh")
|
||||
{
|
||||
Log::error("NavMesh", "NavMesh '%s' not found. \n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Assigning m_nav_mesh here because constructing NavPoly requires m_nav_mesh to be defined
|
||||
m_nav_mesh = this;
|
||||
|
||||
for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if(xml_node->getName()=="vertices")
|
||||
{
|
||||
for(unsigned int i=0; i<xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if(!(xml_node_node->getName()=="vertex"))
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n",
|
||||
xml_node_node->getName().c_str(),filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Reading vertices
|
||||
Vec3 p;
|
||||
readVertex(xml_node_node, &p);
|
||||
m_n_verts++;
|
||||
m_verts.push_back(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(xml_node->getName()=="faces")
|
||||
{
|
||||
for(unsigned int i=0; i<xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if(xml_node_node->getName()!="face")
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n",
|
||||
xml_node_node->getName().c_str(),filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Reading faces/polys
|
||||
std::vector<int> polygonVertIndices;
|
||||
std::vector<int> adjacentPolygonIndices;
|
||||
xml_node_node->get("indices", &polygonVertIndices);
|
||||
xml_node_node->get("adjacents", &adjacentPolygonIndices);
|
||||
NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices);
|
||||
m_polys.push_back(*np);
|
||||
m_n_polys++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(xml_node->getName()=="MaxVertsPerPoly")
|
||||
{
|
||||
xml_node->get("nvp", &m_nvp);
|
||||
}
|
||||
|
||||
//delete xml_node;
|
||||
}
|
||||
|
||||
delete xml;
|
||||
|
||||
} // NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NavMesh::~NavMesh()
|
||||
{
|
||||
} // ~NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** Reads the vertex information from an XMLNode */
|
||||
void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const
|
||||
{
|
||||
float x, y, z;
|
||||
xml->get("x", &x);
|
||||
xml->get("y", &y);
|
||||
xml->get("z", &z);
|
||||
Vec3 temp(x, y, z);
|
||||
*result = temp;
|
||||
} // readVertex
|
||||
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009-2015 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 "tracks/navmesh.hpp"
|
||||
#include "tracks/nav_poly.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <S3DVertex.h>
|
||||
#include <triangle3d.h>
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
|
||||
NavMesh *NavMesh::m_nav_mesh = NULL;
|
||||
|
||||
/** Constructor, loads the mesh information from a given set of polygons
|
||||
* from a navmesh.xml file.
|
||||
* \param filename Name of the file containing all polygons
|
||||
*/
|
||||
NavMesh::NavMesh(const std::string &filename)
|
||||
{
|
||||
|
||||
m_n_verts=0;
|
||||
m_n_polys=0;
|
||||
|
||||
XMLNode *xml = file_manager->createXMLTree(filename);
|
||||
if(!xml || xml->getName()!="navmesh")
|
||||
{
|
||||
Log::error("NavMesh", "NavMesh '%s' not found. \n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Assigning m_nav_mesh here because constructing NavPoly requires m_nav_mesh to be defined
|
||||
m_nav_mesh = this;
|
||||
|
||||
for(unsigned int i=0; i<xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if(xml_node->getName()=="vertices")
|
||||
{
|
||||
for(unsigned int i=0; i<xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if(!(xml_node_node->getName()=="vertex"))
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n",
|
||||
xml_node_node->getName().c_str(),filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Reading vertices
|
||||
Vec3 p;
|
||||
readVertex(xml_node_node, &p);
|
||||
m_n_verts++;
|
||||
m_verts.push_back(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(xml_node->getName()=="faces")
|
||||
{
|
||||
for(unsigned int i=0; i<xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if(xml_node_node->getName()!="face")
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s' - ignored. \n",
|
||||
xml_node_node->getName().c_str(),filename.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Reading faces/polys
|
||||
std::vector<int> polygonVertIndices;
|
||||
std::vector<int> adjacentPolygonIndices;
|
||||
xml_node_node->get("indices", &polygonVertIndices);
|
||||
xml_node_node->get("adjacents", &adjacentPolygonIndices);
|
||||
NavPoly *np = new NavPoly(polygonVertIndices, adjacentPolygonIndices);
|
||||
m_polys.push_back(*np);
|
||||
m_n_polys++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(xml_node->getName()=="MaxVertsPerPoly")
|
||||
{
|
||||
xml_node->get("nvp", &m_nvp);
|
||||
}
|
||||
|
||||
//delete xml_node;
|
||||
}
|
||||
|
||||
delete xml;
|
||||
|
||||
} // NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NavMesh::~NavMesh()
|
||||
{
|
||||
} // ~NavMesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** Reads the vertex information from an XMLNode */
|
||||
void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const
|
||||
{
|
||||
float x, y, z;
|
||||
xml->get("x", &x);
|
||||
xml->get("y", &y);
|
||||
xml->get("z", &z);
|
||||
Vec3 temp(x, y, z);
|
||||
*result = temp;
|
||||
} // readVertex
|
||||
|
@ -1,150 +1,144 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#ifndef HEADER_NAVMESH_HPP
|
||||
#define HEADER_NAVMESH_HPP
|
||||
|
||||
#include "tracks/nav_poly.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video { struct S3DVertex; }
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
|
||||
class btTransform;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*
|
||||
* \brief This class stores a set of navigatoin polygons. It uses a
|
||||
* 'simplified singleton' design pattern: it has a static create function
|
||||
* to create exactly one instance, a destroy function, and a get function
|
||||
* (that does not have the side effect of the 'normal singleton' design
|
||||
* pattern to create an instance). Besides saving on the if statement in get(),
|
||||
* this is necessary since certain race modes might not have a navigaton
|
||||
* mesh at all (e.g. race mode). So get() returns NULL in this case, and
|
||||
* this is tested where necessary.
|
||||
\ingroup tracks
|
||||
*/
|
||||
class NavMesh
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
static NavMesh *m_nav_mesh;
|
||||
|
||||
/** The actual set of nav polys that constitute the nav mesh */
|
||||
std::vector<NavPoly> m_polys;
|
||||
|
||||
/** The set of vertices that are part of this nav mesh*/
|
||||
std::vector< Vec3 > m_verts;
|
||||
|
||||
/** Number of vertices */
|
||||
unsigned int m_n_verts;
|
||||
/** Number of polygons */
|
||||
unsigned int m_n_polys;
|
||||
/** Maximum vertices per polygon */
|
||||
unsigned int m_nvp;
|
||||
|
||||
void readVertex(const XMLNode *xml, Vec3* result) const;
|
||||
//void readFace(const XMLNode *xml, Vec3* result) const;
|
||||
NavMesh(const std::string &filename);
|
||||
~NavMesh();
|
||||
|
||||
public:
|
||||
|
||||
/** Creates a NavMesh instance. */
|
||||
static void create(const std::string &filename)
|
||||
{
|
||||
assert(m_nav_mesh==NULL);
|
||||
|
||||
// m_nav_mesh assigned in the constructor because it needs to defined
|
||||
// for NavPoly which is constructed in NavMesh()
|
||||
new NavMesh(filename);
|
||||
}
|
||||
|
||||
/** Cleans up the nav mesh. It is possible that this function is called
|
||||
* even if no instance exists (e.g. in race). So it is not an
|
||||
* error if there is no instance. */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_nav_mesh)
|
||||
{
|
||||
delete m_nav_mesh;
|
||||
m_nav_mesh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. in normal race, since it doesn't have
|
||||
* a nav mesh), so we don't assert that an instance exist, and we
|
||||
* also don't create one if it doesn't exists. */
|
||||
static NavMesh *get() { return m_nav_mesh; }
|
||||
|
||||
/** Returns a const reference to a NavPoly */
|
||||
const NavPoly& getNavPoly(int n) const
|
||||
{ return m_polys[n]; }
|
||||
|
||||
/** Returns a const reference to a vertex(Vec3) */
|
||||
const Vec3& getVertex(int n) const
|
||||
{ return m_verts[n]; }
|
||||
|
||||
/** Returns a const reference to a vector containing all vertices */
|
||||
const std::vector<Vec3>& getAllVertices() const
|
||||
{ return m_verts; }
|
||||
|
||||
/** Returns the total number of polys */
|
||||
unsigned int getNumberOfPolys() const
|
||||
{ return m_n_polys; }
|
||||
|
||||
/** Returns the total number of vertices */
|
||||
unsigned int getNumberOfVerts() const
|
||||
{ return m_n_verts; }
|
||||
|
||||
/** Returns maximum vertices per polygon */
|
||||
unsigned int getMaxVertsPerPoly() const
|
||||
{ return m_nvp; }
|
||||
|
||||
/** Returns the center of a polygon */
|
||||
const Vec3& getCenterOfPoly(int n) const
|
||||
{return m_polys[n].getCenter();}
|
||||
|
||||
/** Returns a const referece to a vector containing the indices
|
||||
* of polygons adjacent to a given polygon */
|
||||
const std::vector<int>& getAdjacentPolys(int n) const
|
||||
{return m_polys[n].getAdjacents();}
|
||||
|
||||
/** Returns a const reference to a vector containing the vertices
|
||||
* of a given polygon. */
|
||||
const std::vector<Vec3> getVertsOfPoly(int n)
|
||||
{return m_polys[n].getVertices();}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2009 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, B
|
||||
|
||||
#ifndef HEADER_NAVMESH_HPP
|
||||
#define HEADER_NAVMESH_HPP
|
||||
|
||||
#include "tracks/nav_poly.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace video { struct S3DVertex; }
|
||||
}
|
||||
using namespace irr;
|
||||
|
||||
class btTransform;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*
|
||||
* \brief This class stores a set of navigatoin polygons. It uses a
|
||||
* 'simplified singleton' design pattern: it has a static create function
|
||||
* to create exactly one instance, a destroy function, and a get function
|
||||
* (that does not have the side effect of the 'normal singleton' design
|
||||
* pattern to create an instance). Besides saving on the if statement in get(),
|
||||
* this is necessary since certain race modes might not have a navigaton
|
||||
* mesh at all (e.g. race mode). So get() returns NULL in this case, and
|
||||
* this is tested where necessary.
|
||||
\ingroup tracks
|
||||
*/
|
||||
class NavMesh
|
||||
{
|
||||
|
||||
private:
|
||||
static NavMesh *m_nav_mesh;
|
||||
|
||||
/** The actual set of nav polys that constitute the nav mesh */
|
||||
std::vector<NavPoly> m_polys;
|
||||
|
||||
/** The set of vertices that are part of this nav mesh*/
|
||||
std::vector< Vec3 > m_verts;
|
||||
|
||||
/** Number of vertices */
|
||||
unsigned int m_n_verts;
|
||||
/** Number of polygons */
|
||||
unsigned int m_n_polys;
|
||||
/** Maximum vertices per polygon */
|
||||
unsigned int m_nvp;
|
||||
|
||||
void readVertex(const XMLNode *xml, Vec3* result) const;
|
||||
//void readFace(const XMLNode *xml, Vec3* result) const;
|
||||
NavMesh(const std::string &filename);
|
||||
~NavMesh();
|
||||
|
||||
public:
|
||||
/** Creates a NavMesh instance. */
|
||||
static void create(const std::string &filename)
|
||||
{
|
||||
assert(m_nav_mesh==NULL);
|
||||
|
||||
// m_nav_mesh assigned in the constructor because it needs to defined
|
||||
// for NavPoly which is constructed in NavMesh()
|
||||
new NavMesh(filename);
|
||||
}
|
||||
|
||||
/** Cleans up the nav mesh. It is possible that this function is called
|
||||
* even if no instance exists (e.g. in race). So it is not an
|
||||
* error if there is no instance. */
|
||||
static void destroy()
|
||||
{
|
||||
if(m_nav_mesh)
|
||||
{
|
||||
delete m_nav_mesh;
|
||||
m_nav_mesh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. in normal race, since it doesn't have
|
||||
* a nav mesh), so we don't assert that an instance exist, and we
|
||||
* also don't create one if it doesn't exists. */
|
||||
static NavMesh *get() { return m_nav_mesh; }
|
||||
|
||||
/** Returns a const reference to a NavPoly */
|
||||
const NavPoly& getNavPoly(int n) const
|
||||
{ return m_polys[n]; }
|
||||
|
||||
/** Returns a const reference to a vertex(Vec3) */
|
||||
const Vec3& getVertex(int n) const
|
||||
{ return m_verts[n]; }
|
||||
|
||||
/** Returns a const reference to a vector containing all vertices */
|
||||
const std::vector<Vec3>& getAllVertices() const
|
||||
{ return m_verts; }
|
||||
|
||||
/** Returns the total number of polys */
|
||||
unsigned int getNumberOfPolys() const
|
||||
{ return m_n_polys; }
|
||||
|
||||
/** Returns the total number of vertices */
|
||||
unsigned int getNumberOfVerts() const
|
||||
{ return m_n_verts; }
|
||||
|
||||
/** Returns maximum vertices per polygon */
|
||||
unsigned int getMaxVertsPerPoly() const
|
||||
{ return m_nvp; }
|
||||
|
||||
/** Returns the center of a polygon */
|
||||
const Vec3& getCenterOfPoly(int n) const
|
||||
{return m_polys[n].getCenter();}
|
||||
|
||||
/** Returns a const referece to a vector containing the indices
|
||||
* of polygons adjacent to a given polygon */
|
||||
const std::vector<int>& getAdjacentPolys(int n) const
|
||||
{return m_polys[n].getAdjacents();}
|
||||
|
||||
/** Returns a const reference to a vector containing the vertices
|
||||
* of a given polygon. */
|
||||
const std::vector<Vec3> getVertsOfPoly(int n)
|
||||
{return m_polys[n].getVertices();}
|
||||
|
||||
};
|
||||
#endif
|
||||
|
@ -708,14 +708,18 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
|
||||
}
|
||||
}
|
||||
} // loadQuadGraph
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way
|
||||
* they are connected to each other. Input file name is hardcoded for now
|
||||
*/
|
||||
void Track::loadBattleGraph()
|
||||
{
|
||||
BattleGraph::create(m_root+"navmesh.xml");
|
||||
}// -----------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const
|
||||
{
|
||||
QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
@ -1595,10 +1599,9 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
// the information about the size of the texture to render the mini
|
||||
// map to.
|
||||
if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track);
|
||||
|
||||
|
||||
ItemManager::create();
|
||||
|
||||
|
||||
// Set the default start positions. Node that later the default
|
||||
// positions can still be overwritten.
|
||||
float forwards_distance = 1.5f;
|
||||
@ -1820,15 +1823,14 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
|
||||
delete root;
|
||||
|
||||
// ItemManager assumes the existence of a QuadGraph, that is why the
|
||||
// quad graph is loaded before ItemManager::create(). This is undesirable
|
||||
// but requires signifcant code overhaul to fix. The new battle graph
|
||||
// performs its own computatoins separate from ItemManager. But
|
||||
// Battle Graph needs ItemManager to be created, and all items to be
|
||||
// added to ItemManager. Loading battle graph here is therefore a workaround
|
||||
// to the main problem.
|
||||
if (m_is_arena && !m_is_soccer && !m_is_cutscene) loadBattleGraph();
|
||||
|
||||
// ItemManager assumes the existence of a QuadGraph, that is why the
|
||||
// quad graph is loaded before ItemManager::create(). This is undesirable
|
||||
// but requires signifcant code overhaul to fix. The new battle graph
|
||||
// performs its own computatoins separate from ItemManager. But
|
||||
// Battle Graph needs ItemManager to be created, and all items to be
|
||||
// added to ItemManager. Loading battle graph here is therefore a workaround
|
||||
// to the main problem.
|
||||
if (m_is_arena && !m_is_soccer && !m_is_cutscene) loadBattleGraph();
|
||||
|
||||
if (UserConfigParams::m_track_debug &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
@ -1842,7 +1844,6 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
!m_is_cutscene)
|
||||
BattleGraph::get()->createDebugMesh();
|
||||
|
||||
|
||||
// Only print warning if not in battle mode, since battle tracks don't have
|
||||
// any quads or check lines.
|
||||
if (CheckManager::get()->getCheckStructureCount()==0 &&
|
||||
@ -1876,8 +1877,6 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
|
||||
}
|
||||
|
||||
irr_driver->unsetTextureErrorMessage();
|
||||
|
||||
|
||||
} // loadTrackModel
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -434,7 +434,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if this track is a racing track. This means it is not an
|
||||
* internal track (like cut scenes), arena, or soccer field. */
|
||||
bool isRaceTrack() const
|
||||
bool isRaceTrack() const
|
||||
{
|
||||
return !m_internal && !m_is_arena && !m_is_soccer;
|
||||
} // isRaceTrack
|
||||
|
Loading…
x
Reference in New Issue
Block a user