diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 405f36767..db2057df3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -13,6 +13,8 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwphysics/collisiontype.hpp" + #include "pathgrid.hpp" #include "creaturestats.hpp" #include "steering.hpp" @@ -45,6 +47,16 @@ namespace MWMechanics std::string("idle9"), }; + namespace + { + inline int getCountBeforeReset(const MWWorld::ConstPtr& actor) + { + if (actor.getClass().isPureWaterCreature(actor) || actor.getClass().isPureFlyingCreature(actor)) + return 1; + return COUNT_BEFORE_RESET; + } + } + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), @@ -298,7 +310,8 @@ namespace MWMechanics const auto currentPosition = actor.getRefData().getPosition().asVec3(); std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here - bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); + const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); + const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor); do { // Determine a random location within radius of original position const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance; @@ -309,22 +322,31 @@ namespace MWMechanics mDestination = osg::Vec3f(destinationX, destinationY, destinationZ); // Check if land creature will walk onto water or if water creature will swim onto land - if ((!isWaterCreature && !destinationIsAtWater(actor, mDestination)) || - (isWaterCreature && !destinationThroughGround(currentPosition, mDestination))) + if (!isWaterCreature && destinationIsAtWater(actor, mDestination)) + continue; + + if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination)) + continue; + + if (isWaterCreature || isFlyingCreature) + { + mPathFinder.buildStraightPath(mDestination); + } + else { const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); - mPathFinder.buildPath(actor, currentPosition, mDestination, actor.getCell(), - getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); - mPathFinder.addPointToPath(mDestination); + mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, + getNavigatorFlags(actor)); + } - if (mPathFinder.isPathConstructed()) - { - storage.setState(AiWanderStorage::Wander_Walking, true); - mHasDestination = true; - mUsePathgrid = false; - } - return; + if (mPathFinder.isPathConstructed()) + { + storage.setState(AiWanderStorage::Wander_Walking, true); + mHasDestination = true; + mUsePathgrid = false; } + + break; } while (--attempts); } @@ -342,8 +364,10 @@ namespace MWMechanics * Returns true if the start to end point travels through a collision point (land). */ bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) { + const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door; return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(), - destination.x(), destination.y(), destination.z()); + destination.x(), destination.y(), destination.z(), + mask); } void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { @@ -479,7 +503,7 @@ namespace MWMechanics } // if stuck for sufficiently long, act like current location was the destination - if (storage.mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset + if (storage.mStuckCount >= getCountBeforeReset(actor)) // something has gone wrong, reset { mObstacleCheck.clear(); stopWalking(actor, storage); diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 3d8fe7919..63167e302 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -2,6 +2,8 @@ #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -121,17 +123,19 @@ namespace MWMechanics * u = how long to move sideways * */ - void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance) + void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) { - const MWWorld::Class& cls = actor.getClass(); - ESM::Position pos = actor.getRefData().getPosition(); + const ESM::Position pos = actor.getRefData().getPosition(); - if(mDistSameSpot == -1) - mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor) * scaleMinimumDistance; - - float distSameSpot = mDistSameSpot * duration; + if (mDistSameSpot == -1) + { + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y()); + } - bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot; + const float distSameSpot = mDistSameSpot * duration; + const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2(); + const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot; // update position mPrevX = pos.pos[0]; diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index d7e582f8c..46c1bc83d 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -30,7 +30,7 @@ namespace MWMechanics bool isEvading() const; // Updates internal state, call each frame for moving actor - void update(const MWWorld::Ptr& actor, float duration, float scaleMinimumDistance = 1.0f); + void update(const MWWorld::Ptr& actor, float duration); // change direction to try to fix "stuck" actor void takeEvasiveAction(MWMechanics::Movement& actorMovement) const; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index e0285be92..db6b8d686 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -269,6 +269,13 @@ namespace MWMechanics mPath.pop_front(); } + void PathFinder::buildStraightPath(const osg::Vec3f& endPoint) + { + mPath.clear(); + mPath.push_back(endPoint); + mConstructed = true; + } + void PathFinder::buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph) { @@ -280,6 +287,16 @@ namespace MWMechanics mConstructed = true; } + void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) + { + mPath.clear(); + + buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + + mConstructed = true; + } + void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 8a5b8338a..1dc85c5e5 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -72,9 +72,14 @@ namespace MWMechanics mCell = nullptr; } + void buildStraightPath(const osg::Vec3f& endPoint); + void buildPathByPathgrid(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); + void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); + void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags);