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:
commit
bd866cf210
14 changed files with 109 additions and 41 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,16 +77,24 @@ 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))
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ namespace MWMechanics
|
|||
const float mZ;
|
||||
|
||||
const bool mHidden;
|
||||
|
||||
AiReactionTimer mDestinationCheck;
|
||||
};
|
||||
|
||||
struct AiInternalTravel final : public AiTravel
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue