From a872c9f83ad487456b032a896ad699d590d6ad61 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 May 2014 12:53:21 +0200 Subject: [PATCH] Feature #1323: handle restocking levelled items --- apps/openmw/mwclass/container.cpp | 12 +----- apps/openmw/mwclass/creature.cpp | 12 +----- apps/openmw/mwclass/npc.cpp | 12 +----- apps/openmw/mwworld/containerstore.cpp | 60 ++++++++++++++++++++++---- apps/openmw/mwworld/containerstore.hpp | 10 ++++- components/esm/inventorystate.cpp | 16 ++++++- components/esm/inventorystate.hpp | 4 ++ 7 files changed, 85 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index fd8eeb46b9..2d9695c6b9 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -74,16 +74,8 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; - for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) - { - if (it->mCount < 0) - { - MWWorld::ContainerStore& store = getContainerStore(ptr); - int currentCount = store.count(it->mItem.toString()); - if (currentCount < std::abs(it->mCount)) - store.add (it->mItem.toString(), std::abs(it->mCount) - currentCount, ptr); - } - } + MWWorld::ContainerStore& store = getContainerStore(ptr); + store.restock(list, ptr, ptr.getCellRef().mOwner, ptr.getCellRef().mFaction); } void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index db3a70a6a6..924021faae 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -843,16 +843,8 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; - for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) - { - if (it->mCount < 0) - { - MWWorld::ContainerStore& store = getContainerStore(ptr); - int currentCount = store.count(it->mItem.toString()); - if (currentCount < std::abs(it->mCount)) - store.add (it->mItem.toString(), std::abs(it->mCount) - currentCount, ptr); - } - } + MWWorld::ContainerStore& store = getContainerStore(ptr); + store.restock(list, ptr, ptr.getCellRef().mRefID, ptr.getCellRef().mFaction); } const ESM::GameSetting* Creature::fMinWalkSpeedCreature; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 009559d192..72505fa081 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1334,16 +1334,8 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); const ESM::InventoryList& list = ref->mBase->mInventory; - for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) - { - if (it->mCount < 0) - { - MWWorld::ContainerStore& store = getContainerStore(ptr); - int currentCount = store.count(it->mItem.toString()); - if (currentCount < std::abs(it->mCount)) - store.add (it->mItem.toString(), std::abs(it->mCount) - currentCount, ptr); - } - } + MWWorld::ContainerStore& store = getContainerStore(ptr); + store.restock(list, ptr, ptr.getCellRef().mRefID, ptr.getCellRef().mFaction); } const ESM::GameSetting *Npc::fMinWalkSpeed; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 1cbaa349ca..8f2a566461 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -366,7 +366,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std:: for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { - std::string id = iter->mItem.toString(); + std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); addInitialItem(id, owner, faction, iter->mCount); } @@ -374,21 +374,18 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std:: } void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, - int count, bool topLevel) + int count, bool topLevel, const std::string& levItem) { - // A negative count indicates restocking items, but this is implemented elsewhere - count = std::abs(count); - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) + if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each) { - for (int i=0; i 0 ? 1 : -1, true, levItem->mId); return; } else @@ -396,17 +393,58 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; - addInitialItem(id, owner, faction, count, false); + addInitialItem(id, owner, faction, count, false, levItem->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 (mLevelledItemMap.find(id) == mLevelledItemMap.end()) + mLevelledItemMap[id] = 0; + mLevelledItemMap[id] += std::abs(count); + } + count = std::abs(count); + ref.getPtr().getCellRef().mOwner = owner; ref.getPtr().getCellRef().mFaction = faction; addImp (ref.getPtr(), count); } } +void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction) +{ + // Remove the items already spawned by levelled items that will restock + for (std::map::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + { + if (count(it->first) >= it->second) + remove(it->first, it->second, ptr); + } + mLevelledItemMap.clear(); + + for (std::vector::const_iterator it = items.mList.begin(); it != items.mList.end(); ++it) + { + if (it->mCount >= 0) + continue; + + std::string item = Misc::StringUtils::lowerCase(it->mItem.toString()); + + if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mItem.toString())) + { + addInitialItem(item, owner, faction, it->mCount, true); + } + else + { + int currentCount = count(item); + if (currentCount < std::abs(it->mCount)) + add (item, std::abs(it->mCount) - currentCount, ptr); + } + } + flagAsModified(); +} + void MWWorld::ContainerStore::clear() { for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) @@ -585,6 +623,8 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const state.mLights.clear(); + state.mLevelledItemMap = mLevelledItemMap; + for (MWWorld::CellRefList::List::const_iterator iter (lights.mList.begin()); iter!=lights.mList.end(); ++iter) { @@ -628,6 +668,8 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { getState (lights, iter->first); } + + mLevelledItemMap = state.mLevelledItemMap; } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index dd0c1b0024..7c81bdb6e7 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -2,6 +2,7 @@ #define GAME_MWWORLD_CONTAINERSTORE_H #include +#include #include #include @@ -65,10 +66,15 @@ namespace MWWorld MWWorld::CellRefList probes; MWWorld::CellRefList repairs; MWWorld::CellRefList weapons; + + std::map mLevelledItemMap; + ///< Stores result of levelled item spawns. + /// This is used to remove the spawned item(s) if the levelled item is restocked. + mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); - void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); + void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true, const std::string& levItem = ""); template ContainerStoreIterator getState (CellRefList& collection, @@ -145,6 +151,8 @@ namespace MWWorld void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store); ///< Insert items into *this. + void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction); + virtual void clear(); ///< Empty container. diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 4d8cbc6228..1b0bc772e9 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -47,6 +47,14 @@ void ESM::InventoryState::load (ESMReader &esm) mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); } } + + while (esm.isNextSub("LEVM")) + { + std::string id = esm.getHString(); + int count; + esm.getHNT (count, "COUN"); + mLevelledItemMap[id] = count; + } } void ESM::InventoryState::save (ESMWriter &esm) const @@ -57,4 +65,10 @@ void ESM::InventoryState::save (ESMWriter &esm) const for (std::vector >::const_iterator iter (mLights.begin()); iter!=mLights.end(); ++iter) write (esm, iter->first, ESM::REC_LIGH, iter->second); -} \ No newline at end of file + + for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) + { + esm.writeHNString ("LEVM", it->first); + esm.writeHNT ("COUN", it->second); + } +} diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 3cfffbccc9..f754fb57aa 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_ESM_INVENTORYSTATE_H #define OPENMW_ESM_INVENTORYSTATE_H +#include + #include "objectstate.hpp" #include "lightstate.hpp" @@ -20,6 +22,8 @@ namespace ESM // lights (slot) std::vector > mLights; + std::map mLevelledItemMap; + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm) const; };