Moved pathfinding code to aiPackage, implemented it's use with aiFollow and aiPursue

pull/99/head
Thomas 11 years ago
parent 645d174a96
commit 2c74ea381e

@ -12,17 +12,23 @@
#include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("")
{
mTimer = 0;
mStuckTimer = 0;
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId)
{
mTimer = 0;
mStuckTimer = 0;
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("")
{
mTimer = 0;
mStuckTimer = 0;
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
@ -31,10 +37,6 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
if(target == MWWorld::Ptr()) return true; //Target doesn't exist
mTimer = mTimer + duration; //Update timer
mStuckTimer = mStuckTimer + duration; //Update stuck timer
mTotalTime = mTotalTime + duration; //Update total time following
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
if(!mAlwaysFollow) //Update if you only follow for a bit
@ -60,74 +62,19 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
}
//Set the target desition from the actor
ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1];
dest.mZ = target.getRefData().getPosition().pos[2];
//Current position, for pathfilding stuff
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
//Build the path to get to the destination
if(mPathFinder.getPath().empty())
mPathFinder.buildPath(start, dest, actor.getCell(), true);
//***********************
// Checks if you can't get to the end position at all
//***********************
if(mTimer > 0.25)
{
if(!mPathFinder.getPath().empty()) //Path has points in it
{
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX)
+(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY)
+(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ)
> 100*100) //End of the path is far from the destination
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
}
mTimer = 0;
}
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
//************************
// Checks if you aren't moving; you're stuck
//************************
if(mStuckTimer>0.5) //Checks every half of a second
{
if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0])
+(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1])
+(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck
mPathFinder.buildPath(start, dest, actor.getCell(), true);
mStuckTimer = 0;
mStuckPos = pos;
}
//Checks if the path isn't over, turn tomards the direction that you're going
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
pathTo(actor, dest, duration); //Go to the destination
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
else
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
//Check if you're far away
if((dest.mX - start.mX)*(dest.mX - start.mX)
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) > 1000*1000)
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000)
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
else if((dest.mX - start.mX)*(dest.mX - start.mX) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
+(dest.mY - start.mY)*(dest.mY - start.mY)
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) < 800*800)
else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 800) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
return false;

@ -40,14 +40,6 @@ namespace MWMechanics
float mZ;
std::string mActorId;
std::string mCellId;
float mTimer;
float mStuckTimer;
float mTotalTime;
ESM::Position mStuckPos;
PathFinder mPathFinder;
};
}
#endif

@ -1,4 +1,95 @@
#include "aipackage.hpp"
#include <cmath>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "creaturestats.hpp"
#include "movement.hpp"
#include <OgreMath.h>
#include "steering.hpp"
MWMechanics::AiPackage::~AiPackage() {}
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration)
{
//Update various Timers
mTimer = mTimer + duration; //Update timer
mStuckTimer = mStuckTimer + duration; //Update stuck timer
mTotalTime = mTotalTime + duration; //Update total time following
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
/// Stops the actor when it gets too close to a unloaded cell
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const ESM::Cell *cell = actor.getCell()->getCell();
Movement &movement = actor.getClass().getMovementSettings(actor);
//Ensure pursuer doesn't leave loaded cells
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
}
//Start position
ESM::Pathgrid::Point start = pos.pos;
//***********************
/// Checks if you can't get to the end position at all, adds end position to end of path
/// Rebuilds path every quarter of a second, in case the target has moved
//***********************
if(mTimer > 0.25)
{
mPathFinder.buildPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved
if(!mPathFinder.getPath().empty()) //Path has points in it
{
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
if(distance(dest, lastPos) > 100) //End of the path is far from the destination
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
}
mTimer = 0;
}
//************************
/// Checks if you aren't moving; attempts to unstick you
//************************
if(mStuckTimer>0.5) //Checks every half of a second
{
if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < 10) //NPC hasn't moved much is half a second, he's stuck
mPathFinder.buildPath(start, dest, actor.getCell(), true);
mStuckTimer = 0;
mStuckPos = pos;
}
//Checks if the path isn't over, turn tomards the direction that you're going
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
}

@ -1,6 +1,9 @@
#ifndef GAME_MWMECHANICS_AIPACKAGE_H
#define GAME_MWMECHANICS_AIPACKAGE_H
#include "pathfinding.hpp"
#include "../../../components/esm/defs.hpp"
namespace MWWorld
{
class Ptr;
@ -38,8 +41,21 @@ namespace MWMechanics
/// \see enum TypeId
virtual int getTypeId() const = 0;
/// Higher number is higher priority (0 beeing the lowest)
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;}
protected:
/// Causes the actor to attempt to walk to the specified location
/** \return If the actor has arrived at his destination **/
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
PathFinder mPathFinder;
float mTimer;
float mStuckTimer;
float mTotalTime;
ESM::Position mStuckPos;
};
}

@ -21,75 +21,28 @@ MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
}
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos = actor.getRefData().getPosition();
Movement &movement = actor.getClass().getMovementSettings(actor);
const ESM::Cell *cell = actor.getCell()->getCell();
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
// Big TODO: Sync this with current AiFollow. Move common code to a shared base class or helpers (applies to all AI packages, way too much duplicated code)
MWWorld::Ptr target = world->getPtr(mObjectId,false);
ESM::Position targetPos = target.getRefData().getPosition();
if(target == MWWorld::Ptr())
return true; //Target doesn't exist
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
mCellX = cell->mData.mX;
mCellY = cell->mData.mY;
//Set the target desition from the actor
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
ESM::Pathgrid::Point dest;
dest.mX = targetPos.pos[0];
dest.mY = targetPos.pos[1];
dest.mZ = targetPos.pos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
pathTo(actor, dest, duration); //Go to the destination
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 100*100)
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
return true;
}
else
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
movement.mPosition[1] = 1;
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
return false;
}

@ -9,18 +9,12 @@
namespace MWMechanics
{
/// \brief Makes the actor very closely follow the actor
/** Used for getting closer to fight, or to arrest (I think?) **/
class AiPersue : public AiPackage
/** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. **/
class AiPursue : public AiPackage
{
public:
///Constructor
/** \param objectId Actor to pursue **/
AiPersue(const std::string &objectId);
virtual AiPersue *clone() const;
class AiPursue : public AiPackage
{
public:
AiPursue(const std::string &objectId);
virtual AiPursue *clone() const;
virtual bool execute (const MWWorld::Ptr& actor,float duration);
@ -28,8 +22,6 @@ namespace MWMechanics
private:
std::string mObjectId;
PathFinder mPathFinder;
int mCellX;
int mCellY;
};

@ -11,30 +11,6 @@
namespace
{
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + 0.1 * z * z);
}
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + z * z);
}
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{
float x = a.mX - b.mX;
float y = a.mY - b.mY;
float z = a.mZ - b.mZ;
return sqrt(x * x + y * y + z * z);
}
// Slightly cheaper version for comparisons.
// Caller needs to be careful for very short distances (i.e. less than 1)
// or when accumuating the results i.e. (a + b)^2 != a^2 + b^2
@ -114,6 +90,30 @@ namespace
namespace MWMechanics
{
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + 0.1 * z * z);
}
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + z * z);
}
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{
float x = a.mX - b.mX;
float y = a.mY - b.mY;
float z = a.mZ - b.mZ;
return sqrt(x * x + y * y + z * z);
}
PathFinder::PathFinder()
: mIsPathConstructed(false),
mPathgrid(NULL),

@ -13,6 +13,8 @@ namespace MWWorld
namespace MWMechanics
{
float distance(ESM::Pathgrid::Point point, float x, float y, float);
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b);
class PathFinder
{
public:

@ -8,6 +8,20 @@ namespace ESM
{
unsigned int Pathgrid::sRecordId = REC_PGRD;
Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[]) {
mX = rhs[0];
mY = rhs[1];
mZ = rhs[2];
return *this;
}
Pathgrid::Point::Point(const float rhs[]) {
mX = rhs[0];
mY = rhs[1];
mZ = rhs[2];
}
Pathgrid::Point::Point():mX(0),mY(0),mZ(0) {
}
void Pathgrid::load(ESMReader &esm)
{
esm.getHNT(mData, "DATA", 12);

@ -31,6 +31,9 @@ struct Pathgrid
unsigned char mAutogenerated; // autogenerated vs. user coloring flag?
unsigned char mConnectionNum; // number of connections for this point
short mUnknown;
Point& operator=(const float[]);
Point(const float[]);
Point();
}; // 16 bytes
struct Edge // path grid edge

Loading…
Cancel
Save