From 3aa53f3cb4cacbe9f88fe71dec800b6177c84ed3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 4 Dec 2015 19:46:02 +0100 Subject: [PATCH] Object cell movement tracker works. Savegame handling is still missing and some game functionality is still stubbed out. --- apps/openmw/mwworld/cellstore.cpp | 178 ++++++++++++++++++------------ apps/openmw/mwworld/cellstore.hpp | 175 +++++++++++++++-------------- apps/openmw/mwworld/scene.cpp | 16 ++- apps/openmw/mwworld/worldimp.cpp | 11 +- 4 files changed, 214 insertions(+), 166 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index e3ee7d249e..d56baf231f 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -47,13 +47,16 @@ namespace template MWWorld::Ptr searchViaActorId (MWWorld::CellRefList& actorList, int actorId, - MWWorld::CellStore *cell) + MWWorld::CellStore *cell, const std::map& toIgnore) { for (typename MWWorld::CellRefList::List::iterator iter (actorList.mList.begin()); iter!=actorList.mList.end(); ++iter) { MWWorld::Ptr actor (&*iter, cell); + if (toIgnore.find(&*iter) != toIgnore.end()) + continue; + if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0) return actor; } @@ -181,6 +184,7 @@ namespace MWWorld void CellStore::moveFrom(const Ptr &object, CellStore *from) { + mHasState = true; MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); if (found != mMovedToAnotherCell.end()) { @@ -192,10 +196,27 @@ namespace MWWorld { mMovedHere.insert(std::make_pair(object.getBase(), from)); } + + if (mState == State_Loaded) + updateMergedRefs(); + else if (mState == State_Preloaded) + { + mIds.push_back(object.getCellRef().getRefId()); + std::sort(mIds.begin(), mIds.end()); + } } - void CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) + MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) { + if (cellToMoveTo == this) + throw std::runtime_error("object is already in this cell"); + + // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise. + if (mState != State_Loaded) + throw std::runtime_error("can't move object from a non-loaded cell (how did you get this object anyway?)"); + + // TODO: ensure that the object actually exists in the cell + MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); if (found != mMovedHere.end()) { @@ -208,21 +229,59 @@ namespace MWWorld mMovedHere.erase(found); // Now that object is back to its rightful owner, we can move it - originalCell->moveTo(object, cellToMoveTo); + if (cellToMoveTo != originalCell) + { + originalCell->moveTo(object, cellToMoveTo); + } updateMergedRefs(); - return; + return MWWorld::Ptr(object.getBase(), cellToMoveTo); } cellToMoveTo->moveFrom(object, this); mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo)); updateMergedRefs(); + return MWWorld::Ptr(object.getBase(), cellToMoveTo); } + struct MergeFunctor + { + MergeFunctor(std::vector& mergeTo, const std::map& movedHere, + const std::map& movedToAnotherCell) + : mMergeTo(mergeTo) + , mMovedHere(movedHere) + , mMovedToAnotherCell(movedToAnotherCell) + { + } + + bool operator() (const MWWorld::Ptr& ptr) + { + if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end()) + return true; + mMergeTo.push_back(ptr.getBase()); + return true; + } + + void merge() + { + for (std::map::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + mMergeTo.push_back(it->first); + } + + private: + std::vector& mMergeTo; + + const std::map& mMovedHere; + const std::map& mMovedToAnotherCell; + }; + void CellStore::updateMergedRefs() { - + mMergedRefs.clear(); + MergeFunctor functor(mMergedRefs, mMovedHere, mMovedToAnotherCell); + forEachInternal(functor); + functor.merge(); } CellStore::CellStore (const ESM::Cell *cell) @@ -258,85 +317,50 @@ namespace MWWorld return const_cast (this)->search (id).isEmpty(); } + struct SearchFunctor + { + MWWorld::Ptr mFound; + std::string mIdToFind; + bool operator()(const MWWorld::Ptr& ptr) + { + if (ptr.getCellRef().getRefId() == mIdToFind) + { + mFound = ptr; + return false; + } + return true; + } + }; + Ptr CellStore::search (const std::string& id) { bool oldState = mHasState; - mHasState = true; - - if (LiveCellRef *ref = mActivators.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mPotions.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mAppas.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mArmors.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mBooks.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mClothes.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mContainers.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mCreatures.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mDoors.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mIngreds.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mCreatureLists.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mItemLists.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mLights.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mLockpicks.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mMiscItems.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mNpcs.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mProbes.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mRepairs.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mStatics.find (id)) - return Ptr (ref, this); - - if (LiveCellRef *ref = mWeapons.find (id)) - return Ptr (ref, this); + SearchFunctor searchFunctor; + searchFunctor.mIdToFind = id; + forEach(searchFunctor); mHasState = oldState; - - return Ptr(); + return searchFunctor.mFound; } Ptr CellStore::searchViaActorId (int id) { - if (Ptr ptr = ::searchViaActorId (mNpcs, id, this)) + if (Ptr ptr = ::searchViaActorId (mNpcs, id, this, mMovedToAnotherCell)) return ptr; - if (Ptr ptr = ::searchViaActorId (mCreatures, id, this)) + if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell)) return ptr; + for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + { + MWWorld::Ptr actor (it->first, this); + if (!actor.getClass().isActor()) + continue; + if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0) + return actor; + } + return Ptr(); } @@ -435,6 +459,8 @@ namespace MWWorld continue; } + // We don't need to check mMovedToAnotherCell because listRefs isn't used for loaded cells. + mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); } } @@ -447,6 +473,12 @@ namespace MWWorld mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } + // List runtime moved references + for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + { + mIds.push_back(Misc::StringUtils::lowerCase(it->first->mRef.getRefId())); + } + std::sort (mIds.begin(), mIds.end()); } @@ -489,6 +521,8 @@ namespace MWWorld loadRef (ref, false, store); } + + updateMergedRefs(); } bool CellStore::isExterior() const @@ -610,11 +644,15 @@ namespace MWWorld writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); + + // TODO: write moved references } void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap) { + // TODO: read moved references + mHasState = true; while (reader.isNextSub ("OBJE")) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index ef1192277c..2ef03e2c41 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -112,11 +112,64 @@ namespace MWWorld /// Repopulate mMergedRefs. void updateMergedRefs(); + template + LiveCellRefBase* insertBase(CellRefList& list, const LiveCellRef* ref) + { + mHasState = true; + LiveCellRefBase* ret = &list.insert(*ref); + updateMergedRefs(); + return ret; + } + + // helper function for forEachInternal + template + bool forEachImp (Functor& functor, List& list) + { + for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); + ++iter) + { + if (iter->mData.isDeletedByContentFile()) + continue; + if (!functor (MWWorld::Ptr(&*iter, this))) + return false; + } + return true; + } + + // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved objects are accounted for. + template + bool forEachInternal (Functor& functor) + { + return + forEachImp (functor, mActivators) && + forEachImp (functor, mPotions) && + forEachImp (functor, mAppas) && + forEachImp (functor, mArmors) && + forEachImp (functor, mBooks) && + forEachImp (functor, mClothes) && + forEachImp (functor, mContainers) && + forEachImp (functor, mDoors) && + forEachImp (functor, mIngreds) && + forEachImp (functor, mItemLists) && + forEachImp (functor, mLights) && + forEachImp (functor, mLockpicks) && + forEachImp (functor, mMiscItems) && + forEachImp (functor, mProbes) && + forEachImp (functor, mRepairs) && + forEachImp (functor, mStatics) && + forEachImp (functor, mWeapons) && + forEachImp (functor, mCreatures) && + forEachImp (functor, mNpcs) && + forEachImp (functor, mCreatureLists); + } + public: /// Moves object from this cell to the given cell. /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...) - void moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + /// @note throws exception if cellToMoveTo == this + /// @return updated MWWorld::Ptr with the new CellStore pointer set. + MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. @@ -136,6 +189,7 @@ namespace MWWorld bool hasId (const std::string& id) const; ///< May return true for deleted IDs when in preload state. Will return false, if cell is /// unloaded. + /// @note Will not account for moved references which may exist in Loaded state. Use search() instead if the cell is loaded. Ptr search (const std::string& id); ///< Will return an empty Ptr if cell is not loaded. Does not check references in @@ -166,45 +220,23 @@ namespace MWWorld /// false will abort the iteration. /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? - /// - /// \note Creatures and NPCs are handled last. template bool forEach (Functor& functor) { + if (mState != State_Loaded) + return false; + mHasState = true; - return - forEachImp (functor, mActivators) && - forEachImp (functor, mPotions) && - forEachImp (functor, mAppas) && - forEachImp (functor, mArmors) && - forEachImp (functor, mBooks) && - forEachImp (functor, mClothes) && - forEachImp (functor, mContainers) && - forEachImp (functor, mDoors) && - forEachImp (functor, mIngreds) && - forEachImp (functor, mItemLists) && - forEachImp (functor, mLights) && - forEachImp (functor, mLockpicks) && - forEachImp (functor, mMiscItems) && - forEachImp (functor, mProbes) && - forEachImp (functor, mRepairs) && - forEachImp (functor, mStatics) && - forEachImp (functor, mWeapons) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs) && - forEachImp (functor, mCreatureLists); - } + for (unsigned int i=0; imData.isDeletedByContentFile()) + continue; - template - bool forEachContainer (Functor& functor) - { - mHasState = true; - - return - forEachImp (functor, mContainers) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs); + if (!functor(MWWorld::Ptr(mMergedRefs[i], this))) + return false; + } + return true; } /// \todo add const version of forEach @@ -234,20 +266,6 @@ namespace MWWorld private: - template - bool forEachImp (Functor& functor, List& list) - { - for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); - ++iter) - { - if (iter->mData.isDeletedByContentFile()) - continue; - if (!functor (MWWorld::Ptr(&*iter, this))) - return false; - } - return true; - } - /// Run through references and store IDs void listRefs(const MWWorld::ESMStore &store, std::vector &esm); @@ -261,126 +279,105 @@ namespace MWWorld MWMechanics::PathgridGraph mPathgridGraph; }; - template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mActivators.insert(*ref); + return insertBase(mActivators, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mPotions.insert(*ref); + return insertBase(mPotions, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mAppas.insert(*ref); + return insertBase(mAppas, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mArmors.insert(*ref); + return insertBase(mArmors, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mBooks.insert(*ref); + return insertBase(mBooks, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mClothes.insert(*ref); + return insertBase(mClothes, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mContainers.insert(*ref); + return insertBase(mContainers, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mCreatures.insert(*ref); + return insertBase(mCreatures, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mDoors.insert(*ref); + return insertBase(mDoors, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mIngreds.insert(*ref); + return insertBase(mIngreds, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mCreatureLists.insert(*ref); + return insertBase(mCreatureLists, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mItemLists.insert(*ref); + return insertBase(mItemLists, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mLights.insert(*ref); + return insertBase(mLights, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mLockpicks.insert(*ref); + return insertBase(mLockpicks, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mMiscItems.insert(*ref); + return insertBase(mMiscItems, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mNpcs.insert(*ref); + return insertBase(mNpcs, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mProbes.insert(*ref); + return insertBase(mProbes, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mRepairs.insert(*ref); + return insertBase(mRepairs, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mStatics.insert(*ref); + return insertBase(mStatics, ref); } template<> inline LiveCellRefBase* CellStore::insert(const LiveCellRef* ref) { - mHasState = true; - return &mWeapons.insert(*ref); + return insertBase(mWeapons, ref); } bool operator== (const CellStore& left, const CellStore& right); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 45c94b6d9a..896d5f8ebd 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -116,7 +116,6 @@ namespace { addObject(ptr, mPhysics, mRendering); updateObjectRotation(ptr, mPhysics, mRendering, false); - ptr.getClass().adjustPosition (ptr, false); } catch (const std::exception& e) { @@ -129,6 +128,17 @@ namespace return true; } + + struct AdjustPositionFunctor + { + bool operator() (const MWWorld::Ptr& ptr) + { + if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) + ptr.getClass().adjustPosition (ptr, false); + return true; + } + }; + } @@ -553,6 +563,10 @@ namespace MWWorld { InsertFunctor functor (cell, rescale, *loadingListener, *mPhysics, mRendering); cell.forEach (functor); + + // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order + AdjustPositionFunctor adjustPosFunctor; + cell.forEach (adjustPosFunctor); } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 950c38e769..bb2ca2aae2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -722,7 +722,7 @@ namespace MWWorld for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { FindContainerFunctor functor(ptr); - (*cellIt)->forEachContainer(functor); + //(*cellIt)->forEachContainer(functor); if (!functor.mResult.isEmpty()) return functor.mResult; @@ -1146,7 +1146,7 @@ namespace MWWorld bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { - newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + newPtr = currCell->moveTo(ptr, newCell); mWorldScene->addObjectToScene(newPtr); std::string script = newPtr.getClass().getScript(newPtr); @@ -1162,14 +1162,14 @@ namespace MWWorld removeContainerScripts (ptr); haveToMove = false; - newPtr = ptr.getClass().copyToCell(ptr, *newCell); + newPtr = currCell->moveTo(ptr, newCell); newPtr.getRefData().setBaseNode(0); } else if (!currCellActive && !newCellActive) - newPtr = ptr.getClass().copyToCell(ptr, *newCell); + newPtr = currCell->moveTo(ptr, newCell); else // both cells active { - newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + newPtr = currCell->moveTo(ptr, newCell); mRendering->updatePtr(ptr, newPtr); ptr.getRefData().setBaseNode(NULL); @@ -1189,7 +1189,6 @@ namespace MWWorld addContainerScripts (newPtr, newCell); } } - ptr.getRefData().setCount(0); } } if (haveToMove && newPtr.getRefData().getBaseNode())