From db879e77c24b5cf91e34aef984261f145f89587a Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Fri, 5 Sep 2014 17:17:45 +0200
Subject: [PATCH] Handle death in actor update rather than instantly (Fixes
 #1866)

---
 apps/openmw/mwbase/mechanicsmanager.hpp       |  5 ++---
 apps/openmw/mwmechanics/actors.cpp            | 17 ++------------
 apps/openmw/mwmechanics/actors.hpp            |  5 ++---
 apps/openmw/mwmechanics/creaturestats.cpp     | 15 ++++++-------
 apps/openmw/mwmechanics/creaturestats.hpp     |  2 ++
 .../mwmechanics/mechanicsmanagerimp.cpp       | 22 ++++++++++++++-----
 .../mwmechanics/mechanicsmanagerimp.hpp       |  4 +---
 7 files changed, 33 insertions(+), 37 deletions(-)

diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp
index 532c1c3faa..e99f4ca3cd 100644
--- a/apps/openmw/mwbase/mechanicsmanager.hpp
+++ b/apps/openmw/mwbase/mechanicsmanager.hpp
@@ -198,9 +198,8 @@ namespace MWBase
             ///             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;
+            /// Resurrects the player if necessary
+            virtual void keepPlayerAlive() = 0;
     };
 }
 
diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index a039c82327..2f73520162 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -1282,23 +1282,10 @@ namespace MWMechanics
                     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<float> stat (stats.getHealth());
-
-                if (stat.getModified()<1)
-                {
-                    stat.setModified(1, 0);
-                    stats.setHealth(stat);
-                }
-                stats.resurrect();
-                continue;
-            }
-
             if (iter->second->kill())
             {
+                iter->first.getClass().getCreatureStats(iter->first).notifyDied();
+
                 ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
 
                 // Make sure spell effects with CasterLinked flag are removed
diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp
index f893f7596a..55f1719f62 100644
--- a/apps/openmw/mwmechanics/actors.hpp
+++ b/apps/openmw/mwmechanics/actors.hpp
@@ -44,6 +44,8 @@ namespace MWMechanics
 
             void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration);
 
+            void killDeadActors ();
+
         public:
 
             Actors();
@@ -98,9 +100,6 @@ 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 6c0356a040..7a2ece6cad 100644
--- a/apps/openmw/mwmechanics/creaturestats.cpp
+++ b/apps/openmw/mwmechanics/creaturestats.cpp
@@ -188,16 +188,10 @@ namespace MWMechanics
 
         if (index==0 && mDynamic[index].getCurrent()<1)
         {
-            if (!mDead)
-                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();
+            if (MWBase::Environment::get().getWorld()->getGodModeState())
+                MWBase::Environment::get().getMechanicsManager()->keepPlayerAlive();
         }
     }
 
@@ -242,6 +236,11 @@ namespace MWMechanics
         return mDead;
     }
 
+    void CreatureStats::notifyDied()
+    {
+        mDied = true;
+    }
+
     bool CreatureStats::hasDied() const
     {
         return mDied;
diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp
index 037f37cc91..827408e111 100644
--- a/apps/openmw/mwmechanics/creaturestats.hpp
+++ b/apps/openmw/mwmechanics/creaturestats.hpp
@@ -168,6 +168,8 @@ namespace MWMechanics
 
         bool isDead() const;
 
+        void notifyDied();
+
         bool hasDied() const;
 
         void clearHasDied();
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index b6b2c213d6..8547188637 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -664,11 +664,6 @@ 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)
     {
@@ -1303,4 +1298,21 @@ namespace MWMechanics
 
         return (fight >= 100);
     }
+
+    void MechanicsManager::keepPlayerAlive()
+    {
+        MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
+        CreatureStats& stats = player.getClass().getCreatureStats(player);
+        if (stats.isDead())
+        {
+            MWMechanics::DynamicStat<float> stat (stats.getHealth());
+
+            if (stat.getModified()<1)
+            {
+                stat.setModified(1, 0);
+                stats.setHealth(stat);
+            }
+            stats.resurrect();
+        }
+    }
 }
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
index 7dbc6da74f..48553e099f 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp
@@ -162,9 +162,7 @@ namespace MWMechanics
             ///             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();
+            virtual void keepPlayerAlive();
     };
 }