1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-24 08:41:37 +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 #6283: Avis Dorsey follows you after her death
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
Bug #6302: Teleporting disabled actor breaks its disabled state 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 #890: OpenMW-CS: Column filtering
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record 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 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 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; 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; result |= DetourNavigator::Flag_swim;
if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0) if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0)
{
result |= DetourNavigator::Flag_walk; result |= DetourNavigator::Flag_walk;
if (getTypeId() == AiPackageTypeId::Travel)
result |= DetourNavigator::Flag_usePathgrid;
}
if (canOpenDoors(actor) && getTypeId() != AiPackageTypeId::Wander) if (canOpenDoors(actor) && getTypeId() != AiPackageTypeId::Wander)
result |= DetourNavigator::Flag_openDoor; result |= DetourNavigator::Flag_openDoor;
@ -462,20 +466,31 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P
const DetourNavigator::Flags flags = getNavigatorFlags(actor); const DetourNavigator::Flags flags = getNavigatorFlags(actor);
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim) const float swimSpeed = (flags & DetourNavigator::Flag_swim) == 0
costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor)); ? 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) if (getTypeId() == AiPackageTypeId::Wander)
walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor)); return actorClass.getWalkSpeed(actor);
else return actorClass.getRunSpeed(actor);
walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor)); } ();
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost; const float maxSpeed = std::max(swimSpeed, walkSpeed);
costs.mGround = costs.mGround * walkCost;
} 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; return costs;
} }

View file

@ -1,5 +1,7 @@
#include "aitravel.hpp" #include "aitravel.hpp"
#include <algorithm>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include "../mwbase/environment.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; 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
@ -70,18 +77,26 @@ namespace MWMechanics
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor. // 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 we got close to target, check for actors nearby. If they are, finish AI package.
int destinationTolerance = 64; if (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed)
if (distance(actorPos, targetPos) <= destinationTolerance)
{ {
std::vector<MWWorld::Ptr> targetActors; std::vector<MWWorld::Ptr> occupyingActors;
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors); if (isAreaOccupiedByOtherActor(actor, targetPos, &occupyingActors))
{
if (!result.first.isEmpty()) 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; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
return true; return true;
} }
} }
}
}
if (pathTo(actor, targetPos, duration)) if (pathTo(actor, targetPos, duration))
{ {

View file

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

View file

@ -85,14 +85,6 @@ namespace MWMechanics
return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor); 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) void stopMovement(const MWWorld::Ptr& actor)
{ {
actor.getClass().getMovementSettings(actor).mPosition[0] = 0; actor.getClass().getMovementSettings(actor).mPosition[0] = 0;

View file

@ -4,6 +4,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "movement.hpp" #include "movement.hpp"
@ -72,6 +74,15 @@ namespace MWMechanics
return MWWorld::Ptr(); // none found 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() ObstacleCheck::ObstacleCheck()
: mWalkState(WalkState::Initial) : mWalkState(WalkState::Initial)
, mStateDuration(0) , mStateDuration(0)

View file

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

View file

@ -397,7 +397,7 @@ namespace MWMechanics
mPath.clear(); 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, status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents,
flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath)); flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath));

View file

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

View file

@ -920,7 +920,8 @@ namespace MWPhysics
CollisionType_Actor|CollisionType_Projectile); 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; btCollisionObject* object = nullptr;
const auto it = mActors.find(ignore.mRef); const auto it = mActors.find(ignore.mRef);
@ -931,7 +932,19 @@ namespace MWPhysics
const auto aabbMax = bulletPosition + btVector3(radius, radius, radius); const auto aabbMax = bulletPosition + btVector3(radius, radius, radius);
const int mask = MWPhysics::CollisionType_Actor; const int mask = MWPhysics::CollisionType_Actor;
const int group = 0xff; 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); mTaskScheduler->aabbTest(aabbMin, aabbMax, callback);
return callback.getResult(); return callback.getResult();
} }

View file

@ -252,7 +252,8 @@ namespace MWPhysics
std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function); 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 reportStats(unsigned int frameNumber, osg::Stats& stats) const;
void reportCollision(const btVector3& position, const btVector3& normal); void reportCollision(const btVector3& position, const btVector3& normal);

View file

@ -3935,9 +3935,10 @@ namespace MWWorld
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal); 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 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 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; void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;