diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a..83682c507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Bug #4125: OpenMW logo cropped on bugtracker Bug #4215: OpenMW shows book text after last EOL tag Bug #4221: Characters get stuck in V-shaped terrain + Bug #4230: AiTravel package issues break some Tribunal quests Bug #4251: Stationary NPCs do not return to their position after combat Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 97993e984..a88616625 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -297,7 +297,7 @@ namespace MWBase ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0; ///< cast a Ray and return true if there is an object in the ray path. virtual bool toggleCollisionMode() = 0; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 72e6ced19..8b52b15a4 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -53,7 +53,23 @@ namespace MWMechanics if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3())) return false; - if (pathTo(actor, ESM::Pathgrid::Point(static_cast(mX), static_cast(mY), static_cast(mZ)), duration)) + // 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; + ESM::Pathgrid::Point dest(static_cast(mX), static_cast(mY), static_cast(mZ)); + if (distance(pos.pos, dest) <= destinationTolerance) + { + std::vector targetActors; + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors); + + if (!result.first.isEmpty()) + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + if (pathTo(actor, dest, duration)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return true; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 0591667b7..b1b04f304 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -231,6 +231,28 @@ namespace MWMechanics { mPath = pathgridGraph.aStarSearch(startNode, endNode.first); + // If nearest path node is in opposite direction from second, remove it from path. + // Especially useful for wandering actors, if the nearest node is blocked for some reason. + if (mPath.size() > 1) + { + ESM::Pathgrid::Point secondNode = *(++mPath.begin()); + osg::Vec3f firstNodeVec3f = MakeOsgVec3(mPathgrid->mPoints[startNode]); + osg::Vec3f secondNodeVec3f = MakeOsgVec3(secondNode); + osg::Vec3f toSecondNodeVec3f = secondNodeVec3f - firstNodeVec3f; + osg::Vec3f toStartPointVec3f = startPointInLocalCoords - firstNodeVec3f; + if (toSecondNodeVec3f * toStartPointVec3f > 0) + { + ESM::Pathgrid::Point temp(secondNode); + converter.toWorld(temp); + // Add Z offset since path node can overlap with other objects. + // Also ignore doors in raytesting. + bool isPathClear = !MWBase::Environment::get().getWorld()->castRay( + startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true); + if (isPathClear) + mPath.pop_front(); + } + } + // convert supplied path to world coordinates for (std::list::iterator iter(mPath.begin()); iter != mPath.end(); ++iter) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 66eee963d..c162fd57d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1471,12 +1471,16 @@ namespace MWWorld moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); } - bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); + int mask = MWPhysics::CollisionType_World; + if (!ignoreDoors) + mask |= MWPhysics::CollisionType_Door; + + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), mask); return result.mHit; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 941ab7a96..a2616995a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -400,7 +400,7 @@ namespace MWWorld ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override; + bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override; ///< cast a Ray and return true if there is an object in the ray path. bool toggleCollisionMode() override;