From 109a3f78a1b35990f11c80336242f1b8b9918921 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 9 Dec 2014 16:02:07 +0100 Subject: [PATCH] Adjust AiFollow distance for groups of multiple followers (Fixes #1637) --- apps/openmw/mwbase/mechanicsmanager.hpp | 1 + apps/openmw/mwmechanics/actors.cpp | 30 ++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 3 ++ apps/openmw/mwmechanics/aifollow.cpp | 35 +++++++++++++++---- apps/openmw/mwmechanics/aifollow.hpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 1 + 7 files changed, 73 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index ce213b316..b7af1cbf7 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -183,6 +183,7 @@ namespace MWBase ///return the list of actors which are following the given actor /**ie AiFollow is active and the target is the actor**/ virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; + virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor) = 0; ///Returns a list of actors who are fighting the given actor within the fAlarmDistance /** ie AiCombat is active and the target is the actor **/ diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a3cfbfd49..ae430e5c6 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1470,6 +1470,36 @@ namespace MWMechanics return list; } + std::list Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor) + { + std::list list; + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); + if (stats.isDead()) + continue; + + // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package + for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) + { + MWWorld::Ptr followTarget = dynamic_cast(*it)->getTarget(); + if (followTarget.isEmpty()) + continue; + if (followTarget == actor) + list.push_back(dynamic_cast(*it)->getFollowIndex()); + else + break; + } + else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + break; + } + } + return list; + } + std::list Actors::getActorsFighting(const MWWorld::Ptr& actor) { std::list list; std::vector neighbors; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 0ccfaad78..a24095c70 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -112,6 +112,9 @@ namespace MWMechanics /**ie AiFollow is active and the target is the actor **/ std::list getActorsFollowing(const MWWorld::Ptr& actor); + /// Get the list of AiFollow::mFollowIndex for all actors following this target + std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); + ///Returns the list of actors which are fighting the given actor /**ie AiCombat is active and the target is the actor **/ std::list getActorsFighting(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index f309dc740..8f3e19b46 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "creaturestats.hpp" @@ -15,28 +16,31 @@ #include "steering.hpp" +int MWMechanics::AiFollow::mFollowIndexCounter = 0; + MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(""), mActorId(-1) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) : mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z) -, mActorRefId(actorId), mCellId(cellId), mActorId(-1) +, mActorRefId(actorId), mCellId(cellId), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded) : mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0) -, mActorRefId(actorId), mCellId(""), mActorId(-1) +, mActorRefId(actorId), mCellId(""), mActorId(-1), mFollowIndex(mFollowIndexCounter++) { + } MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mActorRefId(follow->mTargetId), mActorId(-1), mCellId(follow->mCellId) - , mCommanded(follow->mCommanded) + , mCommanded(follow->mCommanded), mFollowIndex(mFollowIndexCounter++) { } @@ -48,12 +52,24 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, if (target.isEmpty() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) - return true; //Target doesn't exist + return false; // Target is not here right now, wait for it to return actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); ESM::Position pos = actor.getRefData().getPosition(); //position of the actor + float followDistance = 180; + // When there are multiple actors following the same target, they form a group with each group member at 180*(i+1) distance to the target + int i=0; + std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target); + followers.sort(); + for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) + { + if (*it == mFollowIndex) + followDistance *= (i+1); + ++i; + } + if(!mAlwaysFollow) //Update if you only follow for a bit { //Check if we've run out of time @@ -66,7 +82,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + - (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) //Close-ish to final position + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < followDistance*followDistance) //Close-ish to final position { if(actor.getCell()->isExterior()) //Outside? { @@ -84,7 +100,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor, AiState& state, //Set the target destination from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; - if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close + if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < followDistance) //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else { pathTo(actor, dest, duration); //Go to the destination @@ -159,3 +175,8 @@ MWWorld::Ptr MWMechanics::AiFollow::getTarget() else return MWWorld::Ptr(); } + +int MWMechanics::AiFollow::getFollowIndex() const +{ + return mFollowIndex; +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index d5dd42826..23b159b88 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -46,6 +46,8 @@ namespace MWMechanics bool isCommanded() const; + int getFollowIndex() const; + private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ @@ -58,6 +60,9 @@ namespace MWMechanics std::string mActorRefId; int mActorId; std::string mCellId; + int mFollowIndex; + + static int mFollowIndexCounter; }; } #endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 9cafe9b3c..1dbe6f950 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1268,6 +1268,11 @@ namespace MWMechanics return mActors.getActorsFollowing(actor); } + std::list MechanicsManager::getActorsFollowingIndices(const MWWorld::Ptr& actor) + { + return mActors.getActorsFollowingIndices(actor); + } + std::list MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { return mActors.getActorsFighting(actor); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 9f9e85c5a..1eec26c8a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -147,6 +147,7 @@ namespace MWMechanics virtual void getActorsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects); virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); + virtual std::list getActorsFollowingIndices(const MWWorld::Ptr& actor); virtual std::list getActorsFighting(const MWWorld::Ptr& actor);