1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-01 05:15:33 +00:00

Merge remote-tracking branch 'torben/pathfindingoverhaul'

This commit is contained in:
Marc Zinnschlag 2013-06-01 10:44:23 +02:00
commit f586eef604
6 changed files with 658 additions and 631 deletions

View file

@ -13,8 +13,9 @@ namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.0; if(a > 0)
else return -1.0; return 1.0;
return -1.0;
} }
} }
@ -24,7 +25,9 @@ namespace
TODO: Take account for actors being in different cells. TODO: Take account for actors being in different cells.
*/ */
MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x, float y, float z) namespace 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) : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
mMaxDist = 470; mMaxDist = 470;
@ -41,7 +44,7 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x
} }
} }
MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) 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) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
{ {
mMaxDist = 470; mMaxDist = 470;
@ -59,12 +62,12 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &ce
} }
MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const AiEscort *MWMechanics::AiEscort::clone() const
{ {
return new AiEscort(*this); return new AiEscort(*this);
} }
bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) bool AiEscort::execute (const MWWorld::Ptr& actor)
{ {
// If AiEscort has ran for as long or longer then the duration specified // If AiEscort has ran for as long or longer then the duration specified
// and the duration is not infinite, the package is complete. // and the duration is not infinite, the package is complete.
@ -76,19 +79,19 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
return true; return true;
} }
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; 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 = const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{ {
int sideX = sgn(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. // 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. // 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)) 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -99,7 +102,8 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
int sideY = sgn(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. // 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. // 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)) 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -129,10 +133,10 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; start.mZ = pos.pos[2];
mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true);
} }
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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -143,15 +147,16 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
const float* const followerPos = follower.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos;
double differenceBetween[3]; double differenceBetween[3];
for (short i = 0; i < 3; ++i) for (short counter = 0; counter < 3; counter++)
differenceBetween[i] = (leaderPos[i] - followerPos[i]); differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]);
float distanceBetweenResult = float distanceBetweenResult =
(differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] *
differenceBetween[2]);
if(distanceBetweenResult <= mMaxDist * mMaxDist) 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); MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470; mMaxDist = 470;
@ -167,8 +172,9 @@ bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor)
return false; return false;
} }
int MWMechanics::AiEscort::getTypeId() const int AiEscort::getTypeId() const
{ {
return 2; return 2;
} }
}

View file

@ -2,44 +2,47 @@
#include "movement.hpp" #include "movement.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.; if(a > 0)
else return -1.; return 1.0;
return -1.0;
} }
} }
MWMechanics::AiTravel::AiTravel(float x, float y, float z) namespace MWMechanics
{
AiTravel::AiTravel(float x, float y, float z)
: mX(x),mY(y),mZ(z),mPathFinder() : mX(x),mY(y),mZ(z),mPathFinder()
{ {
} }
MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const AiTravel *MWMechanics::AiTravel::clone() const
{ {
return new AiTravel(*this); return new AiTravel(*this);
} }
bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) 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 = const ESM::Pathgrid *pathgrid =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell); 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) if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{ {
int sideX = sgn(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. //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(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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -49,7 +52,8 @@ bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor)
{ {
int sideY = sgn(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. //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)) 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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
@ -62,6 +66,7 @@ bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor)
cellY = actor.getCell()->mCell->mData.mY; cellY = actor.getCell()->mCell->mData.mY;
float xCell = 0; float xCell = 0;
float yCell = 0; float yCell = 0;
if (actor.getCell()->mCell->isExterior()) if (actor.getCell()->mCell->isExterior())
{ {
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
@ -78,23 +83,25 @@ bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor)
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; start.mZ = pos.pos[2];
mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true);
} }
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; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; 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); MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
return false; return false;
} }
int MWMechanics::AiTravel::getTypeId() const int AiTravel::getTypeId() const
{ {
return 1; return 1;
} }
}

View file

@ -14,12 +14,15 @@ namespace
{ {
float sgn(float a) float sgn(float a)
{ {
if(a > 0) return 1.0; if(a > 0)
else return -1.0; return 1.0;
return -1.0;
} }
} }
MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): namespace MWMechanics
{
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
{ {
for(unsigned short counter = 0; counter < mIdle.size(); counter++) for(unsigned short counter = 0; counter < mIdle.size(); counter++)
@ -49,12 +52,12 @@ MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const
mWalking = false; mWalking = false;
} }
MWMechanics::AiPackage * MWMechanics::AiWander::clone() const AiPackage * MWMechanics::AiWander::clone() const
{ {
return new AiWander(*this); return new AiWander(*this);
} }
bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) bool AiWander::execute (const MWWorld::Ptr& actor)
{ {
if(mDuration) if(mDuration)
{ {
@ -64,7 +67,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
{ {
if(!mRepeat) if(!mRepeat)
{ {
stopWalking(actor, mPathFinder); stopWalking(actor);
return true; return true;
} }
else else
@ -74,7 +77,7 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
{ {
if(!mRepeat) if(!mRepeat)
{ {
stopWalking(actor, mPathFinder); stopWalking(actor);
return true; return true;
} }
else else
@ -114,7 +117,8 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) 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); Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY,
mPathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(mPathgrid->mPoints[counter]); mAllowedNodes.push_back(mPathgrid->mPoints[counter]);
} }
@ -125,7 +129,8 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
unsigned int index = 0; unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{ {
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, mAllowedNodes[counterThree].mZ); Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos); float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode) if(tempDist < closestNode)
index = counterThree; index = counterThree;
@ -147,9 +152,10 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
int sideX = sgn(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. // 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. // 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)) 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; return true;
} }
} }
@ -159,9 +165,10 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
int sideY = sgn(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. // 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. // 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)) 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; return true;
} }
} }
@ -219,12 +226,6 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); 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; ESM::Pathgrid::Point dest;
dest.mX = destNodePos[0] + mXCell; dest.mX = destNodePos[0] + mXCell;
dest.mY = destNodePos[1] + mYCell; dest.mY = destNodePos[1] + mYCell;
@ -235,14 +236,28 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; start.mZ = pos.pos[2];
mPathFinder.buildPath(start,dest,mPathgrid,mXCell,mYCell); 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; 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) 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); MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle,false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
@ -254,32 +269,30 @@ bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor)
actorPos[1] = actorPos[1] - mYCell; actorPos[1] = actorPos[1] - mYCell;
float distance = actorPos.squaredDistance(destNodePos); 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, mPathFinder); stopWalking(actor);
mMoveNow = false; mMoveNow = false;
mWalking = false; mWalking = false;
mChooseAction = true; mChooseAction = true;
} }
} }
return false; return false;
} }
int MWMechanics::AiWander::getTypeId() const int AiWander::getTypeId() const
{ {
return 0; return 0;
} }
void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) void AiWander::stopWalking(const MWWorld::Ptr& actor)
{ {
PathFinder pathClearer; mPathFinder.clearPath();
path = pathClearer;
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
} }
void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{ {
if(idleSelect == 2) if(idleSelect == 2)
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1);
@ -299,7 +312,7 @@ void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short i
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1);
} }
bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) bool AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
{ {
if(idleSelect == 2) if(idleSelect == 2)
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2");
@ -320,4 +333,5 @@ bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short
else else
return false; return false;
} }
}

View file

@ -22,7 +22,7 @@ namespace MWMechanics
///< 0: Wander ///< 0: Wander
private: private:
void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); void stopWalking(const MWWorld::Ptr& actor);
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);

View file

@ -3,164 +3,134 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "OgreMath.h"
#include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/adjacency_list.hpp> #include <boost/graph/adjacency_list.hpp>
#include "boost/tuple/tuple.hpp"
#include "OgreMath.h"
namespace namespace
{ {
//helpers functions struct found_path {};
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, 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; 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 boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
typedef PathGridGraph::vertex_descriptor PointID; 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) {}
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private:
PointID mGoal;
};
class DistanceHeuristic : public boost::atasr_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;
};*/
class goalVisited : public boost::default_dijkstra_visitor class goalVisited : public boost::default_dijkstra_visitor
{ {
public: public:
goalVisited(PointID goal) : mGoal(goal) {} goalVisited(PointID goal) {mGoal = goal;};
void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();};
void examine_vertex(PointID u, const PathGridGraph g)
{
if(u == mGoal)
throw found_path();
}
private: private:
PointID mGoal; PointID mGoal;
}; };
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + 0.1 * z * z);
}
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
{
x -= point.mX;
y -= point.mY;
z -= point.mZ;
return sqrt(x * x + y * y + z * z);
}
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
{
float x = a.mX - b.mX;
float y = a.mY - b.mY;
float z = a.mZ - b.mZ;
return sqrt(x * x + y * y + z * z);
}
static float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z)
{
if(!grid || grid->mPoints.empty())
return -1;
float distanceBetween = distance(grid->mPoints[0], x, y, z);
int closestIndex = 0;
for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++)
{
if(distance(grid->mPoints[counter], x, y, z) < distanceBetween)
{
distanceBetween = distance(grid->mPoints[counter], x, y, z);
closestIndex = counter;
}
}
return closestIndex;
}
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; 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); PointID pID = boost::add_vertex(graph);
graph[pID].mX = pathgrid->mPoints[i].mX + xCell; graph[pID].mX = pathgrid->mPoints[counter].mX + xCell;
graph[pID].mY = pathgrid->mPoints[i].mY + yCell; graph[pID].mY = pathgrid->mPoints[counter].mY + yCell;
graph[pID].mZ = pathgrid->mPoints[i].mZ; graph[pID].mZ = pathgrid->mPoints[counter].mZ;
} }
for(unsigned int i = 0;i<pathgrid->mEdges.size();++i) for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++)
{ {
PointID u = pathgrid->mEdges[i].mV0; PointID u = pathgrid->mEdges[counterTwo].mV0;
PointID v = pathgrid->mEdges[i].mV1; PointID v = pathgrid->mEdges[counterTwo].mV1;
PointConnectionID edge; PointConnectionID edge;
bool done; 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 weightmap = boost::get(boost::edge_weight, graph);
weightmap[edge] = distance(graph[u], graph[v]); weightmap[edge] = distance(graph[u], graph[v]);
} }
return graph; return graph;
} }
std::list<ESM::Pathgrid::Point> findPath(PointID start,PointID end,PathGridGraph graph){ std::list<ESM::Pathgrid::Point> findPath(PointID start, PointID end, PathGridGraph graph)
{
std::vector<PointID> p(boost::num_vertices(graph)); std::vector<PointID> p(boost::num_vertices(graph));
std::vector<float> d(boost::num_vertices(graph)); std::vector<float> d(boost::num_vertices(graph));
std::list<ESM::Pathgrid::Point> shortest_path; std::list<ESM::Pathgrid::Point> shortest_path;
try { try
boost::dijkstra_shortest_paths {
( boost::dijkstra_shortest_paths(graph, start,
graph, boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end)));
start, }
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
);
} catch(found_path fg) { catch(found_path fg)
for(PointID v = end;; v = p[v]) { {
for(PointID v = end; ; v = p[v])
{
shortest_path.push_front(graph[v]); shortest_path.push_front(graph[v]);
if(p[v] == v) if(p[v] == v)
break; break;
} }
} }
return shortest_path; return shortest_path;
} }
//end of helpers functions
} }
namespace MWMechanics namespace MWMechanics
@ -170,55 +140,81 @@ namespace MWMechanics
mIsPathConstructed = false; mIsPathConstructed = false;
} }
void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, void PathFinder::clearPath()
const ESM::Pathgrid* pathGrid,float xCell,float yCell)
{ {
//first check if there is an obstacle if(!mPath.empty())
if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, mPath.clear();
endPoint.mX,endPoint.mY,endPoint.mZ) ) mIsPathConstructed = false;
{ }
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) void PathFinder::buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint,
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;
}
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);
if(startNode != -1 && endNode != -1)
{ {
PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); PathGridGraph graph = buildGraph(pathGrid, xCell, yCell);
mPath = findPath(start,end,graph); mPath = findPath(startNode, endNode, graph);
if(!mPath.empty())
{
mPath.push_back(endPoint);
mIsPathConstructed = true;
} }
} }
}
else
{
mPath.push_back(endPoint); mPath.push_back(endPoint);
mIsPathConstructed = true; mIsPathConstructed = true;
} }
float PathFinder::getZAngleToNext(float x,float y,float z)
{
if(mPath.empty()) if(mPath.empty())
{ mIsPathConstructed = false;
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) float PathFinder::getZAngleToNext(float x, float y)
{
// This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call
// if otherwise).
if(mPath.empty())
return 0;
ESM::Pathgrid::Point nextPoint = *mPath.begin();
float directionX = nextPoint.mX - x;
float directionY = nextPoint.mY - y;
float directionResult = sqrt(directionX * directionX + directionY * directionY);
return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees();
}
bool PathFinder::checkPathCompleted(float x, float y, float z)
{ {
if(mPath.empty()) if(mPath.empty())
{
return true; return true;
}
ESM::Pathgrid::Point nextPoint = *mPath.begin(); ESM::Pathgrid::Point nextPoint = *mPath.begin();
if(distanceZCorrected(nextPoint,x,y,z) < 20) if(distanceZCorrected(nextPoint, x, y, z) < 40)
{ {
mPath.pop_front(); mPath.pop_front();
if(mPath.empty()) if(mPath.empty())
{ {
mIsPathConstructed = false;
return true; return true;
} }
nextPoint = *mPath.begin();
} }
return false; return false;
} }
@ -226,8 +222,10 @@ namespace MWMechanics
{ {
return mPath; return mPath;
} }
bool PathFinder::isPathConstructed() bool PathFinder::isPathConstructed()
{ {
return mIsPathConstructed; return mIsPathConstructed;
} }
} }

View file

@ -11,11 +11,13 @@ namespace MWMechanics
public: public:
PathFinder(); PathFinder();
void clearPath();
void buildPath(ESM::Pathgrid::Point startPoint, ESM::Pathgrid::Point endPoint, 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);//returns true if the last point of the path has been reached. bool checkPathCompleted(float x, float y, float z);
float getZAngleToNext(float x,float y,float z); ///< \Returns true if the last point of the path has been reached.
float getZAngleToNext(float x, float y);
std::list<ESM::Pathgrid::Point> getPath(); std::list<ESM::Pathgrid::Point> getPath();
bool isPathConstructed(); bool isPathConstructed();