From 48386789446ba0f09ce055b0edd60b3f3bd0349e Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 29 May 2013 15:59:23 -0700 Subject: [PATCH 1/7] Pathfinding Overhaul - Cleanup, removed unnecessary include, fixed spacing, added a function for clearing a path, overall preperation to begin working on fixing pathfinding. --- apps/openmw/mwmechanics/pathfinding.cpp | 140 +++++++++++++----------- apps/openmw/mwmechanics/pathfinding.hpp | 25 +++-- 2 files changed, 87 insertions(+), 78 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 62c825be7..6d9089455 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -3,46 +3,48 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "OgreMath.h" + #include #include -#include "boost/tuple/tuple.hpp" -#include "OgreMath.h" namespace { - //helpers functions +//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)); + 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)); + 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)); + 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.; + if(a > 0) return 1.0; + else return -1.0; } int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) { - if(!grid) return -1; - if(grid->mPoints.empty()) return -1; + 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; imPoints.size();++i) + for(unsigned int i = 1; i < grid->mPoints.size(); ++i) { - if(distance(grid->mPoints[i],x,y,z)mPoints[i],x,y,z) < m) { m = distance(grid->mPoints[i],x,y,z); i0 = i; @@ -55,64 +57,64 @@ namespace boost::property,boost::property > PathGridGraph; typedef boost::property_map::type WeightMap; typedef PathGridGraph::vertex_descriptor PointID; - typedef PathGridGraph::edge_descriptor PointConnectionID; + typedef PathGridGraph::edge_descriptor PointConnectionID; struct found_path {}; /*class goalVisited : public boost::default_astar_visitor - { - public: - goalVisited(PointID goal) : mGoal(goal) {} +{ +public: +goalVisited(PointID goal) : mGoal(goal) {} - void examine_vertex(PointID u, const PathGridGraph g) - { - if(u == mGoal) - throw found_path(); - } - private: - PointID mGoal; - }; +void examine_vertex(PointID u, const PathGridGraph g) +{ +if(u == mGoal) +throw found_path(); +} +private: +PointID mGoal; +}; - class DistanceHeuristic : public boost::atasr_heuristic - { - public: - DistanceHeuristic(const PathGridGraph & l, PointID goal) - : mGraph(l), mGoal(goal) {} +class DistanceHeuristic : public boost::atasr_heuristic +{ +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; - };*/ +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; +};*/ - class goalVisited : public boost::default_dijkstra_visitor - { - public: - goalVisited(PointID goal) : mGoal(goal) {} +class goalVisited : public boost::default_dijkstra_visitor +{ +public: +goalVisited(PointID goal) : mGoal(goal) {} - void examine_vertex(PointID u, const PathGridGraph g) - { - if(u == mGoal) - throw found_path(); - } - private: - PointID mGoal; - }; +void examine_vertex(PointID u, const PathGridGraph g) +{ +if(u == mGoal) +throw found_path(); +} +private: +PointID mGoal; +}; PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) { PathGridGraph graph; - for(unsigned int i = 0;imPoints.size();++i) + for(unsigned int i = 0; i < pathgrid->mPoints.size(); ++i) { PointID pID = boost::add_vertex(graph); graph[pID].mX = pathgrid->mPoints[i].mX + xCell; @@ -130,7 +132,6 @@ namespace 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; @@ -147,10 +148,10 @@ namespace graph, start, 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]) { + for(PointID v = end; ; v = p[v]) { shortest_path.push_front(graph[v]); if(p[v] == v) break; @@ -170,6 +171,13 @@ namespace MWMechanics mIsPathConstructed = false; } + void PathFinder::clearPath() + { + if(!mPath.empty()) + mPath.clear(); + mIsPathConstructed = false; + } + void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, const ESM::Pathgrid* pathGrid,float xCell,float yCell) { @@ -193,9 +201,8 @@ namespace MWMechanics float PathFinder::getZAngleToNext(float x,float y,float z) { if(mPath.empty()) - { - return 0;/// shouldn't happen! - } + return 0; /// shouldn't happen! + ESM::Pathgrid::Point nextPoint = *mPath.begin(); float dX = nextPoint.mX - x; float dY = nextPoint.mY - y; @@ -206,17 +213,16 @@ namespace MWMechanics 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; @@ -226,8 +232,10 @@ namespace MWMechanics { return mPath; } + bool PathFinder::isPathConstructed() { return mIsPathConstructed; } -} \ No newline at end of file +} + diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index b1bbab37a..dc380afb4 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -8,22 +8,23 @@ namespace MWMechanics { class PathFinder { - public: - PathFinder(); + public: + PathFinder(); - void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); + void clearPath(); + 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); + 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 getPath(); - bool isPathConstructed(); + std::list getPath(); + bool isPathConstructed(); - private: - std::list mPath; - bool mIsPathConstructed; + private: + std::list mPath; + bool mIsPathConstructed; }; } -#endif \ No newline at end of file +#endif From 96fdaf74109ce79065224d7787ce08563bf8326e Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 29 May 2013 16:10:15 -0700 Subject: [PATCH 2/7] Pathfinding Overhaul - More cleanup. --- apps/openmw/mwmechanics/pathfinding.cpp | 37 +++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 6d9089455..b13bfa7af 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -10,7 +10,7 @@ namespace { -//helpers functions + // 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)); @@ -28,8 +28,10 @@ namespace static float sgn(float a) { - if(a > 0) return 1.0; - else return -1.0; + if(a > 0) + return 1.0; + else + return -1.0; } int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) @@ -95,19 +97,20 @@ const PathGridGraph & mGraph; PointID mGoal; };*/ -class goalVisited : public boost::default_dijkstra_visitor -{ -public: -goalVisited(PointID goal) : mGoal(goal) {} + class goalVisited : public boost::default_dijkstra_visitor + { + public: + goalVisited(PointID goal) : mGoal(goal) {} -void examine_vertex(PointID u, const PathGridGraph g) -{ -if(u == mGoal) -throw found_path(); -} -private: -PointID mGoal; -}; + void examine_vertex(PointID u, const PathGridGraph g) + { + if(u == mGoal) + throw found_path(); + } + + private: + PointID mGoal; + }; PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) @@ -206,8 +209,8 @@ namespace MWMechanics 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(); + 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) From 7b465ae4f1703f19182538658a58d74462d71b96 Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 29 May 2013 17:33:33 -0700 Subject: [PATCH 3/7] Pathfinding Overhaul - Even more cleanup and spacing corrections, small renaming (more to come), removed a few unnecessary actions that wasted CPU time and tmp RAM. --- apps/openmw/mwmechanics/pathfinding.cpp | 169 +++++++++--------------- apps/openmw/mwmechanics/pathfinding.hpp | 3 +- 2 files changed, 65 insertions(+), 107 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b13bfa7af..e319116f1 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -10,18 +10,36 @@ namespace { - // helpers functions - float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z) + struct found_path {}; + + typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, + boost::property, boost::property > + PathGridGraph; + typedef boost::property_map::type WeightMap; + typedef PathGridGraph::vertex_descriptor PointID; + typedef PathGridGraph::edge_descriptor PointConnectionID; + + class goalVisited : public boost::default_dijkstra_visitor + { + public: + goalVisited(PointID goal) {mGoal = goal;}; + void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();}; + + private: + PointID mGoal; + }; + + 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) + 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) + 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)); } @@ -30,90 +48,30 @@ namespace { if(a > 0) return 1.0; - else - return -1.0; + return -1.0; } - int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) + int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) { - if(!grid) - return -1; - if(grid->mPoints.empty()) + if(!grid || grid->mPoints.empty()) return -1; - float m = distance(grid->mPoints[0],x,y,z); - int i0 = 0; + float distanceBetween = distance(grid->mPoints[0], x, y, z); + int closestIndex = 0; - for(unsigned int i = 1; i < grid->mPoints.size(); ++i) + for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) { - if(distance(grid->mPoints[i],x,y,z) < m) + if(distance(grid->mPoints[counter], x, y, z) < distanceBetween) { - m = distance(grid->mPoints[i],x,y,z); - i0 = i; + distanceBetween = distance(grid->mPoints[counter], x, y, z); + closestIndex = counter; } } - return i0; + + return closestIndex; } - typedef boost::adjacency_list,boost::property > PathGridGraph; - typedef boost::property_map::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::atasr_heuristic -{ -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; -};*/ - - class goalVisited : public boost::default_dijkstra_visitor - { - public: - goalVisited(PointID goal) : mGoal(goal) {} - - void examine_vertex(PointID u, const PathGridGraph g) - { - if(u == mGoal) - throw found_path(); - } - - private: - PointID mGoal; - }; - - - PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) + PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0) { PathGridGraph graph; @@ -132,39 +90,38 @@ PointID mGoal; PointConnectionID edge; bool done; - boost::tie(edge,done) = boost::add_edge(u,v,graph); + 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]); + weightmap[edge] = distance(graph[u], graph[v]); } return graph; } - std::list findPath(PointID start,PointID end,PathGridGraph graph){ + std::list findPath(PointID start, PointID end, PathGridGraph graph) + { std::vector p(boost::num_vertices(graph)); std::vector d(boost::num_vertices(graph)); std::list shortest_path; - try { - boost::dijkstra_shortest_paths - ( - graph, - start, - boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph)) -); + try + { + boost::dijkstra_shortest_paths(graph, start, + boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))); + } - } catch(found_path fg) { - for(PointID v = end; ; v = p[v]) { + 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 - } namespace MWMechanics @@ -181,53 +138,53 @@ namespace MWMechanics mIsPathConstructed = false; } - void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid,float xCell,float yCell) + void PathFinder::buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, + const ESM::Pathgrid* pathGrid, float xCell, float yCell) { //first check if there is an obstacle - if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, - endPoint.mX,endPoint.mY,endPoint.mZ) ) + if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, + endPoint.mX, endPoint.mY, endPoint.mZ) ) { - int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ); - int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ); + 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); + 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) + float PathFinder::getZAngleToNext(float x, float y, float z) { if(mPath.empty()) - return 0; /// shouldn't happen! + 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) + 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) + if(distanceZCorrected(nextPoint, x, y, z) < 20) { mPath.pop_front(); - if(mPath.empty()) return true; - - nextPoint = *mPath.begin(); } + return false; } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index dc380afb4..90c72ebcf 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -15,7 +15,8 @@ namespace MWMechanics 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. + 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 getPath(); From c0807852356af8ec06b558ebb799480da8f3d7ea Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 29 May 2013 19:26:45 -0700 Subject: [PATCH 4/7] Pathfinding Overhaul - Finished cleaning, removed unnecessary parameter in one function, fixed use of the function in ai packages and added use of clearPath() function in aiwander, fixed algorithms and got rid of excess subtractions in getDistance functions (thanks to Chris!). --- apps/openmw/mwmechanics/aiescort.cpp | 2 +- apps/openmw/mwmechanics/aitravel.cpp | 2 +- apps/openmw/mwmechanics/aiwander.cpp | 17 +++++----- apps/openmw/mwmechanics/aiwander.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 42 +++++++++++++++---------- apps/openmw/mwmechanics/pathfinding.hpp | 8 ++--- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5b94c4938..b873844a2 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -151,7 +151,7 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) if(distanceBetweenResult <= mMaxDist * mMaxDist) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index fbae5c1d2..99df68a91 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -87,7 +87,7 @@ bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) return true; } - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 46c5598cc..310cc3274 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -64,7 +64,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) { if(!mRepeat) { - stopWalking(actor, mPathFinder); + stopWalking(actor); return true; } else @@ -74,7 +74,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) { if(!mRepeat) { - stopWalking(actor, mPathFinder); + stopWalking(actor); return true; } else @@ -149,7 +149,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) // FIXME: This *should* pause the AiWander package instead of terminating it. if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200)) { - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + stopWalking(actor); return true; } } @@ -161,7 +161,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) // FIXME: This *should* pause the AiWander package instead of terminating it. if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200)) { - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + stopWalking(actor); return true; } } @@ -242,7 +242,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) if(mWalking) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1]); MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; @@ -256,7 +256,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) if(distance < 1200 || mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) { - stopWalking(actor, mPathFinder); + stopWalking(actor); mMoveNow = false; mWalking = false; mChooseAction = true; @@ -272,10 +272,9 @@ int MWMechanics::AiWander::getTypeId() const return 0; } -void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) +void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor) { - PathFinder pathClearer; - path = pathClearer; + mPathFinder.clearPath(); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index cf3820527..c82ccc215 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -22,7 +22,7 @@ namespace MWMechanics ///< 0: Wander private: - void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); + void stopWalking(const MWWorld::Ptr& actor); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index e319116f1..3c15b050e 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -31,17 +31,26 @@ namespace 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)); + 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) { - return sqrt((point.mX - x) * (point.mX - x) + (point.mY - y) * (point.mY - y) + (point.mZ - z) * (point.mZ - 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) { - 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)); + 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); } static float sgn(float a) @@ -75,18 +84,18 @@ namespace { PathGridGraph graph; - for(unsigned int i = 0; i < pathgrid->mPoints.size(); ++i) + for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { 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; + graph[pID].mX = pathgrid->mPoints[counter].mX + xCell; + graph[pID].mY = pathgrid->mPoints[counter].mY + yCell; + graph[pID].mZ = pathgrid->mPoints[counter].mZ; } - for(unsigned int i = 0;imEdges.size();++i) + for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++) { - PointID u = pathgrid->mEdges[i].mV0; - PointID v = pathgrid->mEdges[i].mV1; + PointID u = pathgrid->mEdges[counterTwo].mV0; + PointID v = pathgrid->mEdges[counterTwo].mV1; PointConnectionID edge; bool done; @@ -145,13 +154,13 @@ namespace MWMechanics if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, endPoint.mX, endPoint.mY, endPoint.mZ) ) { - int start = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); - int end = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); + int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); + int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); - if(start != -1 && end != -1) + if(startNode != -1 && endNode != -1) { PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); - mPath = findPath(start, end, graph); + mPath = findPath(startNode, endNode, graph); } } @@ -159,10 +168,11 @@ namespace MWMechanics mIsPathConstructed = true; } - float PathFinder::getZAngleToNext(float x, float y, float z) + float PathFinder::getZAngleToNext(float x, float y) { + // This if should never be true: if(mPath.empty()) - return 0; // shouldn't happen! + return 0; ESM::Pathgrid::Point nextPoint = *mPath.begin(); float dX = nextPoint.mX - x; diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 90c72ebcf..fcd609c8e 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -12,12 +12,12 @@ namespace MWMechanics PathFinder(); void clearPath(); - void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); + 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); + 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); + float getZAngleToNext(float x, float y); std::list getPath(); bool isPathConstructed(); From 73a967174267891896e03d9ddc3c369bc3e81961 Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Wed, 29 May 2013 20:05:17 -0700 Subject: [PATCH 5/7] Pathfinding Overhaul - Changed the name of checkIfNextPointReached to a more intuitive name considering what it does (checkPathCompleted) and fixed a minor bug in it, modified buildPath() to take one final parameter, a bool which dictates whether or not to always use pathfinding (like AIWander should be doing) or to allow for "shortcuts", modified all ai packages to work with these two changes. --- apps/openmw/mwmechanics/aiescort.cpp | 4 ++-- apps/openmw/mwmechanics/aitravel.cpp | 4 ++-- apps/openmw/mwmechanics/aiwander.cpp | 4 ++-- apps/openmw/mwmechanics/pathfinding.cpp | 24 ++++++++++++++++-------- apps/openmw/mwmechanics/pathfinding.hpp | 4 ++-- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index b873844a2..755994622 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -129,10 +129,10 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, 1); } - if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; return true; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 99df68a91..4307ed284 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -78,10 +78,10 @@ bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, 1); } - if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; return true; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 310cc3274..b7d391a9f 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -235,7 +235,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start,dest,mPathgrid,mXCell,mYCell); + mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, 0); mWalking = true; } } @@ -254,7 +254,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) actorPos[1] = actorPos[1] - mYCell; float distance = actorPos.squaredDistance(destNodePos); - if(distance < 1200 || mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { stopWalking(actor); mMoveNow = false; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 3c15b050e..78be90804 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -148,11 +148,16 @@ namespace MWMechanics } void PathFinder::buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid, float xCell, float yCell) + const ESM::Pathgrid* pathGrid, float xCell, float yCell, bool allowShortcuts) { + if(allowShortcuts) + { + if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, endPoint.mX, endPoint.mY, + endPoint.mZ)) + allowShortcuts = false; + } //first check if there is an obstacle - if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, - endPoint.mX, endPoint.mY, endPoint.mZ) ) + if(!allowShortcuts) { int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); @@ -175,14 +180,14 @@ namespace MWMechanics return 0; ESM::Pathgrid::Point nextPoint = *mPath.begin(); - float dX = nextPoint.mX - x; - float dY = nextPoint.mY - y; - float h = sqrt(dX * dX + dY * dY); + float directionX = nextPoint.mX - x; + float directionY = nextPoint.mY - y; + float directionResult = sqrt(directionX * directionX + directionY * directionY); - return Ogre::Radian(acos(dY / h) * sgn(asin(dX / h))).valueDegrees(); + return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); } - bool PathFinder::checkIfNextPointReached(float x, float y, float z) + bool PathFinder::checkPathCompleted(float x, float y, float z) { if(mPath.empty()) return true; @@ -192,7 +197,10 @@ namespace MWMechanics { mPath.pop_front(); if(mPath.empty()) + { + mIsPathConstructed = false; return true; + } } return false; diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index fcd609c8e..1727c650f 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -13,9 +13,9 @@ namespace MWMechanics void clearPath(); void buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, - const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0); + const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0, bool allowShortcuts = 1); - bool checkIfNextPointReached(float x, float y, float z); + bool checkPathCompleted(float x, float y, float z); ///< \Returns true if the last point of the path has been reached. float getZAngleToNext(float x, float y); From a4caec56cf0503196a5d30214ab846df160f79c9 Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Fri, 31 May 2013 17:01:42 -0700 Subject: [PATCH 6/7] Pathfinding Overhaul - Fixed selecting cells that are inaccessable from AIWander and pathfinding in general (sadly requires minor effort on the AI Packages implementation but it is the quickest way I can see), minor cleanup again (there is a lot to cleanup, this will prolly be in every commit). --- apps/openmw/mwmechanics/aiwander.cpp | 25 ++++++++++++++++--------- apps/openmw/mwmechanics/pathfinding.cpp | 20 ++++++++++++++++---- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index b7d391a9f..1530804cd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -219,12 +219,6 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); - // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): - ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; - mAllowedNodes.erase(mAllowedNodes.begin() + randNode); - mAllowedNodes.push_back(mCurrentNode); - mCurrentNode = temp; - ESM::Pathgrid::Point dest; dest.mX = destNodePos[0] + mXCell; dest.mY = destNodePos[1] + mYCell; @@ -236,7 +230,21 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) start.mZ = pos.pos[2]; mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, 0); - mWalking = true; + + if(mPathFinder.isPathConstructed()) + { + // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): + ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + mAllowedNodes.push_back(mCurrentNode); + mCurrentNode = temp; + + mMoveNow = false; + mWalking = true; + } + // Choose a different node and delete this one from possible nodes because it is uncreachable: + else + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); } } @@ -254,14 +262,13 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) actorPos[1] = actorPos[1] - mYCell; float distance = actorPos.squaredDistance(destNodePos); - if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) + if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { stopWalking(actor); mMoveNow = false; mWalking = false; mChooseAction = true; } - } return false; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 78be90804..d3a44e0f9 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -156,7 +156,7 @@ namespace MWMechanics endPoint.mZ)) allowShortcuts = false; } - //first check if there is an obstacle + if(!allowShortcuts) { int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); @@ -166,16 +166,28 @@ namespace MWMechanics { PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); mPath = findPath(startNode, endNode, graph); + + if(!mPath.empty()) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } } } + else + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } - mPath.push_back(endPoint); - mIsPathConstructed = true; + if(mPath.empty()) + mIsPathConstructed = false; } float PathFinder::getZAngleToNext(float x, float y) { - // This if should never be true: + // This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call + // if otherwise). if(mPath.empty()) return 0; From 09beafd044f822bf8af9570225d3ac1419762a2a Mon Sep 17 00:00:00 2001 From: Torben Carrington Date: Fri, 31 May 2013 17:49:52 -0700 Subject: [PATCH 7/7] Pathfinding Overhaul - Master cleanup! Cleaned pathfinding entirely, all AI packages that are implemented as well, Increased buffer! This makes the intro guard no longer walk into you or go to far into the room (not tested against vanilla distances but it seems accurate enough until the next itteration of pathfinding fixes). --- apps/openmw/mwmechanics/aiescort.cpp | 288 ++++++------ apps/openmw/mwmechanics/aitravel.cpp | 149 ++++--- apps/openmw/mwmechanics/aiwander.cpp | 570 ++++++++++++------------ apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 4 files changed, 515 insertions(+), 494 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 755994622..6af87e8bd 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -13,8 +13,9 @@ namespace { float sgn(float a) { - if(a > 0) return 1.0; - else return -1.0; + if(a > 0) + return 1.0; + return -1.0; } } @@ -24,151 +25,156 @@ namespace TODO: Take account for actors being in different cells. */ -MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) -: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) +namespace MWMechanics { - mMaxDist = 470; - - // The CS Help File states that if a duration is givin, the AI package will run for that long - // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. - if(mX != 0 || mY != 0 || mZ != 0) - mDuration = 0; - - else + AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) + : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) { - MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - } -} - -MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) -: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) -{ - mMaxDist = 470; - - // The CS Help File states that if a duration is givin, the AI package will run for that long - // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. - if(mX != 0 || mY != 0 || mZ != 0) - mDuration = 0; - - else - { - MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - } -} - - -MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const -{ - return new AiEscort(*this); -} - -bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) -{ - // If AiEscort has ran for as long or longer then the duration specified - // and the duration is not infinite, the package is complete. - if(mDuration != 0) - { - MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); - unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); - if(currentSecond - mStartingSecond >= mDuration) - return true; - } - - 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(); - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - - 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 AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. - if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2. - 200)) - { - 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 AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. - if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2. - 200)) - { - 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, 1); - } - - if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) - { - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; - } - - const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); - const float* const leaderPos = actor.getRefData().getPosition().pos; - const float* const followerPos = follower.getRefData().getPosition().pos; - double differenceBetween[3]; - - for (short i = 0; i < 3; ++i) - differenceBetween[i] = (leaderPos[i] - followerPos[i]); - - float distanceBetweenResult = - (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); - - if(distanceBetweenResult <= mMaxDist * mMaxDist) - { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1]); - MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } } - else + + AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) + : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) { - // Stop moving if the player is to far away - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - mMaxDist = 330; + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, the AI package will run for that long + // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + if(mX != 0 || mY != 0 || mZ != 0) + mDuration = 0; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } } - return false; -} - -int MWMechanics::AiEscort::getTypeId() const -{ - return 2; + + AiEscort *MWMechanics::AiEscort::clone() const + { + return new AiEscort(*this); + } + + bool AiEscort::execute (const MWWorld::Ptr& actor) + { + // If AiEscort has ran for as long or longer then the duration specified + // and the duration is not infinite, the package is complete. + if(mDuration != 0) + { + MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); + unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); + if(currentSecond - mStartingSecond >= mDuration) + return true; + } + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + 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 AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / + 2.0 - 200)) + { + 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 AiEscort. + // FIXME: This *should* pause the AiEscort package instead of terminating it. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / + 2.0 - 200)) + { + 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, true); + } + + if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const float* const leaderPos = actor.getRefData().getPosition().pos; + const float* const followerPos = follower.getRefData().getPosition().pos; + double differenceBetween[3]; + + for (short counter = 0; counter < 3; counter++) + differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]); + + float distanceBetweenResult = + (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * + differenceBetween[2]); + + if(distanceBetweenResult <= mMaxDist * mMaxDist) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMaxDist = 470; + } + else + { + // Stop moving if the player is to far away + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mMaxDist = 330; + } + + return false; + } + + int AiEscort::getTypeId() const + { + return 2; + } } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 4307ed284..90365c16b 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -2,99 +2,106 @@ #include "movement.hpp" -#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" namespace { float sgn(float a) { - if(a > 0) return 1.; - else return -1.; + if(a > 0) + return 1.0; + return -1.0; } } -MWMechanics::AiTravel::AiTravel(float x, float y, float z) -: mX(x),mY(y),mZ(z),mPathFinder() +namespace MWMechanics { -} + AiTravel::AiTravel(float x, float y, float z) + : mX(x),mY(y),mZ(z),mPathFinder() + { + } -MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const -{ - return new AiTravel(*this); -} + AiTravel *MWMechanics::AiTravel::clone() const + { + return new AiTravel(*this); + } -bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) -{ - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - ESM::Position pos = actor.getRefData().getPosition(); + bool AiTravel::execute (const MWWorld::Ptr& actor) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Position pos = actor.getRefData().getPosition(); bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - 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. - 200)) + 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.0 - 200)) + { + 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.0 - 200)) + { + 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, true); + } + + if(mPathFinder.checkPathCompleted(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]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + return false; } - if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) + + int AiTravel::getTypeId() const { - 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. - 200)) - { - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; - } + return 1; } - - 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, 1); - } - - if(mPathFinder.checkPathCompleted(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]); - MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - - return false; } -int MWMechanics::AiTravel::getTypeId() const -{ - return 1; -} diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 1530804cd..8f7926236 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -14,316 +14,324 @@ namespace { float sgn(float a) { - if(a > 0) return 1.0; - else return -1.0; + if(a > 0) + return 1.0; + return -1.0; } } -MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): - mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) +namespace MWMechanics { - for(unsigned short counter = 0; counter < mIdle.size(); counter++) + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) { - if(mIdle[counter] >= 127 || mIdle[counter] < 0) - mIdle[counter] = 0; - } - - if(mDistance < 0) - mDistance = 0; - if(mDuration < 0) - mDuration = 0; - if(mDuration == 0) - mTimeOfDay = 0; - - srand(time(NULL)); - mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mPlayedIdle = 0; - mPathgrid = NULL; - mIdleChanceMultiplier = - MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); - - mStoredAvailableNodes = false; - mChooseAction = true; - mIdleNow = false; - mMoveNow = false; - mWalking = false; -} - -MWMechanics::AiPackage * MWMechanics::AiWander::clone() const -{ - return new AiWander(*this); -} - -bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) -{ - if(mDuration) - { - // End package if duration is complete or mid-night hits: - MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - if(currentTime.getHour() >= mStartTime.getHour() + mDuration) + for(unsigned short counter = 0; counter < mIdle.size(); counter++) { - if(!mRepeat) - { - stopWalking(actor); - return true; - } - else - mStartTime = currentTime; + if(mIdle[counter] >= 127 || mIdle[counter] < 0) + mIdle[counter] = 0; } - else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) - { - if(!mRepeat) - { - stopWalking(actor); - return true; - } - else - mStartTime = currentTime; - } - } - ESM::Position pos = actor.getRefData().getPosition(); - - if(!mStoredAvailableNodes) - { - mStoredAvailableNodes = true; - mPathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - mCellX = actor.getCell()->mCell->mData.mX; - mCellY = actor.getCell()->mCell->mData.mY; - - if(!mPathgrid) - mDistance = 0; - else if(mPathgrid->mPoints.empty()) + if(mDistance < 0) mDistance = 0; + if(mDuration < 0) + mDuration = 0; + if(mDuration == 0) + mTimeOfDay = 0; - if(mDistance) - { - mXCell = 0; - mYCell = 0; - if(actor.getCell()->mCell->isExterior()) - { - mXCell = mCellX * ESM::Land::REAL_SIZE; - mYCell = mCellY * ESM::Land::REAL_SIZE; - } - - Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); - npcPos[0] = npcPos[0] - mXCell; - npcPos[1] = npcPos[1] - mYCell; - - for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) - { - Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, mPathgrid->mPoints[counter].mZ); - if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) - mAllowedNodes.push_back(mPathgrid->mPoints[counter]); - } - if(!mAllowedNodes.empty()) - { - Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); - float closestNode = npcPos.squaredDistance(firstNodePos); - unsigned int index = 0; - for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) - { - Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, mAllowedNodes[counterThree].mZ); - float tempDist = npcPos.squaredDistance(nodePos); - if(tempDist < closestNode) - index = counterThree; - } - mCurrentNode = mAllowedNodes[index]; - mAllowedNodes.erase(mAllowedNodes.begin() + index); - } - - if(mAllowedNodes.empty()) - mDistance = 0; - } - } - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; - - 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 AiWander. - // FIXME: This *should* pause the AiWander package instead of terminating it. - if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200)) - { - stopWalking(actor); - 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 AiWander. - // FIXME: This *should* pause the AiWander package instead of terminating it. - if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200)) - { - stopWalking(actor); - return true; - } - } - - // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. - if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))) - mDistance = 0; - - if(mChooseAction) - { + srand(time(NULL)); + mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; - unsigned short idleRoll = 0; + mPathgrid = NULL; + mIdleChanceMultiplier = + MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); - for(unsigned int counter = 1; counter < mIdle.size(); counter++) + mStoredAvailableNodes = false; + mChooseAction = true; + mIdleNow = false; + mMoveNow = false; + mWalking = false; + } + + AiPackage * MWMechanics::AiWander::clone() const + { + return new AiWander(*this); + } + + bool AiWander::execute (const MWWorld::Ptr& actor) + { + if(mDuration) { - unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; - unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); - if(randSelect < idleChance && randSelect > idleRoll) + // End package if duration is complete or mid-night hits: + MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + if(currentTime.getHour() >= mStartTime.getHour() + mDuration) { - mPlayedIdle = counter; - idleRoll = randSelect; + if(!mRepeat) + { + stopWalking(actor); + return true; + } + else + mStartTime = currentTime; + } + else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) + { + if(!mRepeat) + { + stopWalking(actor); + return true; + } + else + mStartTime = currentTime; } } - if(!mPlayedIdle && mDistance) - { - mChooseAction = false; - mMoveNow = true; - } - else - { - // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: - MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStartTime = currentTime; - playIdle(actor, mPlayedIdle + 1); - mChooseAction = false; - mIdleNow = true; - } - } + ESM::Position pos = actor.getRefData().getPosition(); - if(mIdleNow) - { - if(!checkIdle(actor, mPlayedIdle + 1)) + if(!mStoredAvailableNodes) + { + mStoredAvailableNodes = true; + mPathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + mCellX = actor.getCell()->mCell->mData.mX; + mCellY = actor.getCell()->mCell->mData.mY; + + if(!mPathgrid) + mDistance = 0; + else if(mPathgrid->mPoints.empty()) + mDistance = 0; + + if(mDistance) + { + mXCell = 0; + mYCell = 0; + if(actor.getCell()->mCell->isExterior()) + { + mXCell = mCellX * ESM::Land::REAL_SIZE; + mYCell = mCellY * ESM::Land::REAL_SIZE; + } + + Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); + npcPos[0] = npcPos[0] - mXCell; + npcPos[1] = npcPos[1] - mYCell; + + for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) + { + Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, + mPathgrid->mPoints[counter].mZ); + if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) + mAllowedNodes.push_back(mPathgrid->mPoints[counter]); + } + if(!mAllowedNodes.empty()) + { + Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); + float closestNode = npcPos.squaredDistance(firstNodePos); + unsigned int index = 0; + for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) + { + Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, + mAllowedNodes[counterThree].mZ); + float tempDist = npcPos.squaredDistance(nodePos); + if(tempDist < closestNode) + index = counterThree; + } + mCurrentNode = mAllowedNodes[index]; + mAllowedNodes.erase(mAllowedNodes.begin() + index); + } + + if(mAllowedNodes.empty()) + mDistance = 0; + } + } + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; + + 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 AiWander. + // FIXME: This *should* pause the AiWander package instead of terminating it. + if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / + 2.0 - 200)) + { + stopWalking(actor); + 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 AiWander. + // FIXME: This *should* pause the AiWander package instead of terminating it. + if(sideY * (pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / + 2.0 - 200)) + { + stopWalking(actor); + return true; + } + } + + // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. + if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))) + mDistance = 0; + + if(mChooseAction) { mPlayedIdle = 0; - mIdleNow = false; - mChooseAction = true; - } - } + unsigned short idleRoll = 0; - if(mMoveNow && mDistance) - { - if(!mPathFinder.isPathConstructed()) - { - unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); - Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); - - ESM::Pathgrid::Point dest; - dest.mX = destNodePos[0] + mXCell; - dest.mY = destNodePos[1] + mYCell; - dest.mZ = destNodePos[2]; - - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, 0); - - if(mPathFinder.isPathConstructed()) + for(unsigned int counter = 1; counter < mIdle.size(); counter++) { - // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): - ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; - mAllowedNodes.erase(mAllowedNodes.begin() + randNode); - mAllowedNodes.push_back(mCurrentNode); - mCurrentNode = temp; - - mMoveNow = false; - mWalking = true; + unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; + unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); + if(randSelect < idleChance && randSelect > idleRoll) + { + mPlayedIdle = counter; + idleRoll = randSelect; + } + } + + if(!mPlayedIdle && mDistance) + { + mChooseAction = false; + mMoveNow = true; } - // Choose a different node and delete this one from possible nodes because it is uncreachable: else - mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + { + // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: + MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartTime = currentTime; + playIdle(actor, mPlayedIdle + 1); + mChooseAction = false; + mIdleNow = true; + } } - } - if(mWalking) - { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1]); - MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - - // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be - // at the same path node at the same time and both will complete instead of endlessly walking into eachother: - Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ); - Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); - actorPos[0] = actorPos[0] - mXCell; - actorPos[1] = actorPos[1] - mYCell; - float distance = actorPos.squaredDistance(destNodePos); - - if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + if(mIdleNow) { - stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; + if(!checkIdle(actor, mPlayedIdle + 1)) + { + mPlayedIdle = 0; + mIdleNow = false; + mChooseAction = true; + } } + + if(mMoveNow && mDistance) + { + if(!mPathFinder.isPathConstructed()) + { + unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); + Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); + + ESM::Pathgrid::Point dest; + dest.mX = destNodePos[0] + mXCell; + dest.mY = destNodePos[1] + mYCell; + dest.mZ = destNodePos[2]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false); + + if(mPathFinder.isPathConstructed()) + { + // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): + ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + mAllowedNodes.push_back(mCurrentNode); + mCurrentNode = temp; + + mMoveNow = false; + mWalking = true; + } + // Choose a different node and delete this one from possible nodes because it is uncreachable: + else + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + } + } + + if(mWalking) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be + // at the same path node at the same time and both will complete instead of endlessly walking into eachother: + Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ); + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + actorPos[0] = actorPos[0] - mXCell; + actorPos[1] = actorPos[1] - mYCell; + float distance = actorPos.squaredDistance(destNodePos); + + if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } + } + + return false; } - return false; -} - -int MWMechanics::AiWander::getTypeId() const -{ - return 0; -} - -void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor) -{ - mPathFinder.clearPath(); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; -} - -void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) -{ - if(idleSelect == 2) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); - else if(idleSelect == 3) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); - else if(idleSelect == 4) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1); - else if(idleSelect == 5) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1); - else if(idleSelect == 6) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1); - else if(idleSelect == 7) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1); - else if(idleSelect == 8) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1); - else if(idleSelect == 9) - MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); -} - -bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) -{ - if(idleSelect == 2) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); - else if(idleSelect == 3) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3"); - else if(idleSelect == 4) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4"); - else if(idleSelect == 5) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5"); - else if(idleSelect == 6) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6"); - else if(idleSelect == 7) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7"); - else if(idleSelect == 8) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8"); - else if(idleSelect == 9) - return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9"); - else - return false; + int AiWander::getTypeId() const + { + return 0; + } + + void AiWander::stopWalking(const MWWorld::Ptr& actor) + { + mPathFinder.clearPath(); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + } + + void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) + { + if(idleSelect == 2) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); + else if(idleSelect == 3) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); + else if(idleSelect == 4) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1); + else if(idleSelect == 5) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1); + else if(idleSelect == 6) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1); + else if(idleSelect == 7) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1); + else if(idleSelect == 8) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1); + else if(idleSelect == 9) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); + } + + bool AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) + { + if(idleSelect == 2) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); + else if(idleSelect == 3) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3"); + else if(idleSelect == 4) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4"); + else if(idleSelect == 5) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5"); + else if(idleSelect == 6) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6"); + else if(idleSelect == 7) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7"); + else if(idleSelect == 8) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8"); + else if(idleSelect == 9) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9"); + else + return false; + } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index d3a44e0f9..986595a9a 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -205,7 +205,7 @@ namespace MWMechanics return true; ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(distanceZCorrected(nextPoint, x, y, z) < 20) + if(distanceZCorrected(nextPoint, x, y, z) < 40) { mPath.pop_front(); if(mPath.empty())