diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2de5e268f..b87bc453c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -206,7 +206,7 @@ namespace MWMechanics if (LOS) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()), ptr); creatureStats.setHostile(true); } } @@ -537,7 +537,7 @@ namespace MWMechanics // TODO: Add AI to follow player and fight for him AiFollow package(ptr.getRefData().getHandle()); - MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package); + MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr); // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle())); @@ -732,7 +732,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(player), ptr); creatureStats.setHostile(true); npcStats.setCrimeId( MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ); } @@ -761,9 +761,9 @@ namespace MWMechanics else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); + creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player)), ptr); else - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(player), ptr); creatureStats.setHostile(true); } } @@ -771,7 +771,7 @@ namespace MWMechanics // if I didn't report a crime was I attacked? else if (creatureStats.getAttacked() && !creatureStats.isHostile()) { - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(player), ptr); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index c67367a6c..2581d52c9 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -116,8 +116,18 @@ void MWMechanics::AiSequence::clear() mPackages.clear(); } -void MWMechanics::AiSequence::stack (const AiPackage& package) +void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) { + if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPersue) + { + // Notify AiWander of our current position so we can return to it after combat finished + for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) + { + if ((*iter)->getTypeId() == AiPackage::TypeIdWander) + static_cast(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos)); + } + } + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); it++) { if(mPackages.front()->getPriority() <= package.getPriority()) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 07b7c898c..cb1b0de02 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -62,7 +62,7 @@ namespace MWMechanics void clear(); ///< Remove all packages. - void stack (const AiPackage& package); + void stack (const AiPackage& package, const MWWorld::Ptr& actor); ///< Add \a package to the front of the sequence (suspends current package) void queue (const AiPackage& package); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index ad94be0eb..972c4553d 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -37,6 +37,8 @@ namespace MWMechanics , mRotate(false) , mTargetAngle(0) , mSaidGreeting(false) + , mHasReturnPosition(false) + , mReturnPosition(0,0,0) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) { @@ -330,6 +332,37 @@ namespace MWMechanics if(mDistance && cellChange) mDistance = 0; + // For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere + if (cellChange) + mHasReturnPosition = false; + if (mDistance == 0 && mHasReturnPosition && Ogre::Vector3(pos.pos).squaredDistance(mReturnPosition) > 20*20) + { + mChooseAction = false; + mIdleNow = false; + + Ogre::Vector3 destNodePos = mReturnPosition; + + ESM::Pathgrid::Point dest; + dest.mX = destNodePos[0]; + dest.mY = destNodePos[1]; + dest.mZ = destNodePos[2]; + + // actor position is already in world co-ordinates + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + // don't take shortcuts for wandering + mPathFinder.buildPath(start, dest, actor.getCell(), false); + + if(mPathFinder.isPathConstructed()) + { + mMoveNow = false; + mWalking = true; + } + } + if(mChooseAction) { mPlayedIdle = 0; @@ -375,7 +408,7 @@ namespace MWMechanics } // Allow interrupting a walking actor to trigger a greeting - if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState())) + if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState() && mDistance)) { // Play a random voice greeting if the player gets too close int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); @@ -586,5 +619,10 @@ namespace MWMechanics else return false; } + + void AiWander::setReturnPosition(const Ogre::Vector3& position) + { + mHasReturnPosition = true; mReturnPosition = position; + } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index fe14abeb6..a789a372e 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -2,8 +2,11 @@ #define GAME_MWMECHANICS_AIWANDER_H #include "aipackage.hpp" + #include +#include + #include "pathfinding.hpp" #include "obstacle.hpp" @@ -22,6 +25,10 @@ namespace MWMechanics virtual int getTypeId() const; ///< 0: Wander + void setReturnPosition (const Ogre::Vector3& position); + ///< Set the position to return to for a stationary (non-wandering) actor, in case + /// another AI package moved the actor elsewhere + private: void stopWalking(const MWWorld::Ptr& actor); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); @@ -38,6 +45,10 @@ namespace MWMechanics float mGreetDistanceReset; float mChance; + bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, + // if we had the actor in the AiWander constructor... + Ogre::Vector3 mReturnPosition; + // Cached current cell location int mCellX; int mCellY; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index a34c5476c..43da111bf 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -48,7 +48,7 @@ namespace MWScript for (unsigned int i=0; igetPtr(targetID, true) )); + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) ), actor); } };