From bd6716d89d869478f3e4f8b1444463fa1cf7743e Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 19 Jun 2022 22:21:41 +0300 Subject: [PATCH] Give AITravel time to end if the actor is close to the destination --- apps/openmw/mwmechanics/aitravel.cpp | 62 +++++++++++++--------------- apps/openmw/mwmechanics/aitravel.hpp | 2 +- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 5d453c4b44..378099e7d1 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -17,6 +17,8 @@ namespace { + constexpr float TRAVEL_FINISH_TIME = 2.f; + bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) { // Maximum travel distance for vanilla compatibility. @@ -25,24 +27,17 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) return (pos1 - pos2).length2() <= 7168*7168; } - float getActorRadius(const MWWorld::ConstPtr& actor) - { - const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - return std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); - } } namespace MWMechanics { AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*) - : TypedAiPackage(repeat), mX(x), mY(y), mZ(z), mHidden(false) - , mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng()) + : TypedAiPackage(repeat), mX(x), mY(y), mZ(z), mHidden(false), mDestinationTimer(TRAVEL_FINISH_TIME) { } AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived) - : TypedAiPackage(derived), mX(x), mY(y), mZ(z), mHidden(true) - , mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng()) + : TypedAiPackage(derived), mX(x), mY(y), mZ(z), mHidden(true), mDestinationTimer(TRAVEL_FINISH_TIME) { } @@ -53,7 +48,7 @@ namespace MWMechanics AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) : TypedAiPackage(travel->mRepeat), mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false) - , mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng()) + , mDestinationTimer(TRAVEL_FINISH_TIME) { // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type assert(!travel->mHidden); @@ -78,35 +73,34 @@ namespace MWMechanics if (!isWithinMaxRange(targetPos, actorPos)) return mHidden; - // 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 (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed) - { - std::vector occupyingActors; - if (isAreaOccupiedByOtherActor(actor, targetPos, true, &occupyingActors)) - { - const float actorRadius = getActorRadius(actor); - const float distanceToTarget = distance(actorPos, targetPos); - for (const MWWorld::Ptr& other : occupyingActors) - { - const float otherRadius = getActorRadius(other); - const auto [minRadius, maxRadius] = std::minmax(actorRadius, otherRadius); - constexpr float toleranceFactor = 1.25; - if (minRadius * toleranceFactor + maxRadius > distanceToTarget) - { - actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - return true; - } - } - } - } - if (pathTo(actor, targetPos, duration)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return true; } - return false; + + // If we've been close enough to the destination for some time give up like Morrowind. + // The end condition should be pretty much accurate. + // FIXME: But the timing isn't. Right now we're being very generous, + // but Morrowind might stop the actor prematurely under unclear conditions. + + // Note Morrowind uses the halved eye level, but this is close enough. + float dist = distanceIgnoreZ(actorPos, targetPos) - MWBase::Environment::get().getWorld()->getHalfExtents(actor).z(); + const float endTolerance = std::max(64.f, actor.getClass().getCurrentSpeed(actor) * duration); + + // Even if we have entered the threshold, we might have been pushed away. Reset the timer if we're currently too far. + if (dist > endTolerance) + { + mDestinationTimer = TRAVEL_FINISH_TIME; + return false; + } + + mDestinationTimer -= duration; + if (mDestinationTimer > 0) + return false; + + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + return true; } void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 303df7b105..60a68d9cc5 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -53,7 +53,7 @@ namespace MWMechanics const bool mHidden; - AiReactionTimer mDestinationCheck; + float mDestinationTimer; }; struct AiInternalTravel final : public AiTravel