1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 09:09:42 +00:00

Merge branch 'fix_infidelities' into 'master'

Fix Infidelities quest from Tribunal (#6307)

Closes #6307

See merge request OpenMW/openmw!1248
This commit is contained in:
psi29a 2021-10-01 21:12:15 +00:00
commit bd866cf210
14 changed files with 109 additions and 41 deletions

View file

@ -42,6 +42,7 @@
Bug #6283: Avis Dorsey follows you after her death
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
Bug #6302: Teleporting disabled actor breaks its disabled state
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
Feature #890: OpenMW-CS: Column filtering
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console

View file

@ -653,7 +653,8 @@ namespace MWBase
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors = nullptr) const = 0;
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;

View file

@ -448,7 +448,11 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::
result |= DetourNavigator::Flag_swim;
if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0)
{
result |= DetourNavigator::Flag_walk;
if (getTypeId() == AiPackageTypeId::Travel)
result |= DetourNavigator::Flag_usePathgrid;
}
if (canOpenDoors(actor) && getTypeId() != AiPackageTypeId::Wander)
result |= DetourNavigator::Flag_openDoor;
@ -462,20 +466,31 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P
const DetourNavigator::Flags flags = getNavigatorFlags(actor);
const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim)
costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor));
const float swimSpeed = (flags & DetourNavigator::Flag_swim) == 0
? 0.0f
: actorClass.getSwimSpeed(actor);
if (flags & DetourNavigator::Flag_walk)
const float walkSpeed = [&]
{
float walkCost;
if ((flags & DetourNavigator::Flag_walk) == 0)
return 0.0f;
if (getTypeId() == AiPackageTypeId::Wander)
walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor));
else
walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor));
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost;
costs.mGround = costs.mGround * walkCost;
}
return actorClass.getWalkSpeed(actor);
return actorClass.getRunSpeed(actor);
} ();
const float maxSpeed = std::max(swimSpeed, walkSpeed);
if (maxSpeed == 0)
return costs;
const float swimFactor = swimSpeed / maxSpeed;
const float walkFactor = walkSpeed / maxSpeed;
costs.mWater = divOrMax(costs.mWater, swimFactor);
costs.mDoor = divOrMax(costs.mDoor, walkFactor);
costs.mPathgrid = divOrMax(costs.mPathgrid, walkFactor);
costs.mGround = divOrMax(costs.mGround, walkFactor);
return costs;
}

View file

@ -1,5 +1,7 @@
#include "aitravel.hpp"
#include <algorithm>
#include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp"
@ -23,6 +25,11 @@ 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
@ -70,18 +77,26 @@ namespace MWMechanics
// 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.
int destinationTolerance = 64;
if (distance(actorPos, targetPos) <= destinationTolerance)
if (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed)
{
std::vector<MWWorld::Ptr> targetActors;
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
if (!result.first.isEmpty())
std::vector<MWWorld::Ptr> occupyingActors;
if (isAreaOccupiedByOtherActor(actor, targetPos, &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))
{

View file

@ -52,6 +52,8 @@ namespace MWMechanics
const float mZ;
const bool mHidden;
AiReactionTimer mDestinationCheck;
};
struct AiInternalTravel final : public AiTravel

View file

@ -85,14 +85,6 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor);
}
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination)
{
const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor);
}
void stopMovement(const MWWorld::Ptr& actor)
{
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;

View file

@ -4,6 +4,8 @@
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "movement.hpp"
@ -72,6 +74,15 @@ namespace MWMechanics
return MWWorld::Ptr(); // none found
}
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination,
std::vector<MWWorld::Ptr>* occupyingActors)
{
const auto world = MWBase::Environment::get().getWorld();
const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor);
const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor, occupyingActors);
}
ObstacleCheck::ObstacleCheck()
: mWalkState(WalkState::Initial)
, mStateDuration(0)

View file

@ -3,9 +3,12 @@
#include <osg/Vec3f>
#include <vector>
namespace MWWorld
{
class Ptr;
class ConstPtr;
}
namespace MWMechanics
@ -21,6 +24,9 @@ namespace MWMechanics
/** \return Pointer to the door, or empty pointer if none exists **/
const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist);
bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination,
std::vector<MWWorld::Ptr>* occupyingActors = nullptr);
class ObstacleCheck
{
public:

View file

@ -397,7 +397,7 @@ namespace MWMechanics
mPath.clear();
}
if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty())
if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty() && (flags & DetourNavigator::Flag_usePathgrid) == 0)
{
status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents,
flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath));

View file

@ -22,28 +22,36 @@ namespace MWPhysics
return nearest.distance(position) < radius;
}
template <class OnCollision>
class HasSphereCollisionCallback final : public btBroadphaseAabbCallback
{
public:
HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object,
const int mask, const int group)
const int mask, const int group, OnCollision* onCollision)
: mPosition(position),
mRadius(radius),
mCollisionObject(object),
mCollisionFilterMask(mask),
mCollisionFilterGroup(group)
mCollisionFilterGroup(group),
mOnCollision(onCollision)
{
}
bool process(const btBroadphaseProxy* proxy) override
{
if (mResult)
if (mResult && mOnCollision == nullptr)
return false;
const auto collisionObject = static_cast<btCollisionObject*>(proxy->m_clientObject);
if (collisionObject == mCollisionObject)
if (collisionObject == mCollisionObject
|| !needsCollision(*proxy)
|| !testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius))
return true;
if (needsCollision(*proxy))
mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius);
mResult = true;
if (mOnCollision != nullptr)
{
(*mOnCollision)(collisionObject);
return true;
}
return !mResult;
}
@ -58,6 +66,7 @@ namespace MWPhysics
btCollisionObject* mCollisionObject;
int mCollisionFilterMask;
int mCollisionFilterGroup;
OnCollision* mOnCollision;
bool mResult = false;
bool needsCollision(const btBroadphaseProxy& proxy) const

View file

@ -920,7 +920,8 @@ namespace MWPhysics
CollisionType_Actor|CollisionType_Projectile);
}
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const
{
btCollisionObject* object = nullptr;
const auto it = mActors.find(ignore.mRef);
@ -931,7 +932,19 @@ namespace MWPhysics
const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);
const int mask = MWPhysics::CollisionType_Actor;
const int group = 0xff;
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group);
if (occupyingActors == nullptr)
{
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group,
static_cast<void (*)(const btCollisionObject*)>(nullptr));
mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);
return callback.getResult();
}
const auto onCollision = [&] (const btCollisionObject* object)
{
if (PtrHolder* holder = static_cast<PtrHolder*>(object->getUserPointer()))
occupyingActors->push_back(holder->getPtr());
};
HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, &onCollision);
mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);
return callback.getResult();
}

View file

@ -252,7 +252,8 @@ namespace MWPhysics
std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function);
}
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const;
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
void reportCollision(const btVector3& position, const btVector3& normal);

View file

@ -3935,9 +3935,10 @@ namespace MWWorld
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
}
bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const
{
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore);
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors);
}
void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const

View file

@ -735,7 +735,8 @@ namespace MWWorld
bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override;
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override;
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr>* occupyingActors) const override;
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;