mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 23:26:40 +00:00 
			
		
		
		
	Give AITravel time to end if the actor is close to the destination
This commit is contained in:
		
							parent
							
								
									a768068db9
								
							
						
					
					
						commit
						bd6716d89d
					
				
					 2 changed files with 29 additions and 35 deletions
				
			
		| 
						 | 
					@ -17,6 +17,8 @@
 | 
				
			||||||
namespace
 | 
					namespace
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constexpr float TRAVEL_FINISH_TIME = 2.f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
 | 
					bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Maximum travel distance for vanilla compatibility.
 | 
					    // 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;
 | 
					    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
 | 
					namespace MWMechanics
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*)
 | 
					    AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*)
 | 
				
			||||||
        : TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false)
 | 
					        : TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false), mDestinationTimer(TRAVEL_FINISH_TIME)
 | 
				
			||||||
        , mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
 | 
					    AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
 | 
				
			||||||
        : TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
 | 
					        : TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true), mDestinationTimer(TRAVEL_FINISH_TIME)
 | 
				
			||||||
        , mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +48,7 @@ namespace MWMechanics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
 | 
					    AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
 | 
				
			||||||
        : TypedAiPackage<AiTravel>(travel->mRepeat), mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
 | 
					        : TypedAiPackage<AiTravel>(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
 | 
					        // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
 | 
				
			||||||
        assert(!travel->mHidden);
 | 
					        assert(!travel->mHidden);
 | 
				
			||||||
| 
						 | 
					@ -78,37 +73,36 @@ namespace MWMechanics
 | 
				
			||||||
        if (!isWithinMaxRange(targetPos, actorPos))
 | 
					        if (!isWithinMaxRange(targetPos, actorPos))
 | 
				
			||||||
            return mHidden;
 | 
					            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<MWWorld::Ptr> 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))
 | 
					        if (pathTo(actor, targetPos, duration))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
 | 
					            actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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;
 | 
					            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)
 | 
					    void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        osg::Vec3f pos(mX, mY, mZ);
 | 
					        osg::Vec3f pos(mX, mY, mZ);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ namespace MWMechanics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const bool mHidden;
 | 
					            const bool mHidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            AiReactionTimer mDestinationCheck;
 | 
					            float mDestinationTimer;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct AiInternalTravel final : public AiTravel
 | 
					    struct AiInternalTravel final : public AiTravel
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue