From a6e2f43b7562fdf39afc3ab09ecebcfaba3cb87e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 13 Nov 2013 15:44:43 +0100 Subject: [PATCH] Stop continuous VFX when the effect is no longer active. --- apps/openmw/mwgui/spellicons.cpp | 2 + apps/openmw/mwmechanics/activespells.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 20 +++++- apps/openmw/mwmechanics/character.hpp | 2 + apps/openmw/mwmechanics/spells.hpp | 2 + apps/openmw/mwrender/animation.cpp | 82 ++++++++++++++---------- apps/openmw/mwrender/animation.hpp | 10 ++- components/esm/loadmgef.hpp | 4 +- 9 files changed, 85 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index aa2474fa52..da51f7e8f1 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -23,6 +23,8 @@ namespace MWGui void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize) { + // TODO: Tracking add/remove/expire would be better than force updating every frame + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 3bf10e08fc..88fa57a8f6 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -252,7 +252,7 @@ namespace MWMechanics { const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; - MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, loop, ""); + MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } first = false; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4ed7f71b4a..6cb0dc55a3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -378,7 +378,6 @@ namespace MWMechanics iter->second->update(duration); } } - void Actors::restoreDynamicStats() { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 929e012370..0a13f7c8f3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -513,7 +513,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun effect = store.get().find(effectentry.mEffectID); const ESM::Static* castStatic = store.get().find (effect->mCasting); - mAnimation->addEffect("meshes\\" + castStatic->mModel); + mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex); switch(effectentry.mRange) { @@ -716,6 +716,8 @@ void CharacterController::update(float duration) const MWWorld::Class &cls = MWWorld::Class::get(mPtr); Ogre::Vector3 movement(0.0f); + updateContinuousVfx(); + if(!cls.isActor()) { if(mAnimQueue.size() > 1) @@ -1103,4 +1105,20 @@ void CharacterController::resurrect() mDeathState = CharState_None; } +void CharacterController::updateContinuousVfx() +{ + // Keeping track of when to stop a continuous VFX seems to be very difficult to do inside the spells code, + // as it's extremely spread out (ActiveSpells, Spells, InventoryStore effects, etc...) so we do it here. + + // Stop any effects that are no longer active + std::vector effects; + mAnimation->getLoopingEffects(effects); + + for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) + { + if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).mMagnitude <= 0) + mAnimation->removeEffect(*it); + } +} + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8670b385e3..c82b29b479 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -173,6 +173,8 @@ class CharacterController bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + void updateContinuousVfx(); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index e00ac259f1..8f6a750a46 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -4,6 +4,8 @@ #include #include +#include "../mwworld/ptr.hpp" + namespace ESM { struct Spell; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3435df3939..db2482c934 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -928,37 +928,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) mSkelBase->getAllAnimationStates()->_notifyDirty(); } - - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) - { - NifOgre::ObjectList& objects = it->mObjects; - for(size_t i = 0; i < objects.mControllers.size() ;i++) - { - static_cast (objects.mControllers[i].getSource().get())->addTime(duration); - - objects.mControllers[i].update(); - } - - if (objects.mControllers[0].getSource()->getValue() >= objects.mMaxControllerLength) - { - if (it->mLoop) - { - // Start from the beginning again; carry over the remainder - float remainder = objects.mControllers[0].getSource()->getValue() - objects.mMaxControllerLength; - for(size_t i = 0; i < objects.mControllers.size() ;i++) - { - static_cast (objects.mControllers[i].getSource().get())->resetTime(remainder); - } - } - else - { - destroyObjectList(mInsert->getCreator(), it->mObjects); - it = mEffects.erase(it); - continue; - } - } - ++it; - } + updateEffects(duration); return movement; } @@ -1024,7 +994,7 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } -void Animation::addEffect(const std::string &model, bool loop, const std::string &bonename) +void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename) { // Early out if we already have this effect for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) @@ -1035,6 +1005,7 @@ void Animation::addEffect(const std::string &model, bool loop, const std::string params.mModelName = model; params.mObjects = NifOgre::Loader::createObjects(mInsert, model); params.mLoop = loop; + params.mEffectId = effectId; for(size_t i = 0;i < params.mObjects.mControllers.size();i++) { @@ -1044,11 +1015,11 @@ void Animation::addEffect(const std::string &model, bool loop, const std::string mEffects.push_back(params); } -void Animation::removeEffect(const std::string &model) +void Animation::removeEffect(int effectId) { for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) { - if (it->mModelName == model) + if (it->mEffectId == effectId) { destroyObjectList(mInsert->getCreator(), it->mObjects); mEffects.erase(it); @@ -1057,6 +1028,49 @@ void Animation::removeEffect(const std::string &model) } } +void Animation::getLoopingEffects(std::vector &out) +{ + for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) + { + if (it->mLoop) + out.push_back(it->mEffectId); + } +} + +void Animation::updateEffects(float duration) +{ + for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + NifOgre::ObjectList& objects = it->mObjects; + for(size_t i = 0; i < objects.mControllers.size() ;i++) + { + static_cast (objects.mControllers[i].getSource().get())->addTime(duration); + + objects.mControllers[i].update(); + } + + if (objects.mControllers[0].getSource()->getValue() >= objects.mMaxControllerLength) + { + if (it->mLoop) + { + // Start from the beginning again; carry over the remainder + float remainder = objects.mControllers[0].getSource()->getValue() - objects.mMaxControllerLength; + for(size_t i = 0; i < objects.mControllers.size() ;i++) + { + static_cast (objects.mControllers[i].getSource().get())->resetTime(remainder); + } + } + else + { + destroyObjectList(mInsert->getCreator(), it->mObjects); + it = mEffects.erase(it); + continue; + } + } + ++it; + } +} + ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) : Animation(ptr, ptr.getRefData().getBaseNode()) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b983a59441..11adf0c588 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -113,6 +113,7 @@ protected: { std::string mModelName; // Just here so we don't add the same effect twice NifOgre::ObjectList mObjects; + int mEffectId; bool mLoop; }; @@ -200,15 +201,20 @@ public: /** * @brief Add an effect mesh attached to a bone or the insert scene node * @param model + * @param effectId An ID for this effect. Note that adding the same ID again won't add another effect. * @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true, * you need to remove it manually using removeEffect when the effect should end. * @param bonename Bone to attach to, or empty string to use the scene node instead * @note Will not add an effect twice. */ - void addEffect (const std::string& model, bool loop = false, const std::string& bonename = ""); + void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = ""); + void removeEffect (int effectId); + void getLoopingEffects (std::vector& out); +private: + void updateEffects(float duration); - void removeEffect (const std::string& model); +public: void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index d18507b100..b1047e94af 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -232,7 +232,9 @@ struct MagicEffect SummonBear = 139, SummonBonewolf = 140, SummonCreature04 = 141, - SummonCreature05 = 142 + SummonCreature05 = 142, + + Length }; }; }