diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9813cabff..80253e99c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,8 +5,11 @@
     Bug #1952: Incorrect particle lighting
     Bug #2069: Fireflies in Fireflies invade Morrowind look wrong
     Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
+    Bug #2473: Unable to overstock merchants
     Bug #3676: NiParticleColorModifier isn't applied properly
     Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
+    Bug #3862: Random container contents behave differently than vanilla
+    Bug #3929: Leveled list merchant containers respawn on barter
     Bug #4021: Attributes and skills are not stored as floats
     Bug #4055: Local scripts don't inherit variables from their base record
     Bug #4623: Corprus implementation is incorrect
diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp
index b4b068c91..807f2299b 100644
--- a/apps/openmw/mwclass/container.cpp
+++ b/apps/openmw/mwclass/container.cpp
@@ -31,44 +31,41 @@
 
 namespace MWClass
 {
-    class ContainerCustomData : public MWWorld::CustomData
+    ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)
     {
-    public:
-        MWWorld::ContainerStore mContainerStore;
+        unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max());
+        // setting ownership not needed, since taking items from a container inherits the
+        // container's owner automatically
+        mStore.fillNonRandom(container.mInventory, "", seed);
+    }
 
-        virtual MWWorld::CustomData *clone() const;
-
-        virtual ContainerCustomData& asContainerCustomData()
-        {
-            return *this;
-        }
-        virtual const ContainerCustomData& asContainerCustomData() const
-        {
-            return *this;
-        }
-    };
+    ContainerCustomData::ContainerCustomData(const ESM::InventoryState& inventory)
+    {
+        mStore.readState(inventory);
+    }
 
     MWWorld::CustomData *ContainerCustomData::clone() const
     {
         return new ContainerCustomData (*this);
     }
 
+    ContainerCustomData& ContainerCustomData::asContainerCustomData()
+    {
+        return *this;
+    }
+    const ContainerCustomData& ContainerCustomData::asContainerCustomData() const
+    {
+        return *this;
+    }
+
     void Container::ensureCustomData (const MWWorld::Ptr& ptr) const
     {
         if (!ptr.getRefData().getCustomData())
         {
-            std::unique_ptr<ContainerCustomData> data (new ContainerCustomData);
-
-            MWWorld::LiveCellRef<ESM::Container> *ref =
-                ptr.get<ESM::Container>();
-
-            // setting ownership not needed, since taking items from a container inherits the
-            // container's owner automatically
-            data->mContainerStore.fill(
-                ref->mBase->mInventory, "");
+            MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
 
             // store
-            ptr.getRefData().setCustomData (data.release());
+            ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()).release());
 
             MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
         }
@@ -98,17 +95,6 @@ namespace MWClass
         }
     }
 
-    void Container::restock(const MWWorld::Ptr& ptr) const
-    {
-        MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
-        const ESM::InventoryList& list = ref->mBase->mInventory;
-        MWWorld::ContainerStore& store = getContainerStore(ptr);
-
-        // setting ownership not needed, since taking items from a container inherits the
-        // container's owner automatically
-        store.restock(list, ptr, "");
-    }
-
     void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
     {
         if (!model.empty()) {
@@ -228,12 +214,12 @@ namespace MWClass
         return !name.empty() ? name : ref->mBase->mId;
     }
 
-    MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr)
-        const
+    MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const
     {
         ensureCustomData (ptr);
-
-        return ptr.getRefData().getCustomData()->asContainerCustomData().mContainerStore;
+        auto& data = ptr.getRefData().getCustomData()->asContainerCustomData();
+        data.mStore.mPtr = ptr;
+        return data.mStore;
     }
 
     std::string Container::getScript (const MWWorld::ConstPtr& ptr) const
@@ -253,8 +239,7 @@ namespace MWClass
     bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
     {
         if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
-            return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
-
+            return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();
         return true;
     }
 
@@ -317,28 +302,20 @@ namespace MWClass
         if (!state.mHasCustomState)
             return;
 
-        if (!ptr.getRefData().getCustomData())
-        {
-            // Create a CustomData, but don't fill it from ESM records (not needed)
-            std::unique_ptr<ContainerCustomData> data (new ContainerCustomData);
-            ptr.getRefData().setCustomData (data.release());
-        }
-
-        ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
         const ESM::ContainerState& containerState = state.asContainerState();
-        customData.mContainerStore.readState (containerState.mInventory);
+        ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory).release());
     }
 
     void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
     {
-        if (!ptr.getRefData().getCustomData())
+        const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
+        if (!ptr.getRefData().getCustomData() || !customData.mStore.isResolved())
         {
             state.mHasCustomState = false;
             return;
         }
 
-        const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData();
         ESM::ContainerState& containerState = state.asContainerState();
-        customData.mContainerStore.writeState (containerState.mInventory);
+        customData.mStore.writeState (containerState.mInventory);
     }
 }
diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp
index b84c5787b..0ee549f81 100644
--- a/apps/openmw/mwclass/container.hpp
+++ b/apps/openmw/mwclass/container.hpp
@@ -2,9 +2,32 @@
 #define GAME_MWCLASS_CONTAINER_H
 
 #include "../mwworld/class.hpp"
+#include "../mwworld/containerstore.hpp"
+#include "../mwworld/customdata.hpp"
+
+namespace ESM
+{
+    struct Container;
+    struct InventoryState;
+}
 
 namespace MWClass
 {
+    class ContainerCustomData : public MWWorld::CustomData
+    {
+        MWWorld::ContainerStore mStore;
+    public:
+        ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);
+        ContainerCustomData(const ESM::InventoryState& inventory);
+
+        virtual MWWorld::CustomData *clone() const;
+
+        virtual ContainerCustomData& asContainerCustomData();
+        virtual const ContainerCustomData& asContainerCustomData() const;
+
+        friend class Container;
+    };
+
     class Container : public MWWorld::Class
     {
             void ensureCustomData (const MWWorld::Ptr& ptr) const;
@@ -60,8 +83,6 @@ namespace MWClass
 
             virtual void respawn (const MWWorld::Ptr& ptr) const;
 
-            virtual void restock (const MWWorld::Ptr &ptr) const;
-
             virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
 
             virtual bool useAnim() const;
diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp
index 5c5524a12..8e67498a1 100644
--- a/apps/openmw/mwclass/creature.cpp
+++ b/apps/openmw/mwclass/creature.cpp
@@ -842,14 +842,6 @@ namespace MWClass
         }
     }
 
-    void Creature::restock(const MWWorld::Ptr& ptr) const
-    {
-        MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
-        const ESM::InventoryList& list = ref->mBase->mInventory;
-        MWWorld::ContainerStore& store = getContainerStore(ptr);
-        store.restock(list, ptr, ptr.getCellRef().getRefId());
-    }
-
     int Creature::getBaseFightRating(const MWWorld::ConstPtr &ptr) const
     {
         const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp
index ca991052b..21d25633a 100644
--- a/apps/openmw/mwclass/creature.hpp
+++ b/apps/openmw/mwclass/creature.hpp
@@ -123,8 +123,6 @@ namespace MWClass
 
             virtual void respawn (const MWWorld::Ptr& ptr) const;
 
-            virtual void restock (const MWWorld::Ptr &ptr) const;
-
             virtual int getBaseFightRating(const MWWorld::ConstPtr &ptr) const;
 
             virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const;
diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index f4a711432..0187f2727 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -1401,14 +1401,6 @@ namespace MWClass
         }
     }
 
-    void Npc::restock(const MWWorld::Ptr& ptr) const
-    {
-        MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
-        const ESM::InventoryList& list = ref->mBase->mInventory;
-        MWWorld::ContainerStore& store = getContainerStore(ptr);
-        store.restock(list, ptr, ptr.getCellRef().getRefId());
-    }
-
     int Npc::getBaseFightRating (const MWWorld::ConstPtr& ptr) const
     {
         const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp
index 1ed4e8cae..43340b8d7 100644
--- a/apps/openmw/mwclass/npc.hpp
+++ b/apps/openmw/mwclass/npc.hpp
@@ -158,8 +158,6 @@ namespace MWClass
 
             virtual void respawn (const MWWorld::Ptr& ptr) const;
 
-            virtual void restock (const MWWorld::Ptr& ptr) const;
-
             virtual int getBaseFightRating (const MWWorld::ConstPtr& ptr) const;
 
             virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp
index a68fddca1..810a369d8 100644
--- a/apps/openmw/mwgui/container.cpp
+++ b/apps/openmw/mwgui/container.cpp
@@ -168,6 +168,7 @@ namespace MWGui
 
         if (!mPtr.isEmpty())
             MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
+        resetReference();
     }
 
     void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp
index 0cfa6ebf5..31439f349 100644
--- a/apps/openmw/mwgui/containeritemmodel.cpp
+++ b/apps/openmw/mwgui/containeritemmodel.cpp
@@ -39,22 +39,29 @@ namespace
 
 namespace MWGui
 {
-
 ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSources, const std::vector<MWWorld::Ptr>& worldItems)
-    : mItemSources(itemSources)
-    , mWorldItems(worldItems)
+    : mWorldItems(worldItems)
+    , mTrading(true)
 {
-    assert (!mItemSources.empty());
+    assert (!itemSources.empty());
+    // Tie resolution lifetimes to the ItemModel
+    mItemSources.reserve(itemSources.size());
+    for(const MWWorld::Ptr& source : itemSources)
+    {
+        MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
+        mItemSources.push_back(std::make_pair(source, store.resolveTemporarily()));
+    }
 }
 
-ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
+ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) : mTrading(false)
 {
-    mItemSources.push_back(source);
+    MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
+    mItemSources.push_back(std::make_pair(source, store.resolveTemporarily()));
 }
 
 bool ContainerItemModel::allowedToUseItems() const
 {
-    if (mItemSources.size() == 0)
+    if (mItemSources.empty())
         return true;
 
     MWWorld::Ptr ptr = MWMechanics::getPlayer();
@@ -62,7 +69,7 @@ bool ContainerItemModel::allowedToUseItems() const
 
     // Check if the player is allowed to use items from opened container
     MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
-    return mm->isAllowedToUse(ptr, mItemSources[0], victim);
+    return mm->isAllowedToUse(ptr, mItemSources[0].first, victim);
 }
 
 ItemStack ContainerItemModel::getItem (ModelIndex index)
@@ -93,25 +100,31 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
 
 MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool allowAutoEquip)
 {
-    const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
-    if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
+    auto& source = mItemSources[0];
+    MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
+    if (item.mBase.getContainerStore() == &store)
         throw std::runtime_error("Item to copy needs to be from a different container!");
-    return *source.getClass().getContainerStore(source).add(item.mBase, count, source, allowAutoEquip);
+    return *store.add(item.mBase, count, source.first, allowAutoEquip);
 }
 
 void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
 {
     int toRemove = count;
 
-    for (MWWorld::Ptr& source : mItemSources)
+    for (auto& source : mItemSources)
     {
-        MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
+        MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
 
         for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
         {
             if (stacks(*it, item.mBase))
             {
-                toRemove -= store.remove(*it, toRemove, source);
+                int quantity = it->mRef->mData.getCount(false);
+                // If this is a restocking quantity, just don't remove it
+                if(quantity < 0 && mTrading)
+                    toRemove += quantity;
+                else
+                    toRemove -= store.remove(*it, toRemove, source.first);
                 if (toRemove <= 0)
                     return;
             }
@@ -138,9 +151,9 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
 void ContainerItemModel::update()
 {
     mItems.clear();
-    for (MWWorld::Ptr& source : mItemSources)
+    for (auto& source : mItemSources)
     {
-        MWWorld::ContainerStore& store = source.getClass().getContainerStore(source);
+        MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first);
 
         for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
         {
@@ -194,7 +207,7 @@ bool ContainerItemModel::onDropItem(const MWWorld::Ptr &item, int count)
     if (mItemSources.empty())
         return false;
 
-    MWWorld::Ptr target = mItemSources[0];
+    MWWorld::Ptr target = mItemSources[0].first;
 
     if (target.getTypeName() != typeid(ESM::Container).name())
         return true;
@@ -224,7 +237,7 @@ bool ContainerItemModel::onTakeItem(const MWWorld::Ptr &item, int count)
     if (mItemSources.empty())
         return false;
 
-    MWWorld::Ptr target = mItemSources[0];
+    MWWorld::Ptr target = mItemSources[0].first;
 
     // Looting a dead corpse is considered OK
     if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp
index 806cc0a73..d09bd7def 100644
--- a/apps/openmw/mwgui/containeritemmodel.hpp
+++ b/apps/openmw/mwgui/containeritemmodel.hpp
@@ -1,8 +1,13 @@
 #ifndef MWGUI_CONTAINER_ITEM_MODEL_H
 #define MWGUI_CONTAINER_ITEM_MODEL_H
 
+#include <utility>
+#include <vector>
+
 #include "itemmodel.hpp"
 
+#include "../mwworld/containerstore.hpp"
+
 namespace MWGui
 {
 
@@ -32,9 +37,9 @@ namespace MWGui
         virtual void update();
 
     private:
-        std::vector<MWWorld::Ptr> mItemSources;
+        std::vector<std::pair<MWWorld::Ptr, MWWorld::ResolutionHandle>> mItemSources;
         std::vector<MWWorld::Ptr> mWorldItems;
-
+        const bool mTrading;
         std::vector<ItemStack> mItems;
     };
 
diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp
index b1bab32dc..f5144ba81 100644
--- a/apps/openmw/mwgui/tradeitemmodel.cpp
+++ b/apps/openmw/mwgui/tradeitemmodel.cpp
@@ -119,7 +119,7 @@ namespace MWGui
         mBorrowedToUs.clear();
     }
 
-    std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs()
+    const std::vector<ItemStack> TradeItemModel::getItemsBorrowedToUs() const
     {
         return mBorrowedToUs;
     }
diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp
index cdb949c49..e349c225a 100644
--- a/apps/openmw/mwgui/tradeitemmodel.hpp
+++ b/apps/openmw/mwgui/tradeitemmodel.hpp
@@ -40,7 +40,7 @@ namespace MWGui
         /// and removing weight for items we've lent to someone else.
         void adjustEncumbrance (float& encumbrance);
 
-        std::vector<ItemStack> getItemsBorrowedToUs();
+        const std::vector<ItemStack> getItemsBorrowedToUs() const;
 
     private:
         void borrowImpl(const ItemStack& item, std::vector<ItemStack>& out);
diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp
index 672ccbd06..e7dbd4447 100644
--- a/apps/openmw/mwgui/tradewindow.cpp
+++ b/apps/openmw/mwgui/tradewindow.cpp
@@ -99,20 +99,6 @@ namespace MWGui
         setCoord(400, 0, 400, 300);
     }
 
-    void TradeWindow::restock()
-    {
-        // Restock items on the actor inventory
-        mPtr.getClass().restock(mPtr);
-
-        // Also restock any containers owned by this merchant, which are also available to buy in the trade window
-        std::vector<MWWorld::Ptr> itemSources;
-        MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources);
-        for (MWWorld::Ptr& source : itemSources)
-        {
-            source.getClass().restock(source);
-        }
-    }
-
     void TradeWindow::setPtr(const MWWorld::Ptr& actor)
     {
         mPtr = actor;
@@ -121,10 +107,10 @@ namespace MWGui
         mCurrentMerchantOffer = 0;
 
         std::vector<MWWorld::Ptr> itemSources;
+        // Important: actor goes first, so purchased items come out of the actor's pocket first
+        itemSources.push_back(actor);
         MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
 
-        // Important: actor goes last, so that items purchased by the merchant go into his inventory
-        itemSources.push_back(actor);
         std::vector<MWWorld::Ptr> worldItems;
         MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems);
 
@@ -281,8 +267,8 @@ namespace MWGui
             MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
 
         // were there any items traded at all?
-        std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs();
-        std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs();
+        const std::vector<ItemStack>& playerBought = playerItemModel->getItemsBorrowedToUs();
+        const std::vector<ItemStack>& merchantBought = mTradeModel->getItemsBorrowedToUs();
         if (playerBought.empty() && merchantBought.empty())
         {
             // user notification
@@ -313,7 +299,7 @@ namespace MWGui
         }
 
         // check if the player is attempting to sell back an item stolen from this actor
-        for (ItemStack& itemStack : merchantBought)
+        for (const ItemStack& itemStack : merchantBought)
         {
             if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(itemStack.mBase.getCellRef().getRefId(), mPtr))
             {
@@ -363,8 +349,6 @@ namespace MWGui
 
         MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up");
         MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
-
-        restock();
     }
 
     void TradeWindow::onAccept(MyGUI::EditBox *sender)
@@ -478,7 +462,7 @@ namespace MWGui
         // connected to buying and selling the same item.
         // This value has been determined by researching the limitations of the vanilla formula
         // and may not be sufficient if getBarterOffer behavior has been changed.
-        std::vector<ItemStack> playerBorrowed = playerTradeModel->getItemsBorrowedToUs();
+        const std::vector<ItemStack>& playerBorrowed = playerTradeModel->getItemsBorrowedToUs();
         for (const ItemStack& itemStack : playerBorrowed)
         {
             const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
@@ -487,7 +471,7 @@ namespace MWGui
             merchantOffer -= std::max(cap, buyingPrice);
         }
 
-        std::vector<ItemStack> merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
+        const std::vector<ItemStack>& merchantBorrowed = mTradeModel->getItemsBorrowedToUs();
         for (const ItemStack& itemStack : merchantBorrowed)
         {
             const int basePrice = getEffectiveValue(itemStack.mBase, itemStack.mCount);
@@ -532,4 +516,9 @@ namespace MWGui
         mTradeModel = nullptr;
         mSortModel = nullptr;
     }
+
+    void TradeWindow::onClose()
+    {
+        resetReference();
+    }
 }
diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp
index 0730df04f..b211c6d09 100644
--- a/apps/openmw/mwgui/tradewindow.hpp
+++ b/apps/openmw/mwgui/tradewindow.hpp
@@ -29,6 +29,7 @@ namespace MWGui
 
             void setPtr(const MWWorld::Ptr& actor);
 
+            virtual void onClose() override;
             void onFrame(float dt);
             void clear() { resetReference(); }
 
@@ -111,8 +112,6 @@ namespace MWGui
             virtual void onReferenceUnavailable();
 
             int getMerchantGold();
-
-            void restock();
     };
 }
 
diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp
index f716f068d..c8368101a 100644
--- a/apps/openmw/mwmechanics/levelledlist.hpp
+++ b/apps/openmw/mwmechanics/levelledlist.hpp
@@ -19,14 +19,14 @@ namespace MWMechanics
 {
 
     /// @return ID of resulting item, or empty if none
-    inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature)
+    inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Seed& seed = Misc::Rng::getSeed())
     {
         const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
 
         const MWWorld::Ptr& player = getPlayer();
         int playerLevel = player.getClass().getCreatureStats(player).getLevel();
 
-        if (Misc::Rng::roll0to99() < levItem->mChanceNone)
+        if (Misc::Rng::roll0to99(seed) < levItem->mChanceNone)
             return std::string();
 
         std::vector<std::string> candidates;
@@ -55,7 +55,7 @@ namespace MWMechanics
         }
         if (candidates.empty())
             return std::string();
-        std::string item = candidates[Misc::Rng::rollDice(candidates.size())];
+        std::string item = candidates[Misc::Rng::rollDice(candidates.size(), seed)];
 
         // Vanilla doesn't fail on nonexistent items in levelled lists
         if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))
@@ -74,9 +74,9 @@ namespace MWMechanics
         else
         {
             if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
-                return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
+                return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false, seed);
             else
-                return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true);
+                return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true, seed);
         }
     }
 
diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
index 466a3bcc8..872a66799 100644
--- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
+++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
@@ -1045,6 +1045,7 @@ namespace MWMechanics
     void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
     {
         MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
+        MWWorld::ContainerStore& containerStore = targetContainer.getClass().getContainerStore(targetContainer);
         for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
         {
             StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getCellRef().getRefId()));
@@ -1065,7 +1066,7 @@ namespace MWMechanics
 
             int toMove = it->getRefData().getCount() - itemCount;
 
-            targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer);
+            containerStore.add(*it, toMove, targetContainer);
             store.remove(*it, toMove, player);
         }
         // TODO: unhardcode the locklevel
diff --git a/apps/openmw/mwworld/actionharvest.cpp b/apps/openmw/mwworld/actionharvest.cpp
index ff35938b2..c9468c715 100644
--- a/apps/openmw/mwworld/actionharvest.cpp
+++ b/apps/openmw/mwworld/actionharvest.cpp
@@ -29,6 +29,7 @@ namespace MWWorld
 
         MWWorld::Ptr target = getTarget();
         MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);
+        store.resolve();
         MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);
         std::map<std::string, int> takenMap;
         for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp
index 2cda83e17..14b96b27c 100644
--- a/apps/openmw/mwworld/cellstore.cpp
+++ b/apps/openmw/mwworld/cellstore.cpp
@@ -1030,7 +1030,8 @@ namespace MWWorld
             for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
             {
                 Ptr ptr = getCurrentPtr(&*it);
-                if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0)
+                if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0
+                && ptr.getClass().getContainerStore(ptr).isResolved())
                 {
                     ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
                 }
diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp
index 445f2e986..1b3d4029e 100644
--- a/apps/openmw/mwworld/class.hpp
+++ b/apps/openmw/mwworld/class.hpp
@@ -353,8 +353,6 @@ namespace MWWorld
 
             virtual void respawn (const MWWorld::Ptr& ptr) const {}
 
-            virtual void restock (const MWWorld::Ptr& ptr) const {}
-
             /// Returns sound id
             virtual std::string getSound(const MWWorld::ConstPtr& ptr) const;
 
diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp
index 6337ca440..5ecb3dd3a 100644
--- a/apps/openmw/mwworld/containerstore.cpp
+++ b/apps/openmw/mwworld/containerstore.cpp
@@ -23,6 +23,21 @@
 
 namespace
 {
+    void addScripts(MWWorld::ContainerStore& store, MWWorld::CellStore* cell)
+    {
+        auto& scripts = MWBase::Environment::get().getWorld()->getLocalScripts();
+        for(const MWWorld::Ptr& ptr : store)
+        {
+            const std::string& script = ptr.getClass().getScript(ptr);
+            if(!script.empty())
+            {
+                MWWorld::Ptr item = ptr;
+                item.mCell = cell;
+                scripts.add(script, item);
+            }
+        }
+    }
+
     template<typename T>
     float getTotalWeight (const MWWorld::CellRefList<T>& cellRefList)
     {
@@ -44,6 +59,7 @@ namespace
     MWWorld::Ptr searchId (MWWorld::CellRefList<T>& list, const std::string& id,
         MWWorld::ContainerStore *store)
     {
+        store->resolve();
         std::string id2 = Misc::StringUtils::lowerCase (id);
 
         for (typename MWWorld::CellRefList<T>::List::iterator iter (list.mList.begin());
@@ -61,6 +77,18 @@ namespace
     }
 }
 
+MWWorld::ResolutionListener::~ResolutionListener()
+{
+    if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty())
+    {
+        for(const MWWorld::Ptr& ptr : mStore)
+            ptr.getRefData().setCount(0);
+        mStore.fillNonRandom(mStore.mPtr.get<ESM::Container>()->mBase->mInventory, "", mStore.mSeed);
+        addScripts(mStore, mStore.mPtr.mCell);
+        mStore.mResolved = false;
+    }
+}
+
 template<typename T>
 MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList<T>& collection,
     const ESM::ObjectState& state)
@@ -119,7 +147,11 @@ MWWorld::ContainerStore::ContainerStore()
     : mListener(nullptr)
     , mRechargingItemsUpToDate(false)
     , mCachedWeight (0)
-    , mWeightUpToDate (false) {}
+    , mWeightUpToDate (false)
+    , mModified(false)
+    , mResolved(false)
+    , mSeed()
+    , mPtr() {}
 
 MWWorld::ContainerStore::~ContainerStore() {}
 
@@ -153,22 +185,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
     return ContainerStoreIterator (this);
 }
 
-int MWWorld::ContainerStore::count(const std::string &id)
+int MWWorld::ContainerStore::count(const std::string &id) const
 {
     int total=0;
-    for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
-        if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
-            total += iter->getRefData().getCount();
-    return total;
-}
-
-int MWWorld::ContainerStore::restockCount(const std::string &id)
-{
-    int total=0;
-    for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
-        if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
-            if (iter->getCellRef().getSoul().empty())
-                total += iter->getRefData().getCount();
+    for (const auto& iter : *this)
+        if (Misc::StringUtils::ciEqual(iter.getCellRef().getRefId(), id))
+            total += iter.getRefData().getCount();
     return total;
 }
 
@@ -185,9 +207,10 @@ void MWWorld::ContainerStore::setContListener(MWWorld::ContainerStoreListener* l
 
 MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container, int count)
 {
+    resolve();
     if (ptr.getRefData().getCount() <= count)
         return end();
-    MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-count);
+    MWWorld::ContainerStoreIterator it = addNewStack(ptr, subtractItems(ptr.getRefData().getCount(false), count));
     const std::string script = it->getClass().getScript(*it);
     if (!script.empty())
         MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
@@ -199,6 +222,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr,
 
 MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)
 {
+    resolve();
     MWWorld::ContainerStoreIterator retval = end();
     for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
     {
@@ -216,7 +240,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::
     {
         if (stacks(*iter, item))
         {
-            iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount());
+            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), item.getRefData().getCount(false)));
             item.getRefData().setCount(0);
             retval = iter;
             break;
@@ -328,8 +352,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
     return it;
 }
 
-MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count)
+MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count, bool markModified)
 {
+    if(markModified)
+        resolve();
     int type = getType(ptr);
 
     const MWWorld::ESMStore &esmStore =
@@ -345,7 +371,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
         {
             if (Misc::StringUtils::ciEqual((*iter).getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))
             {
-                iter->getRefData().setCount(iter->getRefData().getCount() + realCount);
+                iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), realCount));
                 flagAsModified();
                 return iter;
             }
@@ -361,7 +387,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr,
         if (stacks(*iter, ptr))
         {
             // stack
-            iter->getRefData().setCount( iter->getRefData().getCount() + count );
+            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
 
             flagAsModified();
             return iter;
@@ -439,6 +465,7 @@ void MWWorld::ContainerStore::updateRechargingItems()
 
 int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
 {
+    resolve();
     int toRemove = count;
 
     for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter)
@@ -465,6 +492,7 @@ bool MWWorld::ContainerStore::hasVisibleItems() const
 int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor)
 {
     assert(this == item.getContainerStore());
+    resolve();
 
     int toRemove = count;
     RefData& itemRef = item.getRefData();
@@ -476,7 +504,7 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
     }
     else
     {
-        itemRef.setCount(itemRef.getCount() - toRemove);
+        itemRef.setCount(subtractItems(itemRef.getCount(false), toRemove));
         toRemove = 0;
     }
 
@@ -490,20 +518,33 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
     return count - toRemove;
 }
 
-void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner)
+void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed)
 {
-    for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end();
-        ++iter)
+    for (const ESM::ContItem& iter : items.mList)
     {
-        std::string id = Misc::StringUtils::lowerCase(iter->mItem);
-        addInitialItem(id, owner, iter->mCount);
+        std::string id = Misc::StringUtils::lowerCase(iter.mItem);
+        addInitialItem(id, owner, iter.mCount, &seed);
     }
 
     flagAsModified();
+    mResolved = true;
 }
 
-void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
-                                              int count, bool topLevel, const std::string& levItem)
+void MWWorld::ContainerStore::fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed)
+{
+    mSeed = seed;
+    for (const ESM::ContItem& iter : items.mList)
+    {
+        std::string id = Misc::StringUtils::lowerCase(iter.mItem);
+        addInitialItem(id, owner, iter.mCount, nullptr);
+    }
+
+    flagAsModified();
+    mResolved = false;
+}
+
+void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count,
+                                            Misc::Rng::Seed* seed, bool topLevel, const std::string& levItem)
 {
     if (count == 0) return; //Don't restock with nothing.
     try
@@ -511,13 +552,13 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
         ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
         if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
         {
-            addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
+            addInitialItemImp(ref.getPtr(), owner, count, seed, topLevel, levItem);
         }
         else
         {
             // Adding just one item per time to make sure there isn't a stack of scripted items
-            for (int i = 0; i < abs(count); i++)
-                addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
+            for (int i = 0; i < std::abs(count); i++)
+                addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, seed, topLevel, levItem);
         }
     }
     catch (const std::exception& e)
@@ -526,137 +567,43 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
     }
 }
 
-void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner,
-                                               int count, bool topLevel, const std::string& levItem)
+void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner, int count,
+                                               Misc::Rng::Seed* seed, bool topLevel, const std::string& levItem)
 {
     if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
     {
+        if(!seed)
+            return;
         const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
 
         if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
         {
             for (int i=0; i<std::abs(count); ++i)
-                addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, true, levItemList->mId);
+                addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, seed, true, levItemList->mId);
             return;
         }
         else
         {
-            std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false);
+            std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false, *seed);
             if (itemId.empty())
                 return;
-            addInitialItem(itemId, owner, count, false, levItemList->mId);
+            addInitialItem(itemId, owner, count, seed, false, levItemList->mId);
         }
     }
     else
     {
-        // A negative count indicates restocking items
-        // For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
-        if (!levItem.empty() && count < 0)
-        {
-            //If there is no item in map, insert it
-            std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
-                mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first;
-            //Update spawned count
-            itemInMap->second += std::abs(count);
-        }
-        count = std::abs(count);
-
         ptr.getCellRef().setOwner(owner);
-        addImp (ptr, count);
+        addImp (ptr, count, false);
     }
 }
 
-void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)
-{
-    //allowedForReplace - Holds information about how many items from the list were not sold;
-    //                    Hence, tells us how many items we don't need to restock.
-    //allowedForReplace[list] <- How many items we should generate(how many of these were sold)
-    std::map<std::string, int> allowedForReplace;
-
-    //Check which lists need restocking:
-    for (std::map<std::pair<std::string, std::string>, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end();)
-    {
-        int spawnedCount = it->second; //How many items should be in shop originally
-        int itemCount = restockCount(it->first.first); //How many items are there in shop now
-        //If something was not sold
-        if(itemCount >= spawnedCount)
-        {
-            const std::string& parent = it->first.second;
-            // Security check for old saves:
-            //If item is imported from old save(doesn't have an parent) and wasn't sold
-            if(parent == "")
-            {
-                //Remove it, from shop,
-                remove(it->first.first, itemCount, ptr);//ptr is the NPC
-                //And remove it from map, so that when we restock, the new item will have proper parent.
-                mLevelledItemMap.erase(it++);
-                continue;
-            }
-            //Create the entry if it does not exist yet
-            std::map<std::string, int>::iterator listInMap = allowedForReplace.insert(
-                std::make_pair(it->first.second, 0)).first;
-            //And signal that we don't need to restock item from this list
-            listInMap->second += std::abs(itemCount);
-        }
-        //If every of the item was sold
-        else if (itemCount == 0)
-        {
-            mLevelledItemMap.erase(it++);
-            continue;
-        }
-        //If some was sold, but some remain
-        else
-        {
-            //Create entry if it does not exist yet
-            std::map<std::string, int>::iterator listInMap = allowedForReplace.insert(
-                std::make_pair(it->first.second, 0)).first;
-            //And signal that we don't need to restock all items from this list
-            listInMap->second += std::abs(itemCount);
-            //And update itemCount so we don't mistake it next time.
-            it->second = itemCount;
-        }
-        ++it;
-    }
-
-    //Restock:
-    //For every item that NPC could have
-    for (std::vector<ESM::ContItem>::const_iterator it = items.mList.begin(); it != items.mList.end(); ++it)
-    {
-        //If he shouldn't have it restocked, don't restock it.
-        if (it->mCount >= 0)
-            continue;
-
-        std::string itemOrList = Misc::StringUtils::lowerCase(it->mItem);
-
-        //If it's levelled list, restock if there's need to do so.
-        if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem))
-        {
-            std::map<std::string, int>::iterator listInMap = allowedForReplace.find(itemOrList);
-
-            int restockNum = std::abs(it->mCount);
-            //If we know we must restock less, take it into account
-            if(listInMap != allowedForReplace.end())
-                restockNum -= std::min(restockNum, listInMap->second);
-            //restock
-            addInitialItem(itemOrList, owner, -restockNum, true);
-        }
-        else
-        {
-            //Restocking static item - just restock to the max count
-            int currentCount = restockCount(itemOrList);
-            if (currentCount < std::abs(it->mCount))
-                addInitialItem(itemOrList, owner, -(std::abs(it->mCount) - currentCount), true);
-        }
-    }
-    flagAsModified();
-}
-
 void MWWorld::ContainerStore::clear()
 {
     for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter)
         iter->getRefData().setCount (0);
 
     flagAsModified();
+    mModified = true;
 }
 
 void MWWorld::ContainerStore::flagAsModified()
@@ -665,6 +612,45 @@ void MWWorld::ContainerStore::flagAsModified()
     mRechargingItemsUpToDate = false;
 }
 
+bool MWWorld::ContainerStore::isResolved() const
+{
+    return mResolved;
+}
+
+void MWWorld::ContainerStore::resolve()
+{
+    if(!mResolved && !mPtr.isEmpty())
+    {
+        for(const MWWorld::Ptr& ptr : *this)
+            ptr.getRefData().setCount(0);
+        Misc::Rng::Seed seed{mSeed};
+        fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
+        addScripts(*this, mPtr.mCell);
+    }
+    mModified = true;
+}
+
+MWWorld::ResolutionHandle MWWorld::ContainerStore::resolveTemporarily()
+{
+    if(mModified)
+        return {};
+    std::shared_ptr<ResolutionListener> listener = mResolutionListener.lock();
+    if(!listener)
+    {
+        listener = std::make_shared<ResolutionListener>(*this);
+        mResolutionListener = listener;
+    }
+    if(!mResolved && !mPtr.isEmpty())
+    {
+        for(const MWWorld::Ptr& ptr : *this)
+            ptr.getRefData().setCount(0);
+        Misc::Rng::Seed seed{mSeed};
+        fill(mPtr.get<ESM::Container>()->mBase->mInventory, "", seed);
+        addScripts(*this, mPtr.mCell);
+    }
+    return {listener};
+}
+
 float MWWorld::ContainerStore::getWeight() const
 {
     if (!mWeightUpToDate)
@@ -761,6 +747,7 @@ MWWorld::Ptr MWWorld::ContainerStore::findReplacement(const std::string& id)
 
 MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
 {
+    resolve();
     {
         Ptr ptr = searchId (potions, id, this);
         if (!ptr.isEmpty())
@@ -836,6 +823,22 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
     return Ptr();
 }
 
+int MWWorld::ContainerStore::addItems(int count1, int count2)
+{
+    int sum = std::abs(count1) + std::abs(count2);
+    if(count1 < 0 || count2 < 0)
+        return -sum;
+    return sum;
+}
+
+int MWWorld::ContainerStore::subtractItems(int count1, int count2)
+{
+    int sum = std::abs(count1) - std::abs(count2);
+    if(count1 < 0 || count2 < 0)
+        return -sum;
+    return sum;
+}
+
 void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
 {
     state.mItems.clear();
@@ -853,13 +856,13 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
     storeStates (repairs, state, index);
     storeStates (weapons, state, index, true);
     storeStates (lights, state, index, true);
-
-    state.mLevelledItemMap = mLevelledItemMap;
 }
 
 void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
 {
     clear();
+    mModified = true;
+    mResolved = true;
 
     int index = 0;
     for (std::vector<ESM::ObjectState>::const_iterator
@@ -893,9 +896,6 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory)
                 break;
         }
     }
-
-
-    mLevelledItemMap = inventory.mLevelledItemMap;
 }
 
 template<class PtrType>
diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp
index f2858c5aa..c89e855f9 100644
--- a/apps/openmw/mwworld/containerstore.hpp
+++ b/apps/openmw/mwworld/containerstore.hpp
@@ -3,6 +3,7 @@
 
 #include <iterator>
 #include <map>
+#include <memory>
 #include <utility>
 
 #include <components/esm/loadalch.hpp>
@@ -18,6 +19,8 @@
 #include <components/esm/loadrepa.hpp>
 #include <components/esm/loadweap.hpp>
 
+#include <components/misc/rng.hpp>
+
 #include "ptr.hpp"
 #include "cellreflist.hpp"
 
@@ -27,6 +30,11 @@ namespace ESM
     struct InventoryState;
 }
 
+namespace MWClass
+{
+    class Container;
+}
+
 namespace MWWorld
 {
     class ContainerStore;
@@ -37,6 +45,21 @@ namespace MWWorld
     typedef ContainerStoreIteratorBase<Ptr> ContainerStoreIterator;
     typedef ContainerStoreIteratorBase<ConstPtr> ConstContainerStoreIterator;
 
+    class ResolutionListener
+    {
+            ContainerStore& mStore;
+        public:
+            ResolutionListener(ContainerStore& store) : mStore(store) {}
+            ~ResolutionListener();
+    };
+
+    class ResolutionHandle
+    {
+            std::shared_ptr<ResolutionListener> mListener;
+        public:
+            ResolutionHandle(std::shared_ptr<ResolutionListener> listener) : mListener(listener) {}
+            ResolutionHandle() {}
+    };
     
     class ContainerStoreListener
     {
@@ -93,15 +116,18 @@ namespace MWWorld
             MWWorld::CellRefList<ESM::Repair>            repairs;
             MWWorld::CellRefList<ESM::Weapon>            weapons;
 
-            std::map<std::pair<std::string, std::string>, int> mLevelledItemMap;
-            ///< Stores result of levelled item spawns. <(refId, spawningGroup), count>
-            /// This is used to restock levelled items(s) if the old item was sold.
-
             mutable float mCachedWeight;
             mutable bool mWeightUpToDate;
-            ContainerStoreIterator addImp (const Ptr& ptr, int count);
-            void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
-            void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
+
+            bool mModified;
+            bool mResolved;
+            unsigned int mSeed;
+            MWWorld::Ptr mPtr;
+            std::weak_ptr<ResolutionListener> mResolutionListener;
+
+            ContainerStoreIterator addImp (const Ptr& ptr, int count, bool markModified = true);
+            void addInitialItem (const std::string& id, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true, const std::string& levItem = "");
+            void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true, const std::string& levItem = "");
 
             template<typename T>
             ContainerStoreIterator getState (CellRefList<T>& collection,
@@ -175,31 +201,31 @@ namespace MWWorld
             /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted.
             /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item.
 
-            int count (const std::string& id);
+            int count (const std::string& id) const;
             ///< @return How many items with refID \a id are in this container?
 
-            int restockCount (const std::string& id);
-            ///< Item count with restock adjustments (such as ignoring filled soul gems).
-            ///  @return How many items with refID \a id are in this container?
-
             ContainerStoreListener* getContListener() const;
             void setContListener(ContainerStoreListener* listener);
-
         protected:
             ContainerStoreIterator addNewStack (const ConstPtr& ptr, int count);
             ///< Add the item to this container (do not try to stack it onto existing items)
 
             virtual void flagAsModified();
 
+            /// + and - operations that can deal with negative stacks
+            /// Note that negativity is infectious
+            static int addItems(int count1, int count2);
+            static int subtractItems(int count1, int count2);
         public:
 
             virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
             ///< @return true if the two specified objects can stack with each other
 
-            void fill (const ESM::InventoryList& items, const std::string& owner);
+            void fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Seed& seed = Misc::Rng::getSeed());
             ///< Insert items into *this.
 
-            void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner);
+            void fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed);
+            ///< Insert items into *this, excluding leveled items
 
             virtual void clear();
             ///< Empty container.
@@ -220,8 +246,15 @@ namespace MWWorld
 
             virtual void readState (const ESM::InventoryState& state);
 
+            bool isResolved() const;
+
+            void resolve();
+            ResolutionHandle resolveTemporarily();
+
             friend class ContainerStoreIteratorBase<Ptr>;
             friend class ContainerStoreIteratorBase<ConstPtr>;
+            friend class ResolutionListener;
+            friend class MWClass::Container;
     };
 
     
diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp
index d67a22884..cf2b97ce7 100644
--- a/apps/openmw/mwworld/inventorystore.cpp
+++ b/apps/openmw/mwworld/inventorystore.cpp
@@ -86,8 +86,9 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt
         // unstack if required
         if (!allowedSlots.second && iter->getRefData().getCount() > 1)
         {
-            MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1);
-            iter->getRefData().setCount(iter->getRefData().getCount()-1);
+            int count = iter->getRefData().getCount(false);
+            MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, count > 0 ? 1 : -1);
+            iter->getRefData().setCount(subtractItems(count, 1));
             mSlots[slot] = newIter;
         }
         else
@@ -850,8 +851,8 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con
     {
         if (stacks(*iter, item) && !isEquipped(*iter))
         {
-            iter->getRefData().setCount(iter->getRefData().getCount() + count);
-            item.getRefData().setCount(item.getRefData().getCount() - count);
+            iter->getRefData().setCount(addItems(iter->getRefData().getCount(false), count));
+            item.getRefData().setCount(subtractItems(item.getRefData().getCount(false), count));
             return iter;
         }
     }
diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp
index a727b4b3a..8a511030d 100644
--- a/apps/openmw/mwworld/localscripts.cpp
+++ b/apps/openmw/mwworld/localscripts.cpp
@@ -45,7 +45,7 @@ namespace
             // Ignore containers without generated content
             if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&
                 containerPtr.getRefData().getCustomData() == nullptr)
-                return false;
+                return true;
 
             MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);
             for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp
index f6fa3556f..a5f8ef4b1 100644
--- a/apps/openmw/mwworld/refdata.cpp
+++ b/apps/openmw/mwworld/refdata.cpp
@@ -146,8 +146,10 @@ namespace MWWorld
         return mBaseNode;
     }
 
-    int RefData::getCount() const
+    int RefData::getCount(bool absolute) const
     {
+        if(absolute)
+            return std::abs(mCount);
         return mCount;
     }
 
diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp
index 6a59d9797..738a6d53a 100644
--- a/apps/openmw/mwworld/refdata.hpp
+++ b/apps/openmw/mwworld/refdata.hpp
@@ -86,7 +86,7 @@ namespace MWWorld
             /// Set base node (can be a null pointer).
             void setBaseNode (SceneUtil::PositionAttitudeTransform* base);
 
-            int getCount() const;
+            int getCount(bool absolute = true) const;
 
             void setLocals (const ESM::Script& script);
 
diff --git a/components/detournavigator/findrandompointaroundcircle.cpp b/components/detournavigator/findrandompointaroundcircle.cpp
index 3888c59fe..263dba68e 100644
--- a/components/detournavigator/findrandompointaroundcircle.cpp
+++ b/components/detournavigator/findrandompointaroundcircle.cpp
@@ -36,7 +36,7 @@ namespace DetourNavigator
         dtPolyRef resultRef = 0;
         osg::Vec3f resultPosition;
         navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
-            &Misc::Rng::rollProbability, &resultRef, resultPosition.ptr());
+            []() { return Misc::Rng::rollProbability(); }, &resultRef, resultPosition.ptr());
 
         if (resultRef == 0)
             return boost::optional<osg::Vec3f>();
diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp
index fe54762c5..2392d263f 100644
--- a/components/esm/inventorystate.cpp
+++ b/components/esm/inventorystate.cpp
@@ -3,6 +3,8 @@
 #include "esmreader.hpp"
 #include "esmwriter.hpp"
 
+#include <components/misc/stringops.hpp>
+
 void ESM::InventoryState::load (ESMReader &esm)
 {
     // obsolete
@@ -106,6 +108,19 @@ void ESM::InventoryState::load (ESMReader &esm)
 
     mSelectedEnchantItem = -1;
     esm.getHNOT(mSelectedEnchantItem, "SELE");
+
+    // Old saves had restocking levelled items in a special map
+    // This turns items from that map into negative quantities
+    for(const auto& entry : mLevelledItemMap)
+    {
+        const std::string& id = entry.first.first;
+        const int count = entry.second;
+        for(auto& item : mItems)
+        {
+            if(item.mCount == count && Misc::StringUtils::ciEqual(id, item.mRef.mRefID))
+                item.mCount = -count;
+        }
+    }
 }
 
 void ESM::InventoryState::save (ESMWriter &esm) const
diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp
index 0fc84e309..9edcb1a67 100644
--- a/components/esm/savedgame.cpp
+++ b/components/esm/savedgame.cpp
@@ -4,7 +4,7 @@
 #include "esmwriter.hpp"
 
 unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
-int ESM::SavedGame::sCurrentFormat = 14;
+int ESM::SavedGame::sCurrentFormat = 15;
 
 void ESM::SavedGame::load (ESMReader &esm)
 {
diff --git a/components/misc/rng.cpp b/components/misc/rng.cpp
index 09279e85e..4189404b1 100644
--- a/components/misc/rng.cpp
+++ b/components/misc/rng.cpp
@@ -3,29 +3,44 @@
 #include <chrono>
 #include <random>
 
+namespace
+{
+    Misc::Rng::Seed sSeed;
+}
+
 namespace Misc
 {
 
-    std::mt19937 Rng::generator = std::mt19937();
+    Rng::Seed::Seed() {}
+
+    Rng::Seed::Seed(unsigned int seed)
+    {
+        mGenerator.seed(seed);
+    }
+
+    Rng::Seed& Rng::getSeed()
+    {
+        return sSeed;
+    }
 
     void Rng::init(unsigned int seed)
     {
-        generator.seed(seed);
+        sSeed.mGenerator.seed(seed);
     }
 
-    float Rng::rollProbability()
+    float Rng::rollProbability(Seed& seed)
     {
-        return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(generator);
+        return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(sSeed.mGenerator);
     }
 
-    float Rng::rollClosedProbability()
+    float Rng::rollClosedProbability(Seed& seed)
     {
-        return std::uniform_real_distribution<float>(0, 1)(generator);
+        return std::uniform_real_distribution<float>(0, 1)(sSeed.mGenerator);
     }
 
-    int Rng::rollDice(int max)
+    int Rng::rollDice(int max, Seed& seed)
     {
-        return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(generator) : 0;
+        return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(sSeed.mGenerator) : 0;
     }
 
     unsigned int Rng::generateDefaultSeed()
diff --git a/components/misc/rng.hpp b/components/misc/rng.hpp
index 65a554cf2..8efca438d 100644
--- a/components/misc/rng.hpp
+++ b/components/misc/rng.hpp
@@ -13,24 +13,32 @@ namespace Misc
 class Rng
 {
 public:
+    class Seed
+    {
+        std::mt19937 mGenerator;
+    public:
+        Seed();
+        Seed(const Seed&) = delete;
+        Seed(unsigned int seed);
+        friend class Rng;
+    };
 
-    /// create a RNG
-    static std::mt19937 generator;
+    static Seed& getSeed();
 
     /// seed the RNG
     static void init(unsigned int seed = generateDefaultSeed());
 
     /// return value in range [0.0f, 1.0f)  <- note open upper range.
-    static float rollProbability();
+    static float rollProbability(Seed& seed = getSeed());
   
     /// return value in range [0.0f, 1.0f]  <- note closed upper range.
-    static float rollClosedProbability();
+    static float rollClosedProbability(Seed& seed = getSeed());
 
     /// return value in range [0, max)  <- note open upper range.
-    static int rollDice(int max);
+    static int rollDice(int max, Seed& seed = getSeed());
 
     /// return value in range [0, 99]
-    static int roll0to99() { return rollDice(100); }
+    static int roll0to99(Seed& seed = getSeed()) { return rollDice(100, seed); }
 
     /// returns default seed for RNG
     static unsigned int generateDefaultSeed();