mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 03:53:52 +00:00
Merge pull request #2012 from Capostrophic/pathfinding
AI packages cleanup
This commit is contained in:
commit
4814e35802
8 changed files with 77 additions and 111 deletions
|
@ -29,14 +29,13 @@ namespace MWMechanics
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
if (target == MWWorld::Ptr() ||
|
// Stop if the target doesn't exist
|
||||||
!target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should check whether the target is currently registered
|
// Really we should be checking whether the target is currently registered with the MechanicsManager
|
||||||
// with the MechanicsManager
|
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
|
||||||
)
|
return true;
|
||||||
return true; //Target doesn't exist
|
|
||||||
|
|
||||||
//Set the target destination for the actor
|
//Set the target destination for the actor
|
||||||
const auto dest = target.getRefData().getPosition().asVec3();
|
const osg::Vec3f dest = target.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
|
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,9 +26,9 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
|
||||||
{
|
{
|
||||||
if (actorClass.getNpcStats(actor).getTimeToStartDrowning() < fHoldBreathTime / 2)
|
if (actorClass.getNpcStats(actor).getTimeToStartDrowning() < fHoldBreathTime / 2)
|
||||||
{
|
{
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||||
|
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
actorClass.getMovementSettings(actor).mPosition[1] = 1;
|
||||||
smoothTurn(actor, -180, 0);
|
smoothTurn(actor, -180, 0);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -83,24 +83,13 @@ namespace MWMechanics
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
|
|
||||||
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false);
|
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false);
|
||||||
const float* const leaderPos = actor.getRefData().getPosition().pos;
|
const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3();
|
||||||
const float* const followerPos = follower.getRefData().getPosition().pos;
|
const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3();
|
||||||
double differenceBetween[3];
|
|
||||||
|
|
||||||
for (short counter = 0; counter < 3; counter++)
|
if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist)
|
||||||
differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]);
|
|
||||||
|
|
||||||
double distanceBetweenResult =
|
|
||||||
(differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] *
|
|
||||||
differenceBetween[2]);
|
|
||||||
|
|
||||||
if (distanceBetweenResult <= mMaxDist * mMaxDist)
|
|
||||||
{
|
{
|
||||||
ESM::Pathgrid::Point point(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ));
|
const osg::Vec3f dest(mX, mY, mZ);
|
||||||
point.mAutogenerated = 0;
|
if (pathTo(actor, dest, duration)) //Returns true on path complete
|
||||||
point.mConnectionNum = 0;
|
|
||||||
point.mUnknown = 0;
|
|
||||||
if (pathTo(actor, osg::Vec3f(mX, mY, mZ), duration)) //Returns true on path complete
|
|
||||||
{
|
{
|
||||||
mRemainingDuration = mDuration;
|
mRemainingDuration = mDuration;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -74,12 +74,12 @@ AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
|
||||||
|
|
||||||
bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr target = getTarget();
|
const MWWorld::Ptr target = getTarget();
|
||||||
|
|
||||||
if (target.isEmpty() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered
|
// Target is not here right now, wait for it to return
|
||||||
// with the MechanicsManager
|
// Really we should be checking whether the target is currently registered with the MechanicsManager
|
||||||
)
|
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
|
||||||
return false; // Target is not here right now, wait for it to return
|
return false;
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
|
@ -94,6 +94,10 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
|
const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
||||||
|
const osg::Vec3f targetDir = targetPos - actorPos;
|
||||||
|
|
||||||
// AiFollow requires the target to be in range and within sight for the initial activation
|
// AiFollow requires the target to be in range and within sight for the initial activation
|
||||||
if (!mActive)
|
if (!mActive)
|
||||||
{
|
{
|
||||||
|
@ -101,9 +105,7 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
|
|
||||||
if (storage.mTimer < 0)
|
if (storage.mTimer < 0)
|
||||||
{
|
{
|
||||||
if ((actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length2()
|
if (targetDir.length2() < 500*500 && MWBase::Environment::get().getWorld()->getLOS(actor, target))
|
||||||
< 500*500
|
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(actor, target))
|
|
||||||
mActive = true;
|
mActive = true;
|
||||||
storage.mTimer = 0.5f;
|
storage.mTimer = 0.5f;
|
||||||
}
|
}
|
||||||
|
@ -111,8 +113,6 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
if (!mActive)
|
if (!mActive)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
|
||||||
|
|
||||||
// The distances below are approximations based on observations of the original engine.
|
// The distances below are approximations based on observations of the original engine.
|
||||||
// If only one actor is following the target, it uses 186.
|
// If only one actor is following the target, it uses 186.
|
||||||
// If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target.
|
// If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target.
|
||||||
|
@ -145,9 +145,8 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pos.pos[0]-mX)*(pos.pos[0]-mX) +
|
osg::Vec3f finalPos(mX, mY, mZ);
|
||||||
(pos.pos[1]-mY)*(pos.pos[1]-mY) +
|
if ((actorPos-finalPos).length2() < followDistance*followDistance) //Close-ish to final position
|
||||||
(pos.pos[2]-mZ)*(pos.pos[2]-mZ) < followDistance*followDistance) //Close-ish to final position
|
|
||||||
{
|
{
|
||||||
if (actor.getCell()->isExterior()) //Outside?
|
if (actor.getCell()->isExterior()) //Outside?
|
||||||
{
|
{
|
||||||
|
@ -162,8 +161,6 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set the target destination from the actor
|
|
||||||
const auto dest = target.getRefData().getPosition().asVec3();
|
|
||||||
|
|
||||||
short baseFollowDistance = followDistance;
|
short baseFollowDistance = followDistance;
|
||||||
short threshold = 30; // to avoid constant switching between moving/stopping
|
short threshold = 30; // to avoid constant switching between moving/stopping
|
||||||
|
@ -172,15 +169,9 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
else
|
else
|
||||||
followDistance += threshold;
|
followDistance += threshold;
|
||||||
|
|
||||||
osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
|
if (targetDir.length2() <= followDistance * followDistance)
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
|
||||||
|
|
||||||
osg::Vec3f dir = targetPos - actorPos;
|
|
||||||
float targetDistSqr = dir.length2();
|
|
||||||
|
|
||||||
if (targetDistSqr <= followDistance * followDistance)
|
|
||||||
{
|
{
|
||||||
float faceAngleRadians = std::atan2(dir.x(), dir.y());
|
float faceAngleRadians = std::atan2(targetDir.x(), targetDir.y());
|
||||||
|
|
||||||
if (!zTurn(actor, faceAngleRadians, osg::DegreesToRadians(45.f)))
|
if (!zTurn(actor, faceAngleRadians, osg::DegreesToRadians(45.f)))
|
||||||
{
|
{
|
||||||
|
@ -191,16 +182,14 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.mMoving = !pathTo(actor, dest, duration, baseFollowDistance); // Go to the destination
|
storage.mMoving = !pathTo(actor, targetPos, duration, baseFollowDistance); // Go to the destination
|
||||||
|
|
||||||
if (storage.mMoving)
|
if (storage.mMoving)
|
||||||
{
|
{
|
||||||
//Check if you're far away
|
//Check if you're far away
|
||||||
float dist = distance(dest, pos.asVec3());
|
if (targetDir.length2() > 450 * 450)
|
||||||
|
|
||||||
if (dist > 450)
|
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
|
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
|
||||||
else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold
|
else if (targetDir.length2() < 325 * 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
|
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,11 +101,11 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
{
|
{
|
||||||
mTimer += duration; //Update timer
|
mTimer += duration; //Update timer
|
||||||
|
|
||||||
const auto position = actor.getRefData().getPosition().asVec3(); //position of the actor
|
const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto halfExtents = world->getHalfExtents(actor);
|
const osg::Vec3f halfExtents = world->getHalfExtents(actor);
|
||||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
{
|
{
|
||||||
if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path
|
if (wasShortcutting || doesPathNeedRecalc(dest, actor.getCell())) // if need to rebuild path
|
||||||
{
|
{
|
||||||
const auto playerHalfExtents = world->getHalfExtents(world->getPlayerPtr());
|
const osg::Vec3f playerHalfExtents = world->getHalfExtents(getPlayer()); // Using player half extents for better performance
|
||||||
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
||||||
playerHalfExtents, getNavigatorFlags(actor));
|
playerHalfExtents, getNavigatorFlags(actor));
|
||||||
mRotateOnTheRunChecks = 3;
|
mRotateOnTheRunChecks = 3;
|
||||||
|
@ -161,7 +161,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
|
|
||||||
if (!mPathFinder.getPath().empty()) //Path has points in it
|
if (!mPathFinder.getPath().empty()) //Path has points in it
|
||||||
{
|
{
|
||||||
const auto& lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
|
const osg::Vec3f& lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
|
||||||
|
|
||||||
if(distance(dest, lastPos) > 100) //End of the path is far from the destination
|
if(distance(dest, lastPos) > 100) //End of the path is far from the destination
|
||||||
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
|
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
|
||||||
|
@ -171,7 +171,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
mTimer = 0;
|
mTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pointTolerance = std::min(actor.getClass().getSpeed(actor), DEFAULT_TOLERANCE);
|
const float pointTolerance = std::min(actor.getClass().getSpeed(actor), DEFAULT_TOLERANCE);
|
||||||
|
|
||||||
mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE);
|
mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE);
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor)
|
||||||
|
|
||||||
void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
|
void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
static float distance = world->getMaxActivationDistance();
|
static float distance = world->getMaxActivationDistance();
|
||||||
|
|
||||||
const MWWorld::Ptr door = getNearbyDoor(actor, distance);
|
const MWWorld::Ptr door = getNearbyDoor(actor, distance);
|
||||||
|
@ -296,11 +296,7 @@ bool MWMechanics::AiPackage::shortcutPath(const osg::Vec3f& startPoint, const os
|
||||||
|
|
||||||
bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor)
|
bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
if (canActorMoveByZAxis(actor))
|
||||||
const bool actorCanMoveByZ = (actor.getClass().canSwim(actor) && world->isSwimming(actor))
|
|
||||||
|| world->isFlying(actor);
|
|
||||||
|
|
||||||
if (actorCanMoveByZ)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const float actorSpeed = actor.getClass().getSpeed(actor);
|
const float actorSpeed = actor.getClass().getSpeed(actor);
|
||||||
|
@ -309,18 +305,17 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
||||||
|
|
||||||
const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;
|
const float offsetXY = distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2;
|
||||||
|
|
||||||
const bool isClear = checkWayIsClear(startPoint, endPoint, offsetXY);
|
|
||||||
|
|
||||||
// update shortcut prohibit state
|
// update shortcut prohibit state
|
||||||
if (isClear)
|
if (checkWayIsClear(startPoint, endPoint, offsetXY))
|
||||||
{
|
{
|
||||||
if (mShortcutProhibited)
|
if (mShortcutProhibited)
|
||||||
{
|
{
|
||||||
mShortcutProhibited = false;
|
mShortcutProhibited = false;
|
||||||
mShortcutFailPos = osg::Vec3f();
|
mShortcutFailPos = osg::Vec3f();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!isClear)
|
else
|
||||||
{
|
{
|
||||||
if (mShortcutFailPos == osg::Vec3f())
|
if (mShortcutFailPos == osg::Vec3f())
|
||||||
{
|
{
|
||||||
|
@ -329,7 +324,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isClear;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell)
|
bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const MWWorld::CellStore* currentCell)
|
||||||
|
@ -399,7 +394,7 @@ bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& act
|
||||||
|
|
||||||
DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::Ptr& actor) const
|
DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::Ptr& actor) const
|
||||||
{
|
{
|
||||||
const auto& actorClass = actor.getClass();
|
const MWWorld::Class& actorClass = actor.getClass();
|
||||||
DetourNavigator::Flags result = DetourNavigator::Flag_none;
|
DetourNavigator::Flags result = DetourNavigator::Flag_none;
|
||||||
|
|
||||||
if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor)))
|
if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor)))
|
||||||
|
@ -416,7 +411,6 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::
|
||||||
|
|
||||||
bool MWMechanics::AiPackage::canActorMoveByZAxis(const MWWorld::Ptr& actor) const
|
bool MWMechanics::AiPackage::canActorMoveByZAxis(const MWWorld::Ptr& actor) const
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
const auto& actorClass = actor.getClass();
|
return (actor.getClass().canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor);
|
||||||
return (actorClass.canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,27 +35,27 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
|
|
||||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow
|
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow
|
||||||
|
|
||||||
if(target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered
|
// Stop if the target doesn't exist
|
||||||
// with the MechanicsManager
|
// Really we should be checking whether the target is currently registered with the MechanicsManager
|
||||||
)
|
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
|
||||||
return true; //Target doesn't exist
|
return true;
|
||||||
|
|
||||||
if (isTargetMagicallyHidden(target))
|
if (isTargetMagicallyHidden(target))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(target.getClass().getCreatureStats(target).isDead())
|
if (target.getClass().getCreatureStats(target).isDead())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
//Set the target desition from the actor
|
//Set the target destination
|
||||||
const auto dest = target.getRefData().getPosition().asVec3();
|
const osg::Vec3f dest = target.getRefData().getPosition().asVec3();
|
||||||
ESM::Position aPos = actor.getRefData().getPosition();
|
const osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
float pathTolerance = 100.0;
|
const float pathTolerance = 100.f;
|
||||||
|
|
||||||
if (pathTo(actor, dest, duration, pathTolerance) &&
|
if (pathTo(actor, dest, duration, pathTolerance) &&
|
||||||
std::abs(dest.z() - aPos.pos[2]) < pathTolerance) // check the true distance in case the target is far away in Z-direction
|
std::abs(dest.z() - actorPos.z()) < pathTolerance) // check the true distance in case the target is far away in Z-direction
|
||||||
{
|
{
|
||||||
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached
|
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -21,7 +21,7 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
|
||||||
{
|
{
|
||||||
// Maximum travel distance for vanilla compatibility.
|
// Maximum travel distance for vanilla compatibility.
|
||||||
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
|
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
|
||||||
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
|
// The specific range below is configurable, but its limit is currently 7168 units. Anything greater will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
|
||||||
float aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange();
|
float aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange();
|
||||||
return (pos1 - pos2).length2() <= aiDistance*aiDistance;
|
return (pos1 - pos2).length2() <= aiDistance*aiDistance;
|
||||||
}
|
}
|
||||||
|
@ -47,18 +47,19 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||||
{
|
{
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
|
const osg::Vec3f targetPos(mX, mY, mZ);
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
if (!isWithinMaxRange(targetPos, actorPos))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
||||||
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
||||||
int destinationTolerance = 64;
|
int destinationTolerance = 64;
|
||||||
if (distance(pos.asVec3(), osg::Vec3f(mX, mY, mZ)) <= destinationTolerance)
|
if (distance(actorPos, targetPos) <= destinationTolerance)
|
||||||
{
|
{
|
||||||
std::vector<MWWorld::Ptr> targetActors;
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
|
||||||
|
@ -70,7 +71,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pathTo(actor, osg::Vec3f(mX, mY, mZ), duration))
|
if (pathTo(actor, targetPos, duration))
|
||||||
{
|
{
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -124,14 +124,13 @@ namespace MWMechanics
|
||||||
*/
|
*/
|
||||||
bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
bool AiWander::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||||
{
|
{
|
||||||
// get or create temporary storage
|
|
||||||
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
|
||||||
|
|
||||||
const MWWorld::CellStore*& currentCell = storage.mCell;
|
|
||||||
MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor);
|
MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor);
|
||||||
if(cStats.isDead() || cStats.getHealth().getCurrent() <= 0)
|
if (cStats.isDead() || cStats.getHealth().getCurrent() <= 0)
|
||||||
return true; // Don't bother with dead actors
|
return true; // Don't bother with dead actors
|
||||||
|
|
||||||
|
// get or create temporary storage
|
||||||
|
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
||||||
|
const MWWorld::CellStore*& currentCell = storage.mCell;
|
||||||
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
||||||
if(!currentCell || cellChange)
|
if(!currentCell || cellChange)
|
||||||
{
|
{
|
||||||
|
@ -159,8 +158,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const osg::Vec3f playerHalfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(getPlayer()); // Using player half extents for better performance
|
||||||
const auto playerHalfExtents = world->getHalfExtents(world->getPlayerPtr());
|
|
||||||
mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),
|
mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),
|
||||||
getPathGridGraph(actor.getCell()), playerHalfExtents, getNavigatorFlags(actor));
|
getPathGridGraph(actor.getCell()), playerHalfExtents, getNavigatorFlags(actor));
|
||||||
}
|
}
|
||||||
|
@ -279,9 +277,7 @@ namespace MWMechanics
|
||||||
if (mHasDestination)
|
if (mHasDestination)
|
||||||
return mDestination;
|
return mDestination;
|
||||||
|
|
||||||
const ESM::Pathgrid::Point currentPosition = actor.getRefData().getPosition().pos;
|
return actor.getRefData().getPosition().asVec3();
|
||||||
const osg::Vec3f currentPositionVec3f = osg::Vec3f(currentPosition.mX, currentPosition.mY, currentPosition.mZ);
|
|
||||||
return currentPositionVec3f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
bool AiWander::isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||||
|
@ -314,18 +310,17 @@ namespace MWMechanics
|
||||||
const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection);
|
const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection);
|
||||||
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
||||||
const float destinationZ = mInitialActorPosition.z();
|
const float destinationZ = mInitialActorPosition.z();
|
||||||
const osg::Vec3f destinationPosition(destinationX, destinationY, destinationZ);
|
|
||||||
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
||||||
|
|
||||||
// Check if land creature will walk onto water or if water creature will swim onto land
|
// Check if land creature will walk onto water or if water creature will swim onto land
|
||||||
if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) ||
|
if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) ||
|
||||||
(isWaterCreature && !destinationThroughGround(currentPosition, mDestination)))
|
(isWaterCreature && !destinationThroughGround(currentPosition, mDestination)))
|
||||||
{
|
{
|
||||||
const auto world = MWBase::Environment::get().getWorld();;
|
// Using player half extents for better performance
|
||||||
const auto playerHalfExtents = world->getHalfExtents(world->getPlayerPtr());
|
const osg::Vec3f playerHalfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(getPlayer());
|
||||||
mPathFinder.buildPath(actor, currentPosition, destinationPosition, actor.getCell(),
|
mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(),
|
||||||
getPathGridGraph(actor.getCell()), playerHalfExtents, getNavigatorFlags(actor));
|
getPathGridGraph(actor.getCell()), playerHalfExtents, getNavigatorFlags(actor));
|
||||||
mPathFinder.addPointToPath(destinationPosition);
|
mPathFinder.addPointToPath(mDestination);
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
|
@ -520,9 +515,9 @@ namespace MWMechanics
|
||||||
// Only say Idle voices when player is in LOS
|
// Only say Idle voices when player is in LOS
|
||||||
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
||||||
// voices going through walls?
|
// voices going through walls?
|
||||||
const ESM::Position& pos = actor.getRefData().getPosition();
|
const osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||||
if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2()
|
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
< 3000 * 3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead
|
if (roll < x && (playerPos - actorPos).length2() < 3000 * 3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
}
|
}
|
||||||
|
@ -541,13 +536,12 @@ namespace MWMechanics
|
||||||
MWWorld::Ptr player = getPlayer();
|
MWWorld::Ptr player = getPlayer();
|
||||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
float playerDistSqr = (playerPos - actorPos).length2();
|
|
||||||
|
|
||||||
int& greetingTimer = storage.mGreetingTimer;
|
int& greetingTimer = storage.mGreetingTimer;
|
||||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||||
if (greetingState == AiWanderStorage::Greet_None)
|
if (greetingState == AiWanderStorage::Greet_None)
|
||||||
{
|
{
|
||||||
if ((playerDistSqr <= helloDistance*helloDistance) &&
|
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
||||||
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||||
greetingTimer++;
|
greetingTimer++;
|
||||||
|
@ -583,7 +577,7 @@ namespace MWMechanics
|
||||||
if (greetingState == AiWanderStorage::Greet_Done)
|
if (greetingState == AiWanderStorage::Greet_Done)
|
||||||
{
|
{
|
||||||
float resetDist = 2 * helloDistance;
|
float resetDist = 2 * helloDistance;
|
||||||
if (playerDistSqr >= resetDist*resetDist)
|
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
||||||
greetingState = AiWanderStorage::Greet_None;
|
greetingState = AiWanderStorage::Greet_None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -605,10 +599,10 @@ namespace MWMechanics
|
||||||
ToWorldCoordinates(dest, storage.mCell->getCell());
|
ToWorldCoordinates(dest, storage.mCell->getCell());
|
||||||
|
|
||||||
// actor position is already in world coordinates
|
// actor position is already in world coordinates
|
||||||
const auto start = actorPos.asVec3();
|
const osg::Vec3f start = actorPos.asVec3();
|
||||||
|
|
||||||
// don't take shortcuts for wandering
|
// don't take shortcuts for wandering
|
||||||
const auto destVec3f = PathFinder::makeOsgVec3(dest);
|
const osg::Vec3f destVec3f = PathFinder::makeOsgVec3(dest);
|
||||||
mPathFinder.buildPathByPathgrid(start, destVec3f, actor.getCell(), getPathGridGraph(actor.getCell()));
|
mPathFinder.buildPathByPathgrid(start, destVec3f, actor.getCell(), getPathGridGraph(actor.getCell()));
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
|
|
Loading…
Reference in a new issue