From e94fcce6229175d83b2394366a16b7ca9233f42f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 15 Aug 2013 14:45:13 +0200 Subject: [PATCH] accessing references via their ID now also works for references in containers in active cells --- apps/openmw/mwworld/cells.cpp | 53 +++++++++++----------- apps/openmw/mwworld/cells.hpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 61 +++++++++++++++++++++++--- apps/openmw/mwworld/cellstore.hpp | 3 ++ apps/openmw/mwworld/containerstore.cpp | 26 +++++++++-- apps/openmw/mwworld/containerstore.hpp | 2 + 6 files changed, 111 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 3495bdc06..37c4b6a3f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -48,7 +48,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS { Ptr ptr = getPtr (name, cellStore); - if (!ptr.isEmpty()) + if (!ptr.isEmpty() && ptr.isInCell()) { mIdCache[mIdCacheIndex].first = name; mIdCache[mIdCacheIndex].second = &cellStore; @@ -121,7 +121,8 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } -MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell) +MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell, + bool searchInContainers) { if (cell.mState==Ptr::CellStore::State_Unloaded) cell.preload (mStore, mReader); @@ -138,71 +139,69 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce return Ptr(); } - MWWorld::Ptr ptr; - if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mPotions.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mAppas.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mArmors.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mBooks.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mClothes.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mContainers.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mCreatures.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mDoors.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mIngreds.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mCreatureLists.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mItemLists.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mLights.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mMiscItems.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mNpcs.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mProbes.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mRepairs.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mStatics.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mWeapons.find (name)) - ptr = Ptr (ref, &cell); + return Ptr (ref, &cell); + + if (searchInContainers) + return cell.searchInContainer (name); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { - return ptr; - } return Ptr(); } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 6f9c649fb..0c51cf452 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -45,7 +45,8 @@ namespace MWWorld CellStore *getInterior (const std::string& name); - Ptr getPtr (const std::string& name, CellStore& cellStore); + Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); + ///< \param searchInContainers Only affect loaded cells. Ptr getPtr (const std::string& name); }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 44915a7c5..8bb3d3c8d 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,6 +7,29 @@ #include "ptr.hpp" #include "esmstore.hpp" +#include "class.hpp" +#include "containerstore.hpp" + +namespace +{ + template + MWWorld::Ptr searchInContainerList (MWWorld::CellRefList& containerList, const std::string& id) + { + for (typename MWWorld::CellRefList::List::iterator iter (containerList.mList.begin()); + iter!=containerList.mList.end(); ++iter) + { + MWWorld::Ptr container (&*iter, 0); + + MWWorld::Ptr ptr = + MWWorld::Class::get (container).getContainerStore (container).search (id); + + if (!ptr.isEmpty()) + return ptr; + } + + return MWWorld::Ptr(); + } +} namespace MWWorld { @@ -16,14 +39,14 @@ namespace MWWorld { // Get existing reference, in case we need to overwrite it. typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum); - + // Skip this when reference was deleted. // TODO: Support respawning references, in this case, we need to track it somehow. if (ref.mDeleted) { mList.erase(iter); return; } - + // for throwing exception on unhandled record type const MWWorld::Store &store = esmStore.get(); const X *ptr = store.search(ref.mRefID); @@ -39,7 +62,7 @@ namespace MWWorld mList.push_back(LiveRef(ref, ptr)); } } - + template bool operator==(const LiveCellRef& ref, int pRefnum) { return (ref.mRef.mRefnum == pRefnum); @@ -133,7 +156,7 @@ namespace MWWorld ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum); if (iter != mCell->mMovedRefs.end()) { continue; - } + } int rec = store.find(ref.mRefID); ref.mRefID = lowerCase; @@ -186,7 +209,7 @@ namespace MWWorld (int(*)(int)) std::tolower); int rec = store.find(ref.mRefID); - + ref.mRefID = lowerCase; /* We can optimize this further by storing the pointer to the @@ -221,7 +244,33 @@ namespace MWWorld default: std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; } - + } } + + Ptr CellStore::searchInContainer (const std::string& id) + { + { + Ptr ptr = searchInContainerList (mContainers, id); + + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchInContainerList (mCreatures, id); + + if (!ptr.isEmpty()) + return ptr; + } + + { + Ptr ptr = searchInContainerList (mNpcs, id); + + if (!ptr.isEmpty()) + return ptr; + } + + return Ptr(); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index f8467c84f..bcbc5e415 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -131,6 +131,8 @@ namespace MWWorld return mCell->isExterior(); } + Ptr searchInContainer (const std::string& id); + private: template @@ -148,6 +150,7 @@ namespace MWWorld void listRefs(const MWWorld::ESMStore &store, std::vector &esm); void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index cd30f1ac0..e7a4f0ea1 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -85,12 +86,12 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr CellStore *cell; Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - + if(&(MWWorld::Class::get (player).getContainerStore (player)) == this) { cell = 0; // Items in player's inventory have cell set to 0, so their scripts will never be removed - - // Set OnPCAdd special variable, if it is declared + + // Set OnPCAdd special variable, if it is declared item.mRefData->getLocals().setVarByInt(script, "onpcadd", 1); } else @@ -347,6 +348,25 @@ int MWWorld::ContainerStore::getType (const Ptr& ptr) "Object of type " + ptr.getTypeName() + " can not be placed into a container"); } +MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) +{ + /// \todo Since we have direct access to the CellRefList here, the performance of this function + /// could be improved notably by iterating directly over the CellRefLists instead of using + /// a ContainerStoreIterator. + + std::string id2 = Misc::StringUtils::lowerCase (id); + + for (ContainerStoreIterator iter (this); iter!=end(); ++iter) + { + Ptr ptr = *iter; + + if (Misc::StringUtils::lowerCase (Class::get (ptr).getId (ptr))==id2) + return ptr; + } + + return Ptr(); +} + MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container) : mType (-1), mMask (0), mContainer (container) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index bdf6d28e4..9a11f1603 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -106,6 +106,8 @@ namespace MWWorld ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. + Ptr search (const std::string& id); + friend class ContainerStoreIterator; };