mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +00:00
Merge branch 'aitravelendtolerance' into 'master'
Give AITravel time to end if the actor is close to the destination (#6495) Closes #6495 See merge request OpenMW/openmw!2036
This commit is contained in:
commit
d2df89b013
@ -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<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false)
|
||||
, mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
|
||||
: TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false), mDestinationTimer(TRAVEL_FINISH_TIME)
|
||||
{
|
||||
}
|
||||
|
||||
AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
|
||||
: TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
|
||||
, mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
|
||||
: TypedAiPackage<AiTravel>(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<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
|
||||
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<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))
|
||||
{
|
||||
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)
|
||||
|
@ -53,7 +53,7 @@ namespace MWMechanics
|
||||
|
||||
const bool mHidden;
|
||||
|
||||
AiReactionTimer mDestinationCheck;
|
||||
float mDestinationTimer;
|
||||
};
|
||||
|
||||
struct AiInternalTravel final : public AiTravel
|
||||
|
Loading…
x
Reference in New Issue
Block a user