diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index f2b71bd4cc..f718972b93 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -196,6 +196,10 @@ namespace MWBase /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9a4cd44500..809e948e72 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1029,10 +1029,6 @@ namespace MWMechanics iter->second->update(duration); } - // Kill dead actors, update some variables - - int hostilesCount = 0; // need to know this to play Battle music - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); @@ -1048,68 +1044,23 @@ namespace MWMechanics stats.setKnockedDownOneFrame(false); stats.setKnockedDownOverOneFrame(true); } + } + + int hostilesCount = 0; // need to know this to play Battle music + + 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()) { if (stats.isHostile()) hostilesCount++; - - if(iter->second->isDead()) - { - // Actor has been resurrected. Notify the CharacterController and re-enable collision. - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); - iter->second->resurrect(); - } - - if(!stats.isDead()) - continue; - } - - // If it's the player and God Mode is turned on, keep it alive - if (iter->first.getRefData().getHandle()=="player" && - MWBase::Environment::get().getWorld()->getGodModeState()) - { - MWMechanics::DynamicStat stat (stats.getHealth()); - - if (stat.getModified()<1) - { - stat.setModified(1, 0); - stats.setHealth(stat); - } - stats.resurrect(); - continue; - } - - if (iter->second->kill()) - { - ++mDeathCount[cls.getId(iter->first)]; - - // Make sure spell effects with CasterLinked flag are removed - for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) - { - MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); - spells.purge(stats.getActorId()); - } - - // Apply soultrap - if (iter->first.getTypeName() == typeid(ESM::Creature).name()) - { - SoulTrap soulTrap (iter->first); - stats.getActiveSpells().visitEffectSources(soulTrap); - } - - // Reset magic effects and recalculate derived effects - // One case where we need this is to make sure bound items are removed upon death - stats.setMagicEffects(MWMechanics::MagicEffects()); - stats.getActiveSpells().clear(); - calculateCreatureStatModifiers(iter->first, 0); - - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); - - if (cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } + killDeadActors(); + // check if we still have any player enemies to switch music static bool isBattleMusic = false; @@ -1184,6 +1135,74 @@ namespace MWMechanics } } } + + void Actors::killDeadActors() + { + 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()) + { + if(iter->second->isDead()) + { + // Actor has been resurrected. Notify the CharacterController and re-enable collision. + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); + iter->second->resurrect(); + } + + if(!stats.isDead()) + continue; + } + + // If it's the player and God Mode is turned on, keep it alive + if (iter->first.getRefData().getHandle()=="player" && + MWBase::Environment::get().getWorld()->getGodModeState()) + { + MWMechanics::DynamicStat stat (stats.getHealth()); + + if (stat.getModified()<1) + { + stat.setModified(1, 0); + stats.setHealth(stat); + } + stats.resurrect(); + continue; + } + + if (iter->second->kill()) + { + ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; + + // Make sure spell effects with CasterLinked flag are removed + for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(stats.getActorId()); + } + + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + stats.getActiveSpells().clear(); + calculateCreatureStatModifiers(iter->first, 0); + + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); + + if (cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } + } + } + void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4784162f48..cda6abaeaa 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -96,6 +96,9 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + ///@see MechanicsManager::killDeadActors + void killDeadActors (); + void forceStateUpdate(const MWWorld::Ptr &ptr); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index af35a109a6..71217dbb97 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace MWMechanics { @@ -191,6 +192,12 @@ namespace MWMechanics mDied = true; mDead = true; + + if (mDied) + // Must increase death count immediately. There are scripts that use getDeadCount as reaction to onDeath + // and rely on the increased value. + // Would be more appropriate to use a killActor(actor) function, but we don't have access to the Ptr in CreatureStats. + MWBase::Environment::get().getMechanicsManager()->killDeadActors(); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d7c3e2f56d..2d3bc066f6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -653,6 +653,10 @@ namespace MWMechanics return mActors.countDeaths (id); } + void MechanicsManager::killDeadActors() + { + mActors.killDeadActors(); + } void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 596e6887ec..365e240677 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -160,6 +160,10 @@ namespace MWMechanics /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors(); }; }