Merge remote-tracking branch 'gus/AI'

This commit is contained in:
Marc Zinnschlag 2013-04-11 21:45:20 +02:00
commit e561881abe
6 changed files with 372 additions and 40 deletions

View File

@ -65,7 +65,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate repair enchanting aiescort aiactivate repair enchanting pathfinding
) )
add_openmw_dir (mwbase add_openmw_dir (mwbase

View File

@ -210,7 +210,7 @@ namespace MWMechanics
{ {
mDuration += duration; mDuration += duration;
if (mDuration>=0.25) //if (mDuration>=0.25)
{ {
float totalDuration = mDuration; float totalDuration = mDuration;
mDuration = 0; mDuration = 0;

View File

@ -1,25 +1,110 @@
#include "aitravel.hpp" #include "aitravel.hpp"
#include <iostream> #include <iostream>
MWMechanics::AiTravel::AiTravel(float x, float y, float z) #include "character.hpp"
: mX(x),mY(y),mZ(z)
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "movement.hpp"
#include "../mwworld/player.hpp"
#include <boost/graph/astar_search.hpp>
#include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp"
namespace
{ {
float sgn(float a)
{
if(a>0) return 1.;
else return -1.;
}
} }
MWMechanics::AiTravel * MWMechanics::AiTravel::clone() const namespace MWMechanics
{ {
return new AiTravel(*this);
AiTravel::AiTravel(float x, float y, float z)
: mX(x),mY(y),mZ(z),mPathFinder()
{
}
AiTravel * AiTravel::clone() const
{
return new AiTravel(*this);
}
bool AiTravel::execute (const MWWorld::Ptr& actor)
{
const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 2000))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
//check if actor is near the border of an inactive cell. If so, disable aitravel.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 2000))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
}
if(!mPathFinder.isPathConstructed() ||cellChange)
{
cellX = actor.getCell()->mCell->mData.mX;
cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0;
float yCell = 0;
if (actor.getCell()->mCell->isExterior())
{
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
}
ESM::Pathgrid::Point dest;
dest.mX = mX;
dest.mY = mY;
dest.mZ = mZ;
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell);
}
if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2]))
{
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true;
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
return false;
}
int AiTravel::getTypeId() const
{
return 1;
}
} }
bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor)
{
std::cout << "AiTravel completed.\n";
return true;
}
int MWMechanics::AiTravel::getTypeId() const
{
return 1;
}

View File

@ -1,27 +1,35 @@
#ifndef GAME_MWMECHANICS_AITRAVEL_H #ifndef GAME_MWMECHANICS_AITRAVEL_H
#define GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H
#include "aipackage.hpp" #include "aipackage.hpp"
#include "pathfinding.hpp"
namespace MWMechanics
{ namespace MWMechanics
class AiTravel : public AiPackage {
{ class AiTravel : public AiPackage
public: {
AiTravel(float x, float y, float z); public:
virtual AiTravel *clone() const; AiTravel(float x, float y, float z);
virtual AiTravel *clone() const;
virtual bool execute (const MWWorld::Ptr& actor);
///< \return Package completed? virtual bool execute (const MWWorld::Ptr& actor);
///< \return Package completed?
virtual int getTypeId() const;
virtual int getTypeId() const;
private:
float mX; private:
float mY; float mX;
float mZ; float mY;
float mZ;
};
int cellX;
int cellY;
//bool isPathConstructed;
//std::list<ESM::Pathgrid::Point> mPath;
PathFinder mPathFinder;
};
} }
#endif #endif

View File

@ -0,0 +1,210 @@
#include "pathfinding.hpp"
#include <boost/graph/astar_search.hpp>
#include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp"
#include "OgreMath.h"
namespace
{
//helpers functions
float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z)
{
return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z));
}
float distance(ESM::Pathgrid::Point point,float x,float y,float z)
{
return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+(point.mZ - z)*(point.mZ - z));
}
float distance(ESM::Pathgrid::Point a,ESM::Pathgrid::Point b)
{
return sqrt(float(a.mX - b.mX)*(a.mX - b.mX)+(a.mY - b.mY)*(a.mY - b.mY)+(a.mZ - b.mZ)*(a.mZ - b.mZ));
}
static float sgn(float a)
{
if(a>0) return 1.;
else return -1.;
}
int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z)
{
if(!grid) return -1;
if(grid->mPoints.empty()) return -1;
float m = distance(grid->mPoints[0],x,y,z);
int i0 = 0;
for(unsigned int i=1; i<grid->mPoints.size();++i)
{
if(distance(grid->mPoints[i],x,y,z)<m)
{
m = distance(grid->mPoints[i],x,y,z);
i0 = i;
}
}
return i0;
}
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS,
boost::property<boost::vertex_index_t,int,ESM::Pathgrid::Point>,boost::property<boost::edge_weight_t,float> > PathGridGraph;
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
typedef PathGridGraph::vertex_descriptor PointID;
typedef PathGridGraph::edge_descriptor PointConnectionID;
struct found_path {};
class goalVisited : public boost::default_astar_visitor
{
public:
goalVisited(PointID goal) : mGoal(goal) {}
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private:
PointID mGoal;
};
class DistanceHeuristic : public boost::astar_heuristic <PathGridGraph, float>
{
public:
DistanceHeuristic(const PathGridGraph & l, PointID goal)
: mGraph(l), mGoal(goal) {}
float operator()(PointID u)
{
const ESM::Pathgrid::Point & U = mGraph[u];
const ESM::Pathgrid::Point & V = mGraph[mGoal];
float dx = U.mX - V.mX;
float dy = U.mY - V.mY;
float dz = U.mZ - V.mZ;
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
const PathGridGraph & mGraph;
PointID mGoal;
};
}
namespace MWMechanics
{
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0)
{
PathGridGraph graph;
for(unsigned int i = 0;i<pathgrid->mPoints.size();++i)
{
PointID pID = boost::add_vertex(graph);
graph[pID].mX = pathgrid->mPoints[i].mX + xCell;
graph[pID].mY = pathgrid->mPoints[i].mY + yCell;
graph[pID].mZ = pathgrid->mPoints[i].mZ;
}
for(unsigned int i = 0;i<pathgrid->mEdges.size();++i)
{
PointID u = pathgrid->mEdges[i].mV0;
PointID v = pathgrid->mEdges[i].mV1;
PointConnectionID edge;
bool done;
boost::tie(edge,done) = boost::add_edge(u,v,graph);
WeightMap weightmap = boost::get(boost::edge_weight, graph);
weightmap[edge] = distance(graph[u],graph[v]);
}
return graph;
}
std::list<ESM::Pathgrid::Point> findPath(PointID start,PointID end,PathGridGraph graph){
std::vector<PointID> p(boost::num_vertices(graph));
std::vector<float> d(boost::num_vertices(graph));
std::list<ESM::Pathgrid::Point> shortest_path;
try {
boost::astar_search
(
graph,
start,
DistanceHeuristic(graph,end),
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
);
} catch(found_path fg) {
for(PointID v = end;; v = p[v]) {
shortest_path.push_front(graph[v]);
if(p[v] == v)
break;
}
}
return shortest_path;
}
//end of helpers functions
PathFinder::PathFinder()
{
mIsPathConstructed = false;
}
void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint,
const ESM::Pathgrid* pathGrid,float xCell,float yCell)
{
int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ);
int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ);
if(start != -1 && end != -1)
{
PathGridGraph graph = buildGraph(pathGrid,xCell,yCell);
mPath = findPath(start,end,graph);
}
mPath.push_back(endPoint);
mIsPathConstructed = true;
}
float PathFinder::getZAngleToNext(float x,float y,float z)
{
if(mPath.empty())
{
return 0;/// shouldn't happen!
}
ESM::Pathgrid::Point nextPoint = *mPath.begin();
float dX = nextPoint.mX - x;
float dY = nextPoint.mY - y;
float h = sqrt(dX*dX+dY*dY);
return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees();
}
bool PathFinder::checkIfNextPointReached(float x,float y,float z)
{
if(mPath.empty())
{
return true;
}
ESM::Pathgrid::Point nextPoint = *mPath.begin();
if(distanceZCorrected(nextPoint,x,y,z) < 20)
{
mPath.pop_front();
if(mPath.empty())
{
return true;
}
nextPoint = *mPath.begin();
}
return false;
}
std::list<ESM::Pathgrid::Point> PathFinder::getPath()
{
return mPath;
}
bool PathFinder::isPathConstructed()
{
return mIsPathConstructed;
}
}

View File

@ -0,0 +1,29 @@
#ifndef GAME_MWMECHANICS_PATHFINDING_H
#define GAME_MWMECHANICS_PATHFINDING_H
#include <components/esm/loadpgrd.hpp>
#include <list>
namespace MWMechanics
{
class PathFinder
{
public:
PathFinder();
void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint,
const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0);
bool checkIfNextPointReached(float x,float y,float z);//returns true if the last point of the path has been reached.
float getZAngleToNext(float x,float y,float z);
std::list<ESM::Pathgrid::Point> getPath();
bool isPathConstructed();
private:
std::list<ESM::Pathgrid::Point> mPath;
bool mIsPathConstructed;
};
}
#endif