mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 03:53:52 +00:00
Merge remote-tracking branch 'torben/pathfindingoverhaul'
This commit is contained in:
commit
f586eef604
6 changed files with 658 additions and 631 deletions
|
@ -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,151 +25,156 @@ 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
|
||||||
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
|
|
||||||
{
|
{
|
||||||
mMaxDist = 470;
|
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
|
||||||
|
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
|
||||||
// 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::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<ESM::Pathgrid>().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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mPathFinder.checkIfNextPointReached(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],pos.pos[2]);
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
mMaxDist = 470;
|
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
|
mMaxDist = 470;
|
||||||
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
// The CS Help File states that if a duration is givin, the AI package will run for that long
|
||||||
mMaxDist = 330;
|
// 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;
|
|
||||||
}
|
AiEscort *MWMechanics::AiEscort::clone() const
|
||||||
|
{
|
||||||
int MWMechanics::AiEscort::getTypeId() const
|
return new AiEscort(*this);
|
||||||
{
|
}
|
||||||
return 2;
|
|
||||||
|
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<ESM::Pathgrid>().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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,99 +2,106 @@
|
||||||
|
|
||||||
#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
|
||||||
: mX(x),mY(y),mZ(z),mPathFinder()
|
|
||||||
{
|
{
|
||||||
}
|
AiTravel::AiTravel(float x, float y, float z)
|
||||||
|
: 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)
|
||||||
{
|
{
|
||||||
const ESM::Pathgrid *pathgrid =
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
|
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;
|
||||||
|
const ESM::Pathgrid *pathgrid =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);
|
||||||
|
|
||||||
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 /
|
||||||
if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200))
|
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;
|
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
||||||
return true;
|
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);
|
return 1;
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mPathFinder.isPathConstructed() ||cellChange)
|
|
||||||
{
|
|
||||||
cellX = actor.getCell()->mCell->mData.mX;
|
|
||||||
cellY = actor.getCell()->mCell->mData.mY;
|
|
||||||
float xCell = 0;
|
|
||||||
float yCell = 0;
|
|
||||||
if (actor.getCell()->mCell->isExterior())
|
|
||||||
{
|
|
||||||
xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE;
|
|
||||||
yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Pathgrid::Point dest;
|
|
||||||
dest.mX = mX;
|
|
||||||
dest.mY = mY;
|
|
||||||
dest.mZ = mZ;
|
|
||||||
|
|
||||||
ESM::Pathgrid::Point start;
|
|
||||||
start.mX = pos.pos[0];
|
|
||||||
start.mY = pos.pos[1];
|
|
||||||
start.mZ = pos.pos[2];
|
|
||||||
|
|
||||||
mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2]))
|
|
||||||
{
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int MWMechanics::AiTravel::getTypeId() const
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,310 +14,324 @@ 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
|
||||||
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
|
|
||||||
{
|
{
|
||||||
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
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)
|
||||||
{
|
{
|
||||||
if(mIdle[counter] >= 127 || mIdle[counter] < 0)
|
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
||||||
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<ESM::GameSetting>().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)
|
|
||||||
{
|
{
|
||||||
if(!mRepeat)
|
if(mIdle[counter] >= 127 || mIdle[counter] < 0)
|
||||||
{
|
mIdle[counter] = 0;
|
||||||
stopWalking(actor, mPathFinder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mStartTime = currentTime;
|
|
||||||
}
|
}
|
||||||
else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
|
|
||||||
{
|
|
||||||
if(!mRepeat)
|
|
||||||
{
|
|
||||||
stopWalking(actor, mPathFinder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mStartTime = currentTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
if(mDistance < 0)
|
||||||
|
|
||||||
if(!mStoredAvailableNodes)
|
|
||||||
{
|
|
||||||
mStoredAvailableNodes = true;
|
|
||||||
mPathgrid =
|
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().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;
|
mDistance = 0;
|
||||||
|
if(mDuration < 0)
|
||||||
|
mDuration = 0;
|
||||||
|
if(mDuration == 0)
|
||||||
|
mTimeOfDay = 0;
|
||||||
|
|
||||||
if(mDistance)
|
srand(time(NULL));
|
||||||
{
|
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
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))
|
|
||||||
{
|
|
||||||
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 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))
|
|
||||||
{
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
|
||||||
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;
|
mPlayedIdle = 0;
|
||||||
unsigned short idleRoll = 0;
|
mPathgrid = NULL;
|
||||||
|
mIdleChanceMultiplier =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().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];
|
// End package if duration is complete or mid-night hits:
|
||||||
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
|
MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
if(randSelect < idleChance && randSelect > idleRoll)
|
if(currentTime.getHour() >= mStartTime.getHour() + mDuration)
|
||||||
{
|
{
|
||||||
mPlayedIdle = counter;
|
if(!mRepeat)
|
||||||
idleRoll = randSelect;
|
{
|
||||||
|
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)
|
ESM::Position pos = actor.getRefData().getPosition();
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mIdleNow)
|
if(!mStoredAvailableNodes)
|
||||||
{
|
{
|
||||||
if(!checkIdle(actor, mPlayedIdle + 1))
|
mStoredAvailableNodes = true;
|
||||||
|
mPathgrid =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().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;
|
mPlayedIdle = 0;
|
||||||
mIdleNow = false;
|
unsigned short idleRoll = 0;
|
||||||
mChooseAction = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mMoveNow && mDistance)
|
for(unsigned int counter = 1; counter < mIdle.size(); counter++)
|
||||||
{
|
{
|
||||||
if(!mPathFinder.isPathConstructed())
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mIdleNow)
|
||||||
{
|
{
|
||||||
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
|
if(!checkIdle(actor, mPlayedIdle + 1))
|
||||||
Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ);
|
{
|
||||||
|
mPlayedIdle = 0;
|
||||||
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
mIdleNow = false;
|
||||||
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
|
mChooseAction = true;
|
||||||
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;
|
|
||||||
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);
|
|
||||||
mWalking = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(mWalking)
|
if(mMoveNow && mDistance)
|
||||||
{
|
|
||||||
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]);
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false);
|
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
|
||||||
|
|
||||||
// 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.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2]))
|
|
||||||
{
|
{
|
||||||
stopWalking(actor, mPathFinder);
|
if(!mPathFinder.isPathConstructed())
|
||||||
mMoveNow = false;
|
{
|
||||||
mWalking = false;
|
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
|
||||||
mChooseAction = true;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
|
||||||
int MWMechanics::AiWander::getTypeId() const
|
// 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:
|
||||||
return 0;
|
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);
|
||||||
|
|
||||||
void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path)
|
if(distance < 1200 || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||||
{
|
{
|
||||||
PathFinder pathClearer;
|
stopWalking(actor);
|
||||||
path = pathClearer;
|
mMoveNow = false;
|
||||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
mWalking = false;
|
||||||
}
|
mChooseAction = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS,
|
||||||
|
boost::property<boost::vertex_index_t, int, ESM::Pathgrid::Point>, boost::property<boost::edge_weight_t, float> >
|
||||||
|
PathGridGraph;
|
||||||
|
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
|
||||||
|
typedef PathGridGraph::vertex_descriptor PointID;
|
||||||
|
typedef PathGridGraph::edge_descriptor PointConnectionID;
|
||||||
|
|
||||||
|
class goalVisited : public boost::default_dijkstra_visitor
|
||||||
{
|
{
|
||||||
return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z));
|
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)
|
||||||
|
{
|
||||||
|
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)
|
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)
|
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)
|
static float sgn(float a)
|
||||||
{
|
{
|
||||||
if(a>0) return 1.;
|
if(a > 0)
|
||||||
else return -1.;
|
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 || grid->mPoints.empty())
|
||||||
if(grid->mPoints.empty()) return -1;
|
return -1;
|
||||||
|
|
||||||
float m = distance(grid->mPoints[0],x,y,z);
|
float distanceBetween = distance(grid->mPoints[0], x, y, z);
|
||||||
int i0 = 0;
|
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);
|
distanceBetween = distance(grid->mPoints[counter], x, y, z);
|
||||||
i0 = i;
|
closestIndex = counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i0;
|
|
||||||
|
return closestIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS,
|
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0)
|
||||||
boost::property<boost::vertex_index_t,int,ESM::Pathgrid::Point>,boost::property<boost::edge_weight_t,float> > PathGridGraph;
|
|
||||||
typedef boost::property_map<PathGridGraph, boost::edge_weight_t>::type WeightMap;
|
|
||||||
typedef PathGridGraph::vertex_descriptor PointID;
|
|
||||||
typedef PathGridGraph::edge_descriptor PointConnectionID;
|
|
||||||
|
|
||||||
struct found_path {};
|
|
||||||
|
|
||||||
/*class goalVisited : public boost::default_astar_visitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
goalVisited(PointID goal) : mGoal(goal) {}
|
|
||||||
|
|
||||||
void examine_vertex(PointID u, const PathGridGraph g)
|
|
||||||
{
|
|
||||||
if(u == mGoal)
|
|
||||||
throw found_path();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
PointID mGoal;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DistanceHeuristic : public boost::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
|
|
||||||
{
|
|
||||||
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 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mPath.push_back(endPoint);
|
else
|
||||||
mIsPathConstructed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float PathFinder::getZAngleToNext(float x,float y,float z)
|
|
||||||
{
|
|
||||||
if(mPath.empty())
|
|
||||||
{
|
{
|
||||||
return 0;/// shouldn't happen!
|
mPath.push_back(endPoint);
|
||||||
|
mIsPathConstructed = true;
|
||||||
}
|
}
|
||||||
ESM::Pathgrid::Point nextPoint = *mPath.begin();
|
|
||||||
float dX = nextPoint.mX - x;
|
if(mPath.empty())
|
||||||
float dY = nextPoint.mY - y;
|
mIsPathConstructed = false;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,22 +8,24 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
class PathFinder
|
class PathFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PathFinder();
|
PathFinder();
|
||||||
|
|
||||||
void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint,
|
void clearPath();
|
||||||
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 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();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<ESM::Pathgrid::Point> mPath;
|
std::list<ESM::Pathgrid::Point> mPath;
|
||||||
bool mIsPathConstructed;
|
bool mIsPathConstructed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue