From 7697ab37e02890d6a5bdc84dc7cfcd91be87dc9c Mon Sep 17 00:00:00 2001
From: scrawl <scrawl@baseoftrash.de>
Date: Wed, 14 May 2014 06:37:31 +0200
Subject: [PATCH] Fixes #1129: Change summoned creatures to use ActorId

Gracefully handles summoned creatures that are left behind in inactive cells.
---
 apps/openmw/mwmechanics/actors.cpp        | 46 +++++++++++++++++------
 apps/openmw/mwmechanics/creaturestats.hpp | 12 ++++--
 apps/openmw/mwworld/actionteleport.cpp    |  1 -
 3 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp
index 91ef48c092..22d033f408 100644
--- a/apps/openmw/mwmechanics/actors.cpp
+++ b/apps/openmw/mwmechanics/actors.cpp
@@ -38,9 +38,12 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a
 {
     if (bound)
     {
-        MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
-        MWWorld::ActionEquip action(newPtr);
-        action.execute(actor);
+        if (actor.getClass().getContainerStore(actor).count(item) == 0)
+        {
+            MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
+            MWWorld::ActionEquip action(newPtr);
+            action.execute(actor);
+        }
     }
     else
     {
@@ -534,28 +537,49 @@ namespace MWMechanics
                         MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
                         ref.getPtr().getCellRef().mPos = ipos;
 
-                        // TODO: Add AI to follow player and fight for him
+                        MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
+
+                        // Make the summoned creature follow its master and help in fights
                         AiFollow package(ptr.getRefData().getHandle());
-                        MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr);
+                        summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
+                        int creatureActorId = summonedCreatureStats.getActorId();
+
+                        MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
+
                         // TODO: VFX_SummonStart, VFX_SummonEnd
-                        creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
-                            MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
+                        creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
                     }
                 }
                 else
                 {
-                    std::string handle = creatureStats.mSummonedCreatures[it->first];
-                    // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
-                    // plays though, which is a rather lame exploit in vanilla.
-                    MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle);
+                    // Summon lifetime has expired. Try to delete the creature.
+                    int actorId = creatureStats.mSummonedCreatures[it->first];
+                    creatureStats.mSummonedCreatures.erase(it->first);
+
+                    MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
                     if (!ptr.isEmpty())
                     {
+                        // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
+                        // plays though, which is a rather lame exploit in vanilla.
                         MWBase::Environment::get().getWorld()->deleteObject(ptr);
                         creatureStats.mSummonedCreatures.erase(it->first);
                     }
+                    else
+                    {
+                        // We didn't find the creature. It's probably in an inactive cell.
+                        // Add to graveyard so we can delete it when the cell becomes active.
+                        creatureStats.mSummonGraveyard.push_back(actorId);
+                    }
                 }
             }
         }
+
+        for (std::vector<int>::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); ++it)
+        {
+            MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
+            if (!ptr.isEmpty())
+                MWBase::Environment::get().getWorld()->deleteObject(ptr);
+        }
     }
 
     void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr)
diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp
index 7cec26fb84..79686bb978 100644
--- a/apps/openmw/mwmechanics/creaturestats.hpp
+++ b/apps/openmw/mwmechanics/creaturestats.hpp
@@ -223,10 +223,16 @@ namespace MWMechanics
         void setLastHitObject(const std::string &objectid);
         const std::string &getLastHitObject() const;
 
-        // Note, this is just a cache to avoid checking the whole container store every frame TODO: Put it somewhere else?
+        // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves.
+        // TODO: Put it somewhere else?
         std::set<int> mBoundItems;
-        // Same as above
-        std::map<int, std::string> mSummonedCreatures;
+
+        // TODO: store in savegame
+        // TODO: encapsulate?
+        // <ESM::MagicEffect index, actor index>
+        std::map<int, int> mSummonedCreatures;
+        // Contains summoned creatures with an expired lifetime that have not been deleted yet.
+        std::vector<int> mSummonGraveyard;
 
         void writeState (ESM::CreatureStats& state) const;
 
diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp
index 627c05251f..4378e179d7 100644
--- a/apps/openmw/mwworld/actionteleport.cpp
+++ b/apps/openmw/mwworld/actionteleport.cpp
@@ -22,7 +22,6 @@ namespace MWWorld
         std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
         for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
         {
-            std::cout << "teleporting someone!" << (*it).getCellRef().mRefID;
             executeImp(*it);
         }