forked from mirror/openmw-tes3mp
move checkWayIsClear to pathfinding; move shortcut logic to separate func (AiPackage::shortcutPath); rework AiPackage::pathTo
This commit is contained in:
parent
c644f15167
commit
c773ed9f9a
9 changed files with 248 additions and 103 deletions
|
@ -34,20 +34,17 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor, AiState& state
|
|||
)
|
||||
return true; //Target doesn't exist
|
||||
|
||||
//Set the target desition from the actor
|
||||
//Set the target destination for the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
if (pathTo(actor, dest, duration, 200)) //Go to the destination
|
||||
{
|
||||
// activate when reached
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
|
||||
|
||||
MWBase::Environment::get().getWorld()->activate(target, actor);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -37,46 +37,6 @@ namespace
|
|||
|
||||
Ogre::Vector3 AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const Ogre::Vector3& vLastTargetPos,
|
||||
float duration, int weapType, float strength);
|
||||
|
||||
float getZAngleToDir(const Ogre::Vector3& dir)
|
||||
{
|
||||
return Ogre::Math::ATan2(dir.x,dir.y).valueDegrees();
|
||||
}
|
||||
|
||||
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
|
||||
{
|
||||
float len = (dirLen > 0.0f)? dirLen : dir.length();
|
||||
return -Ogre::Math::ASin(dir.z / len).valueDegrees();
|
||||
}
|
||||
|
||||
|
||||
const float PATHFIND_Z_REACH = 50.0f;
|
||||
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
||||
const float PATHFIND_CAUTION_DIST = 500.0f;
|
||||
// distance after which actor (failed previously to shortcut) will try again
|
||||
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
|
||||
|
||||
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
||||
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
||||
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
|
||||
{
|
||||
if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH)
|
||||
{
|
||||
Ogre::Vector3 dir = to - from;
|
||||
dir.z = 0;
|
||||
dir.normalise();
|
||||
float verticalOffset = 200; // instead of '200' here we want the height of the actor
|
||||
Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset;
|
||||
|
||||
// cast up-down ray and find height in world space of hit
|
||||
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
|
||||
|
||||
if(std::abs(from.z - h) <= PATHFIND_Z_REACH)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -702,7 +662,7 @@ namespace MWMechanics
|
|||
if (doesPathNeedRecalc(newPathTarget, actor.getCell()->getCell()))
|
||||
{
|
||||
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actor.getRefData().getPosition()));
|
||||
mPathFinder.buildSyncedPath(start, newPathTarget, actor.getCell(), false);
|
||||
mPathFinder.buildSyncedPath(start, newPathTarget, actor.getCell());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,19 +130,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, float duratio
|
|||
//Set the target destination from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < followDistance) //Stop when you get close
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
|
||||
// turn towards target anyway
|
||||
float directionX = target.getRefData().getPosition().pos[0] - actor.getRefData().getPosition().pos[0];
|
||||
float directionY = target.getRefData().getPosition().pos[1] - actor.getRefData().getPosition().pos[1];
|
||||
zTurn(actor, Ogre::Math::ATan2(directionX,directionY), Ogre::Degree(5));
|
||||
}
|
||||
else
|
||||
{
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
pathTo(actor, dest, duration, followDistance); //Go to the destination
|
||||
|
||||
//Check if you're far away
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 450)
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
MWMechanics::AiPackage::~AiPackage() {}
|
||||
|
||||
MWMechanics::AiPackage::AiPackage() : mTimer(0.26f), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild
|
||||
|
||||
MWMechanics::AiPackage::AiPackage() :
|
||||
mTimer(0.26f), mStuckTimer(0), //mTimer starts at .26 to force initial pathbuild
|
||||
mShortcutProhibited(false), mShortcutFailPos()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration)
|
||||
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest, float duration, float destTolerance)
|
||||
{
|
||||
//Update various Timers
|
||||
mTimer += duration; //Update timer
|
||||
|
@ -63,13 +65,51 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
|
|||
|
||||
//***********************
|
||||
/// Checks if you can't get to the end position at all, adds end position to end of path
|
||||
/// Rebuilds path every quarter of a second, in case the target has moved
|
||||
/// Rebuilds path every [AI_REACTION_TIME] seconds, in case the target has moved
|
||||
//***********************
|
||||
if(mTimer > 0.25)
|
||||
|
||||
bool isStuck = distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < actor.getClass().getSpeed(actor)*0.05
|
||||
&& distance(dest, start) > 20;
|
||||
|
||||
float distToNextWaypoint = distance(start, dest);
|
||||
|
||||
bool isDestReached = (distToNextWaypoint <= destTolerance);
|
||||
|
||||
if (!isDestReached && mTimer > AI_REACTION_TIME)
|
||||
{
|
||||
if (doesPathNeedRecalc(dest, cell)) { //Only rebuild path if it's moved
|
||||
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved
|
||||
mPrevDest = dest;
|
||||
bool needPathRecalc = doesPathNeedRecalc(dest, cell);
|
||||
|
||||
bool isWayClear = true;
|
||||
|
||||
if (!needPathRecalc) // TODO: add check if actor is actually shortcutting
|
||||
{
|
||||
isWayClear = checkWayIsClearForActor(start, dest, actor); // check if current shortcut is safe to follow
|
||||
}
|
||||
|
||||
if (!isWayClear || needPathRecalc) // Only rebuild path if the target has moved or can't follow current shortcut
|
||||
{
|
||||
bool destInLOS = false;
|
||||
|
||||
if (isStuck || !isWayClear || !shortcutPath(start, dest, actor, &destInLOS))
|
||||
{
|
||||
mPathFinder.buildSyncedPath(start, dest, actor.getCell());
|
||||
|
||||
// give priority to go directly on target if there is minimal opportunity
|
||||
if (destInLOS && mPathFinder.getPath().size() > 1)
|
||||
{
|
||||
// get point just before dest
|
||||
std::list<ESM::Pathgrid::Point>::const_iterator pPointBeforeDest = mPathFinder.getPath().end();
|
||||
--pPointBeforeDest;
|
||||
--pPointBeforeDest;
|
||||
|
||||
// if start point is closer to the target then last point of path (excluding target itself) then go straight on the target
|
||||
if (distance(start, dest) <= distance(dest, *pPointBeforeDest))
|
||||
{
|
||||
mPathFinder.clearPath();
|
||||
mPathFinder.addPointToPath(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!mPathFinder.getPath().empty()) //Path has points in it
|
||||
|
@ -86,13 +126,22 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
|
|||
//************************
|
||||
/// Checks if you aren't moving; attempts to unstick you
|
||||
//************************
|
||||
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished?
|
||||
if (isDestReached || mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished?
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0; // stop moving
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[2] = 0;
|
||||
|
||||
// turn to destination point
|
||||
zTurn(actor, Ogre::Degree(getZAngleToPoint(start, dest)));
|
||||
smoothTurn(actor, Ogre::Degree(getXAngleToPoint(start, dest)), 0);
|
||||
return true;
|
||||
}
|
||||
else if(mStuckTimer>0.5) //Every half second see if we need to take action to avoid something
|
||||
{
|
||||
/// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason
|
||||
//if(mObstacleCheck.check(actor, duration)) {
|
||||
if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < actor.getClass().getSpeed(actor)*0.05 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care
|
||||
if (isStuck) { //Actually stuck, and far enough away from destination to care
|
||||
// first check if we're walking into a door
|
||||
MWWorld::Ptr door = getNearbyDoor(actor);
|
||||
if(door != MWWorld::Ptr()) // NOTE: checks interior cells only
|
||||
|
@ -119,12 +168,94 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po
|
|||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time
|
||||
}
|
||||
|
||||
// turn to next path point by X,Z axes
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
smoothTurn(actor, Ogre::Degree(mPathFinder.getXAngleToNext(pos.pos[0], pos.pos[1], pos.pos[2])), 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MWMechanics::AiPackage::shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS)
|
||||
{
|
||||
const MWWorld::Class& actorClass = actor.getClass();
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
||||
// check if actor can move along z-axis
|
||||
bool actorCanMoveByZ = (actorClass.canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||
|| world->isFlying(actor);
|
||||
|
||||
// don't use pathgrid when actor can move in 3 dimensions
|
||||
bool isPathClear = actorCanMoveByZ;
|
||||
|
||||
if (!isPathClear
|
||||
&& (!mShortcutProhibited || (PathFinder::MakeOgreVector3(mShortcutFailPos) - PathFinder::MakeOgreVector3(startPoint)).length() >= PATHFIND_SHORTCUT_RETRY_DIST))
|
||||
{
|
||||
// take the direct path only if there aren't any obstacles
|
||||
isPathClear = !MWBase::Environment::get().getWorld()->castRay(
|
||||
static_cast<float>(startPoint.mX), static_cast<float>(startPoint.mY), static_cast<float>(startPoint.mZ),
|
||||
static_cast<float>(endPoint.mX), static_cast<float>(endPoint.mY), static_cast<float>(endPoint.mZ));
|
||||
|
||||
if (destInLOS != NULL) *destInLOS = isPathClear;
|
||||
|
||||
if (!isPathClear)
|
||||
return false;
|
||||
|
||||
// check if an actor can move along the shortcut path
|
||||
isPathClear = checkWayIsClearForActor(startPoint, endPoint, actor);
|
||||
}
|
||||
|
||||
if (isPathClear) // can shortcut the path
|
||||
{
|
||||
mPathFinder.clearPath();
|
||||
mPathFinder.addPointToPath(endPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MWMechanics::AiPackage::checkWayIsClearForActor(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor)
|
||||
{
|
||||
bool actorCanMoveByZ = (actor.getClass().canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||
|| MWBase::Environment::get().getWorld()->isFlying(actor);
|
||||
|
||||
if (actorCanMoveByZ)
|
||||
return true;
|
||||
|
||||
float actorSpeed = actor.getClass().getSpeed(actor);
|
||||
float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
|
||||
Ogre::Real distToTarget = Ogre::Vector3(static_cast<float>(endPoint.mX), static_cast<float>(endPoint.mY), 0).length();
|
||||
|
||||
float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;
|
||||
|
||||
bool isClear = checkWayIsClear(PathFinder::MakeOgreVector3(startPoint), PathFinder::MakeOgreVector3(endPoint), offsetXY);
|
||||
|
||||
// update shortcut prohibit state
|
||||
if (isClear)
|
||||
{
|
||||
if (mShortcutProhibited)
|
||||
{
|
||||
mShortcutProhibited = false;
|
||||
mShortcutFailPos = ESM::Pathgrid::Point();
|
||||
}
|
||||
}
|
||||
if (!isClear)
|
||||
{
|
||||
if (mShortcutFailPos.mX == 0 && mShortcutFailPos.mY == 0 && mShortcutFailPos.mZ == 0)
|
||||
{
|
||||
mShortcutProhibited = true;
|
||||
mShortcutFailPos = startPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return isClear;
|
||||
}
|
||||
|
||||
bool MWMechanics::AiPackage::doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell)
|
||||
{
|
||||
return distance(mPrevDest, dest) > 10;
|
||||
bool needRecalc = distance(mPrevDest, dest) > 10;
|
||||
if (needRecalc)
|
||||
mPrevDest = dest;
|
||||
|
||||
return needRecalc;
|
||||
}
|
|
@ -24,6 +24,7 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
const float AI_REACTION_TIME = 0.25f;
|
||||
|
||||
/// \brief Base class for AI packages
|
||||
class AiPackage
|
||||
|
@ -70,7 +71,16 @@ namespace MWMechanics
|
|||
protected:
|
||||
/// Causes the actor to attempt to walk to the specified location
|
||||
/** \return If the actor has arrived at his destination **/
|
||||
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
|
||||
bool pathTo(const MWWorld::Ptr& actor, const ESM::Pathgrid::Point& dest, float duration, float destTolerance = 0.0f);
|
||||
|
||||
/// Check if there aren't any obstacles along the path to make shortcut possible
|
||||
/// If a shortcut is possible then path will be cleared and filled with the destination point.
|
||||
/// \param destInLOS If not NULL function will return ray cast check result
|
||||
/// \return If can shortcut the path
|
||||
bool shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS);
|
||||
|
||||
/// Check if the way to the destination is clear, taking into account actor speed
|
||||
bool checkWayIsClearForActor(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor);
|
||||
|
||||
virtual bool doesPathNeedRecalc(ESM::Pathgrid::Point dest, const ESM::Cell *cell);
|
||||
|
||||
|
@ -83,6 +93,9 @@ namespace MWMechanics
|
|||
|
||||
ESM::Position mStuckPos;
|
||||
ESM::Pathgrid::Point mPrevDest;
|
||||
|
||||
bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt
|
||||
ESM::Pathgrid::Point mShortcutFailPos; // position of last shortcut fail
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -55,14 +55,10 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, AiState& state, float duratio
|
|||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
|
||||
if (pathTo(actor, dest, duration, 100)) {
|
||||
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
|
||||
|
||||
|
|
|
@ -413,7 +413,7 @@ namespace MWMechanics
|
|||
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell());
|
||||
|
||||
if(storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
|
@ -520,7 +520,7 @@ namespace MWMechanics
|
|||
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell());
|
||||
|
||||
if(storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
|
|
|
@ -113,6 +113,49 @@ namespace MWMechanics
|
|||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
float getZAngleToDir(const Ogre::Vector3& dir)
|
||||
{
|
||||
return Ogre::Math::ATan2(dir.x,dir.y).valueDegrees();
|
||||
}
|
||||
|
||||
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen)
|
||||
{
|
||||
float len = (dirLen > 0.0f)? dirLen : dir.length();
|
||||
return -Ogre::Math::ASin(dir.z / len).valueDegrees();
|
||||
}
|
||||
|
||||
float getZAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest)
|
||||
{
|
||||
Ogre::Vector3 dir = PathFinder::MakeOgreVector3(dest) - PathFinder::MakeOgreVector3(origin);
|
||||
return getZAngleToDir(dir);
|
||||
}
|
||||
|
||||
float getXAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest)
|
||||
{
|
||||
Ogre::Vector3 dir = PathFinder::MakeOgreVector3(dest) - PathFinder::MakeOgreVector3(origin);
|
||||
return getXAngleToDir(dir);
|
||||
}
|
||||
|
||||
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
|
||||
{
|
||||
if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH)
|
||||
{
|
||||
Ogre::Vector3 dir = to - from;
|
||||
dir.z = 0;
|
||||
dir.normalise();
|
||||
float verticalOffset = 200; // instead of '200' here we want the height of the actor
|
||||
Ogre::Vector3 _from = from + dir*offsetXY + Ogre::Vector3::UNIT_Z * verticalOffset;
|
||||
|
||||
// cast up-down ray and find height in world space of hit
|
||||
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
|
||||
|
||||
if(std::abs(from.z - h) <= PATHFIND_Z_REACH)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PathFinder::PathFinder()
|
||||
: mPathgrid(NULL),
|
||||
mCell(NULL)
|
||||
|
@ -165,23 +208,10 @@ namespace MWMechanics
|
|||
*/
|
||||
void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint,
|
||||
const ESM::Pathgrid::Point &endPoint,
|
||||
const MWWorld::CellStore* cell,
|
||||
bool allowShortcuts)
|
||||
const MWWorld::CellStore* cell)
|
||||
{
|
||||
mPath.clear();
|
||||
|
||||
if(allowShortcuts)
|
||||
{
|
||||
// if there's a ray cast hit, can't take a direct path
|
||||
if (!MWBase::Environment::get().getWorld()->castRay(
|
||||
static_cast<float>(startPoint.mX), static_cast<float>(startPoint.mY), static_cast<float>(startPoint.mZ),
|
||||
static_cast<float>(endPoint.mX), static_cast<float>(endPoint.mY), static_cast<float>(endPoint.mZ)))
|
||||
{
|
||||
mPath.push_back(endPoint);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(mCell != cell || !mPathgrid)
|
||||
{
|
||||
mCell = cell;
|
||||
|
@ -270,6 +300,19 @@ namespace MWMechanics
|
|||
return Ogre::Math::ATan2(directionX,directionY).valueDegrees();
|
||||
}
|
||||
|
||||
float PathFinder::getXAngleToNext(float x, float y, float z) const
|
||||
{
|
||||
// This should never happen (programmers should have an if statement checking
|
||||
// isPathConstructed that prevents this call if otherwise).
|
||||
if(mPath.empty())
|
||||
return 0.;
|
||||
|
||||
const ESM::Pathgrid::Point &nextPoint = *mPath.begin();
|
||||
Ogre::Vector3 dir = MakeOgreVector3(nextPoint) - Ogre::Vector3(x,y,z);
|
||||
|
||||
return -Ogre::Math::ASin(dir.z / dir.length()).valueDegrees();
|
||||
}
|
||||
|
||||
bool PathFinder::checkPathCompleted(float x, float y, float tolerance)
|
||||
{
|
||||
if(mPath.empty())
|
||||
|
@ -291,19 +334,18 @@ namespace MWMechanics
|
|||
// see header for the rationale
|
||||
void PathFinder::buildSyncedPath(const ESM::Pathgrid::Point &startPoint,
|
||||
const ESM::Pathgrid::Point &endPoint,
|
||||
const MWWorld::CellStore* cell,
|
||||
bool allowShortcuts)
|
||||
const MWWorld::CellStore* cell)
|
||||
{
|
||||
if (mPath.size() < 2)
|
||||
{
|
||||
// if path has one point, then it's the destination.
|
||||
// don't need to worry about bad path for this case
|
||||
buildPath(startPoint, endPoint, cell, allowShortcuts);
|
||||
buildPath(startPoint, endPoint, cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ESM::Pathgrid::Point oldStart(*getPath().begin());
|
||||
buildPath(startPoint, endPoint, cell, allowShortcuts);
|
||||
buildPath(startPoint, endPoint, cell);
|
||||
if (mPath.size() >= 2)
|
||||
{
|
||||
// if 2nd waypoint of new path == 1st waypoint of old,
|
||||
|
|
|
@ -15,8 +15,24 @@ namespace MWWorld
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
float distance(ESM::Pathgrid::Point point, float x, float y, float);
|
||||
float distance(ESM::Pathgrid::Point point, float x, float y, float z);
|
||||
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b);
|
||||
float getZAngleToDir(const Ogre::Vector3& dir);
|
||||
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f);
|
||||
float getZAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest);
|
||||
float getXAngleToPoint(const ESM::Pathgrid::Point &origin, const ESM::Pathgrid::Point &dest);
|
||||
|
||||
const float PATHFIND_Z_REACH = 50.0f;
|
||||
//static const float sMaxSlope = 49.0f; // duplicate as in physicssystem
|
||||
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
||||
const float PATHFIND_CAUTION_DIST = 500.0f;
|
||||
// distance after which actor (failed previously to shortcut) will try again
|
||||
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
|
||||
|
||||
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
||||
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
||||
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY);
|
||||
|
||||
class PathFinder
|
||||
{
|
||||
public:
|
||||
|
@ -39,13 +55,15 @@ namespace MWMechanics
|
|||
void clearPath();
|
||||
|
||||
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
|
||||
const MWWorld::CellStore* cell, bool allowShortcuts = true);
|
||||
const MWWorld::CellStore* cell);
|
||||
|
||||
bool checkPathCompleted(float x, float y, float tolerance=32.f);
|
||||
///< \Returns true if we are within \a tolerance units of the last path point.
|
||||
|
||||
float getZAngleToNext(float x, float y) const;
|
||||
|
||||
float getXAngleToNext(float x, float y, float z) const;
|
||||
|
||||
bool isPathConstructed() const
|
||||
{
|
||||
return !mPath.empty();
|
||||
|
@ -69,9 +87,9 @@ namespace MWMechanics
|
|||
Which results in NPC "running in a circle" back to the just passed waypoint.
|
||||
*/
|
||||
void buildSyncedPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
|
||||
const MWWorld::CellStore* cell, bool allowShortcuts = true);
|
||||
const MWWorld::CellStore* cell);
|
||||
|
||||
void addPointToPath(ESM::Pathgrid::Point &point)
|
||||
void addPointToPath(const ESM::Pathgrid::Point &point)
|
||||
{
|
||||
mPath.push_back(point);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue