From b6044d231a1b6bf7a49faddb5a6e173bfd5c6473 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 27 Aug 2019 22:42:41 +0400 Subject: [PATCH] Handle death event manually before disposing a corpse if a death animation was not finished yet (feature #5146) --- CHANGELOG.md | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwgui/container.cpp | 22 +++++++++++++++++++ apps/openmw/mwmechanics/actors.cpp | 11 +++++++--- apps/openmw/mwmechanics/actors.hpp | 2 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 5 +++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ .../reference/modding/settings/game.rst | 7 ++---- files/ui/advancedpage.ui | 2 +- 9 files changed, 45 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65adf8add..21ce44a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -176,6 +176,7 @@ Feature #5122: Use magic glow for enchanted arrows Feature #5131: Custom skeleton bones Feature #5132: Unique animations for different weapon types + Feature #5146: Safe Dispose corpse Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption Task #4789: Optimize cell transitions diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 5c5821bca..56386d4a1 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -237,6 +237,8 @@ namespace MWBase virtual float getActorsProcessingRange() const = 0; + virtual void notifyDied(const MWWorld::Ptr& actor) = 0; + virtual bool onOpen(const MWWorld::Ptr& ptr) = 0; virtual void onClose(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bfe93f6ed..6a303dfba 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -7,12 +7,15 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwscript/interpretercontext.hpp" + #include "countdialog.hpp" #include "inventorywindow.hpp" @@ -229,7 +232,26 @@ namespace MWGui if (mPtr.getClass().isPersistent(mPtr)) MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); else + { + MWMechanics::CreatureStats& creatureStats = mPtr.getClass().getCreatureStats(mPtr); + + // If we dispose corpse before end of death animation, we should update death counter counter manually. + // Also we should run actor's script - it may react on actor's death. + if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished()) + { + creatureStats.setDeathAnimationFinished(true); + MWBase::Environment::get().getMechanicsManager()->notifyDied(mPtr); + + const std::string script = mPtr.getClass().getScript(mPtr); + if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) + { + MWScript::InterpreterContext interpreterContext (&mPtr.getRefData().getLocals(), mPtr); + MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); + } + } + MWBase::Environment::get().getWorld()->deleteObject(mPtr); + } mPtr = MWWorld::Ptr(); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 929560ef1..ff59ba4b9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1630,6 +1630,13 @@ namespace MWMechanics updateCombatMusic(); } + void Actors::notifyDied(const MWWorld::Ptr &actor) + { + actor.getClass().getCreatureStats(actor).notifyDied(); + + ++mDeathCount[Misc::StringUtils::lowerCase(actor.getCellRef().getRefId())]; + } + void Actors::killDeadActors() { for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) @@ -1673,9 +1680,7 @@ namespace MWMechanics } else if (killResult == CharacterController::Result_DeathAnimJustFinished) { - iter->first.getClass().getCreatureStats(iter->first).notifyDied(); - - ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; + notifyDied(iter->first); // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 07e60e1d1..9621a8619 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -71,6 +71,8 @@ namespace MWMechanics PtrActorMap::const_iterator begin() { return mActors.begin(); } PtrActorMap::const_iterator end() { return mActors.end(); } + void notifyDied(const MWWorld::Ptr &actor); + /// Check if the target actor was detected by an observer /// If the observer is a non-NPC, check all actors in AI processing distance as observers bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fe5f60ec4..7f58ebc46 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -455,6 +455,11 @@ namespace MWMechanics } } + void MechanicsManager::notifyDied(const MWWorld::Ptr& actor) + { + mActors.notifyDied(actor); + } + float MechanicsManager::getActorsProcessingRange() const { return mActors.getProcessingRange(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index a81a6bd75..e0ab039c8 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -211,6 +211,8 @@ namespace MWMechanics virtual float getActorsProcessingRange() const override; + virtual void notifyDied(const MWWorld::Ptr& actor) override; + /// Check if the target actor was detected by an observer /// If the observer is a non-NPC, check all actors in AI processing distance as observers virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) override; diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 4e397e9c9..7bfa60c6c 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -72,13 +72,10 @@ can loot during death animation :Default: True If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, -if they are not in combat. However disposing corpses during death animation is not recommended - -death counter may not be incremented, and this behaviour can break quests. -This is how Morrowind behaves. +if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly. If this setting is false, player has to wait until end of death animation in all cases. -This case is more safe, but makes using of summoned creatures exploit -(looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. +Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation. This setting can be toggled in Advanced tab of the launcher. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index f7575cd2b..152ab6411 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -19,7 +19,7 @@ - <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. This is how original Morrowind behaves.</p><p>If this setting is false, player has to wait until end of death animation in all cases. This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.</p></body></html> + <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html> Can loot during death animation