From a487295d39544455d754d107675792cf2074c613 Mon Sep 17 00:00:00 2001 From: psi29a Date: Sat, 29 May 2021 20:29:53 +0000 Subject: [PATCH] Merge branch 'fix_new_game_guard' into 'master' Consider time to destination when try to avoid collision See merge request OpenMW/openmw!914 --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++++++++++---- apps/openmw/mwmechanics/actors.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 26 ++++++++++++++++++++++---- apps/openmw/mwmechanics/aipackage.hpp | 5 +++++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4629ad2d3e..0ffcef2f8b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1,5 +1,7 @@ #include "actors.hpp" +#include + #include #include @@ -173,6 +175,15 @@ namespace MWMechanics static const int GREETING_COOLDOWN = 40; // how many updates should pass before NPC can continue movement static const float DECELERATE_DISTANCE = 512.f; + namespace + { + float getTimeToDestination(const AiPackage& package, const osg::Vec3f& position, float speed, float duration, const osg::Vec3f& halfExtents) + { + const auto distanceToNextPathPoint = (package.getNextPathPoint(package.getDestination()) - position).length(); + return (distanceToNextPathPoint - package.getNextPathPointTolerance(speed, duration, halfExtents)) / speed; + } + } + class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor { public: @@ -1764,7 +1775,7 @@ namespace MWMechanics } - void Actors::predictAndAvoidCollisions() + void Actors::predictAndAvoidCollisions(float duration) { if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) return; @@ -1799,7 +1810,8 @@ namespace MWMechanics bool shouldAvoidCollision = isMoving; bool shouldTurnToApproachingActor = !isMoving; MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets). - for (const auto& package : ptr.getClass().getCreatureStats(ptr).getAiSequence()) + const auto& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + for (const auto& package : aiSequence) { if (package->getTypeId() == AiPackageTypeId::Follow) shouldAvoidCollision = true; @@ -1829,6 +1841,10 @@ namespace MWMechanics osg::Vec2f movementCorrection(0, 0); float angleToApproachingActor = 0; + const float timeToDestination = aiSequence.isEmpty() + ? std::numeric_limits::max() + : getTimeToDestination(**aiSequence.begin(), basePos, maxSpeed, duration, halfExtents); + // Iterate through all other actors and predict collisions. for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter) { @@ -1865,7 +1881,7 @@ namespace MWMechanics continue; // No solution; distance is always >= collisionDist. float t = (-vr - std::sqrt(Dh)) / v2; - if (t < 0 || t > timeToCollision) + if (t < 0 || t > timeToCollision || t > timeToDestination) continue; // Check visibility and awareness last as it's expensive. @@ -2075,7 +2091,7 @@ namespace MWMechanics static const bool avoidCollisions = Settings::Manager::getBool("NPCs avoid collisions", "Game"); if (avoidCollisions) - predictAndAvoidCollisions(); + predictAndAvoidCollisions(duration); timerUpdateHeadTrack += duration; timerUpdateEquippedLight += duration; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 2de0728d58..0ae9687578 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -63,7 +63,7 @@ namespace MWMechanics void purgeSpellEffects (int casterActorId); - void predictAndAvoidCollisions(); + void predictAndAvoidCollisions(float duration); public: diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index a7e8e74b0d..0701f42cf3 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -28,6 +28,12 @@ namespace { return divisor == 0 ? std::numeric_limits::max() * std::numeric_limits::epsilon() : dividend / divisor; } + + float getPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) + { + const float actorTolerance = 2 * speed * duration + 1.2 * std::max(halfExtents.x(), halfExtents.y()); + return std::max(MWMechanics::MIN_TOLERANCE, actorTolerance); + } } MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : @@ -98,6 +104,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& return false; } + mLastDestinationTolerance = destTolerance; + const float distToTarget = distance(position, dest); const bool isDestReached = (distToTarget <= destTolerance); const bool actorCanMoveByZ = canActorMoveByZAxis(actor); @@ -148,9 +156,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& } } - const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration - + 1.2 * std::max(halfExtents.x(), halfExtents.y()); - const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); + const float pointTolerance = getPointTolerance(actor.getClass().getMaxSpeed(actor), duration, halfExtents); static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game"); mPathFinder.update(position, pointTolerance, DEFAULT_TOLERANCE, @@ -181,7 +187,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& zTurn(actor, zAngleToNext); smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0); - const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front(); + const auto destination = getNextPathPoint(dest); mObstacleCheck.update(actor, destination, duration); if (smoothMovement) @@ -461,3 +467,15 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P return costs; } + +osg::Vec3f MWMechanics::AiPackage::getNextPathPoint(const osg::Vec3f& destination) const +{ + return mPathFinder.getPath().empty() ? destination : mPathFinder.getPath().front(); +} + +float MWMechanics::AiPackage::getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const +{ + if (mPathFinder.getPathSize() <= 1) + return mLastDestinationTolerance; + return getPointTolerance(speed, duration, halfExtents); +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 5ad73c2da6..6d8af0d92b 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -122,6 +122,10 @@ namespace MWMechanics /// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing. static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest); + osg::Vec3f getNextPathPoint(const osg::Vec3f& destination) const; + + float getNextPathPointTolerance(float speed, float duration, const osg::Vec3f& halfExtents) const; + protected: /// Handles path building and shortcutting with obstacles avoiding /** \return If the actor has arrived at his destination **/ @@ -166,6 +170,7 @@ namespace MWMechanics bool mIsShortcutting; // if shortcutting at the moment bool mShortcutProhibited; // shortcutting may be prohibited after unsuccessful attempt osg::Vec3f mShortcutFailPos; // position of last shortcut fail + float mLastDestinationTolerance = 0; private: bool isNearInactiveCell(osg::Vec3f position);