1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 18:35:20 +00:00

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
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate repair enchanting
aiescort aiactivate repair enchanting pathfinding
)
add_openmw_dir (mwbase

View File

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

View File

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