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:
Benau 2015-11-21 15:58:03 +08:00
parent f4b723de82
commit 62f5be0d9d
19 changed files with 2184 additions and 2085 deletions

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;}

View File

@ -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)

View File

@ -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,

View File

@ -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";

View File

@ -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];
}

View File

@ -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

View File

@ -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]);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
//-----------------------------------------------------------------------------

View File

@ -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