From 22d7d8a46660eb17052fd0bfb1e77ee9599e0de0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 6 Aug 2014 21:16:14 +0200 Subject: [PATCH] Implement Command creature/humanoid magic effects (Fixes #1120) --- apps/openmw/mwmechanics/actors.cpp | 57 +++++++++++++++++++++++++- apps/openmw/mwmechanics/aifollow.cpp | 15 +++++-- apps/openmw/mwmechanics/aifollow.hpp | 5 ++- apps/openmw/mwmechanics/aisequence.cpp | 14 +++++++ apps/openmw/mwmechanics/aisequence.hpp | 2 + components/esm/aisequence.cpp | 3 ++ components/esm/aisequence.hpp | 1 + 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index eb00af7be..bd1fd8794 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -89,6 +89,58 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) return false; } +class CheckActorCommanded : public MWMechanics::EffectSourceVisitor +{ + MWWorld::Ptr mActor; +public: + bool mCommanded; + CheckActorCommanded(MWWorld::Ptr actor) + : mActor(actor) + , mCommanded(false){} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, int casterActorId, + float magnitude, float remainingTime = -1) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc()) + || (key.mId == ESM::MagicEffect::CommandCreature && mActor.getTypeName() == typeid(ESM::Creature).name())) + && casterActorId == player.getClass().getCreatureStats(player).getActorId() + && magnitude >= mActor.getClass().getCreatureStats(mActor).getLevel()) + mCommanded = true; + } +}; + +void adjustCommandedActor (const MWWorld::Ptr& actor) +{ + CheckActorCommanded check(actor); + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + stats.getActiveSpells().visitEffectSources(check); + + bool hasCommandPackage = false; + + std::list::const_iterator it; + for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && + dynamic_cast(*it)->isCommanded()) + { + hasCommandPackage = true; + break; + } + } + + if (check.mCommanded && !hasCommandPackage) + { + MWMechanics::AiFollow package("player", true); + stats.getAiSequence().stack(package, actor); + } + else if (!check.mCommanded && hasCommandPackage) + { + stats.getAiSequence().erase(it); + } +} + void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka) { MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); @@ -1005,7 +1057,10 @@ namespace MWMechanics if (MWBase::Environment::get().getMechanicsManager()->isAIActive()) { if (timerUpdateAITargets == 0) - { + { + if (iter->first != player) + adjustCommandedActor(iter->first); + for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { if (it->first == iter->first || iter->first == player) // player is not AI-controlled diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 27d944657..623306411 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -16,16 +16,16 @@ #include "steering.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("") +: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("") { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId) +: mAlwaysFollow(false), mCommanded(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId) { } -MWMechanics::AiFollow::AiFollow(const std::string &actorId) -: mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("") +MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded) +: mAlwaysFollow(true), mCommanded(commanded), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("") { } @@ -99,6 +99,11 @@ int MWMechanics::AiFollow::getTypeId() const return TypeIdFollow; } +bool MWMechanics::AiFollow::isCommanded() const +{ + return mCommanded; +} + void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr follow(new ESM::AiSequence::AiFollow()); @@ -109,6 +114,7 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co follow->mRemainingDuration = mRemainingDuration; follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; + follow->mCommanded = mCommanded; ESM::AiSequence::AiPackageContainer package; package.mType = ESM::AiSequence::Ai_Follow; @@ -120,6 +126,7 @@ 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) , mActorId(follow->mTargetId), mCellId(follow->mCellId) + , mCommanded(follow->mCommanded) { } diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 744a9c505..c0eef8008 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -27,7 +27,7 @@ namespace MWMechanics /// Follow Actor for duration or until you arrive at a position in a cell AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); /// Follow Actor indefinitively - AiFollow(const std::string &ActorId); + AiFollow(const std::string &ActorId, bool commanded=false); AiFollow(const ESM::AiSequence::AiFollow* follow); @@ -44,10 +44,13 @@ namespace MWMechanics virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + bool isCommanded() const; + private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ bool mAlwaysFollow; + bool mCommanded; float mRemainingDuration; // Seconds float mX; float mY; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 27f6f8a96..f15b357e5 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -82,6 +82,20 @@ std::list::const_iterator AiSequence::end() const return mPackages.end(); } +void AiSequence::erase(std::list::const_iterator package) +{ + // Not sure if manually terminated packages should trigger mDone, probably not? + for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if (package == it) + { + mPackages.erase(it); + return; + } + } + throw std::runtime_error("can't find package to erase"); +} + bool AiSequence::isInCombat() const { for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index af14cce59..c7d176661 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -54,6 +54,8 @@ namespace MWMechanics std::list::const_iterator begin() const; std::list::const_iterator end() const; + void erase (std::list::const_iterator package); + /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ int getTypeId() const; diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp index 80440bdd3..0e3e54102 100644 --- a/components/esm/aisequence.cpp +++ b/components/esm/aisequence.cpp @@ -58,6 +58,8 @@ namespace AiSequence esm.getHNT (mRemainingDuration, "DURA"); mCellId = esm.getHNOString ("CELL"); esm.getHNT (mAlwaysFollow, "ALWY"); + mCommanded = false; + esm.getHNOT (mCommanded, "CMND"); } void AiFollow::save(ESMWriter &esm) const @@ -68,6 +70,7 @@ namespace AiSequence if (!mCellId.empty()) esm.writeHNString ("CELL", mCellId); esm.writeHNT ("ALWY", mAlwaysFollow); + esm.writeHNT ("CMND", mCommanded); } void AiActivate::load(ESMReader &esm) diff --git a/components/esm/aisequence.hpp b/components/esm/aisequence.hpp index bf0e17fa0..da16bf867 100644 --- a/components/esm/aisequence.hpp +++ b/components/esm/aisequence.hpp @@ -96,6 +96,7 @@ namespace ESM float mRemainingDuration; bool mAlwaysFollow; + bool mCommanded; void load(ESMReader &esm); void save(ESMWriter &esm) const;