diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 822d64afac..114e011ce1 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -197,7 +197,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y())); smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0); - mObstacleCheck.update(actor, duration); + const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front(); + mObstacleCheck.update(actor, destination, duration); // handle obstacles on the way evadeObstacles(actor); diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 2c131ccaef..6268eaddf2 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -77,89 +77,94 @@ namespace MWMechanics } ObstacleCheck::ObstacleCheck() - : mWalkState(State_Norm) - , mStuckDuration(0) - , mEvadeDuration(0) - , mDistSameSpot(-1) // avoid calculating it each time + : mWalkState(WalkState::Initial) + , mStateDuration(0) , mEvadeDirectionIndex(0) { } void ObstacleCheck::clear() { - mWalkState = State_Norm; - mStuckDuration = 0; - mEvadeDuration = 0; + mWalkState = WalkState::Initial; } bool ObstacleCheck::isEvading() const { - return mWalkState == State_Evade; + return mWalkState == WalkState::Evade; } /* * input - actor, duration (time since last check) * output - true if evasive action needs to be taken * - * Walking state transitions (player greeting check not shown): + * Walking state transitions (player greeting check not shown): * - * MoveNow <------------------------------------+ - * | d| - * | | - * +-> State_Norm <---> State_CheckStuck --> State_Evade - * ^ ^ | f ^ | t ^ | | - * | | | | | | | | - * | +---+ +---+ +---+ | u - * | any < t < u | - * +--------------------------------------------+ + * Initial ----> Norm <--------> CheckStuck -------> Evade ---+ + * ^ ^ | f ^ | t ^ | | + * | | | | | | | | + * | +-+ +---+ +---+ | u + * | any < t < u | + * +---------------------------------------------+ * * f = one reaction time - * d = proximity to a closed door * t = how long before considered stuck * u = how long to move sideways * */ - void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) + void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration) { - const osg::Vec3f pos = actor.getRefData().getPosition().asVec3(); + const auto position = actor.getRefData().getPosition().asVec3(); - if (mDistSameSpot == -1) - mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor); - - const float distSameSpot = mDistSameSpot * duration; - const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot; - - mPrev = pos; - - if (mWalkState != State_Evade) + if (mWalkState == WalkState::Initial) { - if(!samePosition) + mWalkState = WalkState::Norm; + mStateDuration = 0; + mPrev = position; + return; + } + + if (mWalkState != WalkState::Evade) + { + const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) * duration; + const float prevDistance = (destination - mPrev).length(); + const float currentDistance = (destination - position).length(); + const float movedDistance = prevDistance - currentDistance; + + mPrev = position; + + if (movedDistance >= distSameSpot) { - mWalkState = State_Norm; - mStuckDuration = 0; - mEvadeDuration = 0; + mWalkState = WalkState::Norm; + mStateDuration = 0; return; } - mWalkState = State_CheckStuck; - mStuckDuration += duration; - // consider stuck only if position unchanges for a period - if(mStuckDuration < DURATION_SAME_SPOT) - return; // still checking, note duration added to timer - else + if (mWalkState == WalkState::Norm) { - mStuckDuration = 0; - mWalkState = State_Evade; - chooseEvasionDirection(); + mWalkState = WalkState::CheckStuck; + mStateDuration = duration; + return; } + + mStateDuration += duration; + if (mStateDuration < DURATION_SAME_SPOT) + { + return; + } + + mWalkState = WalkState::Evade; + mStateDuration = 0; + chooseEvasionDirection(); + return; } - mEvadeDuration += duration; - if(mEvadeDuration >= DURATION_TO_EVADE) + mStateDuration += duration; + if(mStateDuration >= DURATION_TO_EVADE) { // tried to evade, assume all is ok and start again - mWalkState = State_Norm; - mEvadeDuration = 0; + mWalkState = WalkState::Norm; + mStateDuration = 0; + mPrev = position; } } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 2934ceb1f1..8314031ea2 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -32,30 +32,27 @@ namespace MWMechanics bool isEvading() const; // Updates internal state, call each frame for moving actor - void update(const MWWorld::Ptr& actor, float duration); + void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration); // change direction to try to fix "stuck" actor void takeEvasiveAction(MWMechanics::Movement& actorMovement) const; private: - - // for checking if we're stuck osg::Vec3f mPrev; // directions to try moving in when get stuck static const float evadeDirections[NUM_EVADE_DIRECTIONS][2]; - enum WalkState + enum class WalkState { - State_Norm, - State_CheckStuck, - State_Evade + Initial, + Norm, + CheckStuck, + Evade }; WalkState mWalkState; - float mStuckDuration; // accumulate time here while in same spot - float mEvadeDuration; - float mDistSameSpot; // take account of actor's speed + float mStateDuration; int mEvadeDirectionIndex; void chooseEvasionDirection();