diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 03ce71f5c..0113fed02 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -62,7 +62,7 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata worldimp scene globals class action nullaction actionteleport - containerstore actiontalk actiontake manualref player cellfunctors failedaction + containerstore actiontalk actiontake manualref player cellvisitors failedaction cells localscripts customdata inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 3a0f1b951..17757cb6b 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -130,6 +130,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index f93556ef9..07bf25086 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -148,7 +148,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Apparatus::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 324dd32ee..631ddd912 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -382,7 +382,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 2c20435b2..9ea9e659b 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -187,7 +187,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index cea30d561..7250e1837 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -276,7 +276,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index bfdcc23c1..b600cf353 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -293,7 +293,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2cd11d113..e5a98c889 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -600,7 +600,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Creature::isBipedal(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 56c5e655f..fffa8d1fd 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -310,7 +310,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } void Door::ensureCustomData(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c9e6e70f2..db2f2410b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -185,7 +185,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Ingredient::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 34d93da67..fe5149077 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -217,7 +217,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 5cffdf13a..63f75a845 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -166,7 +166,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 98b4faab9..a14ab3330 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -215,13 +215,14 @@ namespace MWClass MWWorld::ManualRef newRef(store, base); MWWorld::LiveCellRef *ref = newRef.getPtr().get(); - newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); + + newPtr = MWWorld::Ptr(cell.insert(ref), &cell); newPtr.getCellRef().setGoldValue(goldAmount); newPtr.getRefData().setCount(1); } else { MWWorld::LiveCellRef *ref = ptr.get(); - newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); + newPtr = MWWorld::Ptr(cell.insert(ref), &cell); } return newPtr; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ec37588d9..41894c1dc 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1130,7 +1130,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index cf6b0919b..20a849019 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -178,7 +178,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Potion::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index ff717c506..79f423b30 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -165,7 +165,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } bool Probe::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index e6baea2e0..78ec2adcc 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -160,7 +160,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 9755df28e..86019a5ad 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -60,6 +60,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index da4c7deb2..5665bf1c4 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -417,7 +417,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.get().insert(*ref), &cell); + return MWWorld::Ptr(cell.insert(ref), &cell); } int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index dae5f8496..5815d8cbe 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -44,9 +44,9 @@ namespace MWMechanics return MWWorld::Ptr(); // check interior cells only // Check all the doors in this cell - MWWorld::CellRefList& doors = cell->get(); - MWWorld::CellRefList::List& refList = doors.mList; - MWWorld::CellRefList::List::iterator it = refList.begin(); + const MWWorld::CellRefList& doors = cell->getReadOnlyDoors(); + const MWWorld::CellRefList::List& refList = doors.mList; + MWWorld::CellRefList::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); /// TODO: How to check whether the actor is facing a door? Below code is for @@ -59,11 +59,12 @@ namespace MWMechanics /// opposite of the code in World::activateDoor() ::confused:: for (; it != refList.end(); ++it) { - MWWorld::LiveCellRef& ref = *it; + const MWWorld::LiveCellRef& ref = *it; if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr && ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2]) { - return MWWorld::Ptr(&ref, actor.getCell()); // found, stop searching + // FIXME cast + return MWWorld::Ptr(&const_cast &>(ref), actor.getCell()); // found, stop searching } } return MWWorld::Ptr(); // none found diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 474f2a392..60847e745 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -300,14 +300,16 @@ namespace MWScript } catch(std::exception&) { + // cell not found, move to exterior instead (vanilla PositionCell compatibility) const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); if(!cell) { - runtime.getContext().report ("unknown cell (" + cellID + ")"); - std::cerr << "unknown cell (" << cellID << ")\n"; + std::string error = "PositionCell: unknown interior cell (" + cellID + "), moving to exterior instead"; + runtime.getContext().report (error); + std::cerr << error << std::endl; } } if(store) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index b096301fd..109447959 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,5 +1,7 @@ #include "cells.hpp" +#include + #include #include #include @@ -23,7 +25,7 @@ MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } return &result->second; @@ -36,7 +38,7 @@ MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) if (result==mExteriors.end()) { result = mExteriors.insert (std::make_pair ( - std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell))).first; + std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell, mStore, mReader))).first; } @@ -70,7 +72,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const { if (cell.getState()!=CellStore::State_Loaded) - cell.load (mStore, mReader); + cell.load (); ESM::CellState cellState; @@ -114,13 +116,12 @@ MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y) } result = mExteriors.insert (std::make_pair ( - std::make_pair (x, y), CellStore (cell))).first; + std::make_pair (x, y), CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { - // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. - result->second.load (mStore, mReader); + result->second.load (); } return &result->second; @@ -135,12 +136,12 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) { const ESM::Cell *cell = mStore.get().find(lowerName); - result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell, mStore, mReader))).first; } if (result->second.getState()!=CellStore::State_Loaded) { - result->second.load (mStore, mReader); + result->second.load (); } return &result->second; @@ -158,13 +159,13 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { if (cell.getState()==CellStore::State_Unloaded) - cell.preload (mStore, mReader); + cell.preload (); if (cell.getState()==CellStore::State_Preloaded) { if (cell.hasId (name)) { - cell.load (mStore, mReader); + cell.load (); } else return Ptr(); @@ -304,6 +305,29 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) } } +struct GetCellStoreCallback : public MWWorld::CellStore::GetCellStoreCallback +{ +public: + GetCellStoreCallback(MWWorld::Cells& cells) + : mCells(cells) + { + } + + MWWorld::Cells& mCells; + + virtual MWWorld::CellStore* getCellStore(const ESM::CellId& cellId) + { + try + { + return mCells.getCell(cellId); + } + catch (...) + { + return NULL; + } + } +}; + bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { @@ -321,9 +345,9 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, catch (...) { // silently drop cells that don't exist anymore + std::cerr << "Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)" << std::endl; reader.skipRecord(); return true; - /// \todo log } state.load (reader); @@ -333,9 +357,11 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, cellStore->readFog(reader); if (cellStore->getState()!=CellStore::State_Loaded) - cellStore->load (mStore, mReader); + cellStore->load (); + + GetCellStoreCallback callback(*this); - cellStore->readReferences (reader, contentFileMap); + cellStore->readReferences (reader, contentFileMap, &callback); return true; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 029dac22d..2173820ad 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; } @@ -141,6 +144,28 @@ namespace ref.load (state); collection.mList.push_back (ref); } + + struct SearchByRefNumVisitor + { + MWWorld::LiveCellRefBase* mFound; + ESM::RefNum mRefNumToFind; + + SearchByRefNumVisitor(const ESM::RefNum& toFind) + : mFound(NULL) + , mRefNumToFind(toFind) + { + } + + bool operator()(const MWWorld::Ptr& ptr) + { + if (ptr.getCellRef().getRefNum() == mRefNumToFind) + { + mFound = ptr.getBase(); + return false; + } + return true; + } + }; } namespace MWWorld @@ -179,8 +204,121 @@ namespace MWWorld return (ref.mRef.mRefnum == pRefnum); } - CellStore::CellStore (const ESM::Cell *cell) - : mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) + void CellStore::moveFrom(const Ptr &object, CellStore *from) + { + if (mState != State_Loaded) + load(); + + mHasState = true; + MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); + if (found != mMovedToAnotherCell.end()) + { + // A cell we had previously moved an object to is returning it to us. + assert (found->second == from); + mMovedToAnotherCell.erase(found); + } + else + { + mMovedHere.insert(std::make_pair(object.getBase(), from)); + } + updateMergedRefs(); + } + + MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) + { + if (cellToMoveTo == this) + throw std::runtime_error("moveTo: 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("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); + + // Ensure that the object actually exists in the cell + SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); + forEach(searchVisitor); + if (!searchVisitor.mFound) + throw std::runtime_error("moveTo: object is not in this cell"); + + + // Objects with no refnum can't be handled correctly in the merging process that happens + // on a save/load, so do a simple copy & delete for these objects. + if (!object.getCellRef().getRefNum().hasContentFile()) + { + MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo); + object.getRefData().setCount(0); + object.getRefData().setBaseNode(NULL); + return copied; + } + + MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); + if (found != mMovedHere.end()) + { + // Special case - object didn't originate in this cell + // Move it back to its original cell first + CellStore* originalCell = found->second; + assert (originalCell != this); + originalCell->moveFrom(object, this); + + mMovedHere.erase(found); + + // Now that object is back to its rightful owner, we can move it + if (cellToMoveTo != originalCell) + { + originalCell->moveTo(object, cellToMoveTo); + } + + updateMergedRefs(); + 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 MergeVisitor + { + MergeVisitor(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(); + MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); + forEachInternal(visitor); + visitor.merge(); + } + + CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) + : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } @@ -212,85 +350,50 @@ namespace MWWorld return const_cast (this)->search (id).isEmpty(); } + struct SearchVisitor + { + 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); + SearchVisitor searchVisitor; + searchVisitor.mIdToFind = id; + forEach(searchVisitor); mHasState = oldState; - - return Ptr(); + return searchVisitor.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(); } @@ -309,37 +412,17 @@ namespace MWWorld int CellStore::count() const { - return - mActivators.mList.size() - + mPotions.mList.size() - + mAppas.mList.size() - + mArmors.mList.size() - + mBooks.mList.size() - + mClothes.mList.size() - + mContainers.mList.size() - + mDoors.mList.size() - + mIngreds.mList.size() - + mCreatureLists.mList.size() - + mItemLists.mList.size() - + mLights.mList.size() - + mLockpicks.mList.size() - + mMiscItems.mList.size() - + mProbes.mList.size() - + mRepairs.mList.size() - + mStatics.mList.size() - + mWeapons.mList.size() - + mCreatures.mList.size() - + mNpcs.mList.size(); - } - - void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) + return mMergedRefs.size(); + } + + void CellStore::load () { if (mState!=State_Loaded) { if (mState==State_Preloaded) mIds.clear(); - loadRefs (store, esm); + loadRefs (); mState = State_Loaded; @@ -349,18 +432,20 @@ namespace MWWorld } } - void CellStore::preload (const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::preload () { if (mState==State_Unloaded) { - listRefs (store, esm); + listRefs (); mState = State_Preloaded; } } - void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::listRefs() { + std::vector& esm = mReader; + assert (mCell); if (mCell->mContextList.empty()) @@ -404,8 +489,10 @@ namespace MWWorld std::sort (mIds.begin(), mIds.end()); } - void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::loadRefs() { + std::vector& esm = mReader; + assert (mCell); if (mCell->mContextList.empty()) @@ -432,7 +519,7 @@ namespace MWWorld continue; } - loadRef (ref, deleted, store); + loadRef (ref, deleted); } } @@ -441,8 +528,10 @@ namespace MWWorld { ESM::CellRef &ref = const_cast(*it); - loadRef (ref, false, store); + loadRef (ref, false); } + + updateMergedRefs(); } bool CellStore::isExterior() const @@ -470,10 +559,12 @@ namespace MWWorld return Ptr(); } - void CellStore::loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store) + void CellStore::loadRef (ESM::CellRef& ref, bool deleted) { Misc::StringUtils::lowerCaseInPlace (ref.mRefID); + const MWWorld::ESMStore& store = mStore; + switch (store.find (ref.mRefID)) { case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break; @@ -564,10 +655,19 @@ namespace MWWorld writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); + + for (MovedRefTracker::const_iterator it = mMovedToAnotherCell.begin(); it != mMovedToAnotherCell.end(); ++it) + { + LiveCellRefBase* base = it->first; + ESM::RefNum refNum = base->mRef.getRefNum(); + ESM::CellId movedTo = it->second->getCell()->getCellId(); + + refNum.save(writer, true, "MVRF"); + movedTo.save(writer); + } } - void CellStore::readReferences (ESM::ESMReader& reader, - const std::map& contentFileMap) + void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; @@ -695,6 +795,48 @@ namespace MWWorld throw std::runtime_error ("unknown type in cell reference section"); } } + + while (reader.isNextSub("MVRF")) + { + reader.cacheSubName(); + ESM::RefNum refnum; + ESM::CellId movedTo; + refnum.load(reader, true, "MVRF"); + movedTo.load(reader); + + // Search for the reference. It might no longer exist if its content file was removed. + SearchByRefNumVisitor visitor(refnum); + forEachInternal(visitor); + + if (!visitor.mFound) + { + std::cout << "Dropping moved ref tag for " << visitor.mFound->mRef.getRefId() << " (moved object no longer exists)" << std::endl; + continue; + } + + CellStore* otherCell = callback->getCellStore(movedTo); + + if (otherCell == NULL) + { + std::cerr << "Dropping moved ref tag for " << visitor.mFound->mRef.getRefId() + << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; + // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. + // Restore original coordinates: + visitor.mFound->mData.setPosition(visitor.mFound->mRef.getPosition()); + continue; + } + + if (otherCell == this) + { + // Should never happen unless someone's tampering with files. + std::cerr << "Found invalid moved ref, ignoring" << std::endl; + continue; + } + + moveTo(MWWorld::Ptr(visitor.mFound, this), otherCell); + } + + updateMergedRefs(); } bool operator== (const CellStore& left, const CellStore& right) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index f88bf0958..b08349293 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -40,6 +40,7 @@ namespace ESM { struct CellState; struct FogState; + struct CellId; } namespace MWWorld @@ -60,6 +61,9 @@ namespace MWWorld private: + const MWWorld::ESMStore& mStore; + std::vector& mReader; + // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. // Note this is NULL until the cell is explored to save some memory @@ -73,6 +77,7 @@ namespace MWWorld MWWorld::TimeStamp mLastRespawn; + // List of refs owned by this cell CellRefList mActivators; CellRefList mPotions; CellRefList mAppas; @@ -94,9 +99,95 @@ namespace MWWorld CellRefList mStatics; CellRefList mWeapons; + typedef std::map MovedRefTracker; + // References owned by a different cell that have been moved here. + // + MovedRefTracker mMovedHere; + // References owned by this cell that have been moved to another cell. + // + MovedRefTracker mMovedToAnotherCell; + + // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell + std::vector mMergedRefs; + + /// Moves object from the given cell to this cell. + void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); + + /// Repopulate mMergedRefs. + void updateMergedRefs(); + + // helper function for forEachInternal + template + bool forEachImp (Visitor& visitor, List& list) + { + for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); + ++iter) + { + if (iter->mData.isDeletedByContentFile()) + continue; + if (!visitor (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 (Visitor& visitor) + { + return + forEachImp (visitor, mActivators) && + forEachImp (visitor, mPotions) && + forEachImp (visitor, mAppas) && + forEachImp (visitor, mArmors) && + forEachImp (visitor, mBooks) && + forEachImp (visitor, mClothes) && + forEachImp (visitor, mContainers) && + forEachImp (visitor, mDoors) && + forEachImp (visitor, mIngreds) && + forEachImp (visitor, mItemLists) && + forEachImp (visitor, mLights) && + forEachImp (visitor, mLockpicks) && + forEachImp (visitor, mMiscItems) && + forEachImp (visitor, mProbes) && + forEachImp (visitor, mRepairs) && + forEachImp (visitor, mStatics) && + forEachImp (visitor, mWeapons) && + forEachImp (visitor, mCreatures) && + forEachImp (visitor, mNpcs) && + forEachImp (visitor, mCreatureLists); + } + + /// @note If you get a linker error here, this means the given type can not be stored in a cell. The supported types are + /// defined at the bottom of this file. + template + CellRefList& get(); + public: - CellStore (const ESM::Cell *cell_); + /// Moves object from this cell to the given cell. + /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...) + /// @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. + /// The supported types are defined at the bottom of this file. + template + LiveCellRefBase* insert(const LiveCellRef* ref) + { + mHasState = true; + CellRefList& list = get(); + LiveCellRefBase* ret = &list.insert(*ref); + updateMergedRefs(); + return ret; + } + + /// @param readerList The readers to use for loading of the cell on-demand. + CellStore (const ESM::Cell *cell_, + const MWWorld::ESMStore& store, + std::vector& readerList); const ESM::Cell *getCell() const; @@ -108,6 +199,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 @@ -128,55 +220,82 @@ namespace MWWorld int count() const; ///< Return total number of references, including deleted ones. - void load (const MWWorld::ESMStore &store, std::vector &esm); + void load (); ///< Load references from content file. - void preload (const MWWorld::ESMStore &store, std::vector &esm); + void preload (); ///< Build ID list from content file. - /// Call functor (ref) for each reference. functor must return a bool. Returning + /// Call visitor (ref) for each reference. visitor must return a bool. Returning /// 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) + template + bool forEach (Visitor& visitor) { + 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; + + if (!visitor(MWWorld::Ptr(mMergedRefs[i], this))) + return false; + } + return true; } - template - bool forEachContainer (Functor& functor) + /// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning + /// false will abort the iteration. + /// \attention This function also lists deleted (count 0) objects! + /// \return Iteration completed? + template + bool forEachType(Visitor& visitor) { + if (mState != State_Loaded) + return false; + mHasState = true; - return - forEachImp (functor, mContainers) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs); + CellRefList& list = get(); + + for (typename CellRefList::List::iterator it (list.mList.begin()); it!=list.mList.end(); ++it) + { + LiveCellRefBase* base = &*it; + if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end()) + continue; + if (base->mData.isDeletedByContentFile()) + continue; + if (!visitor(MWWorld::Ptr(base, this))) + return false; + } + + for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + { + LiveCellRefBase* base = it->first; + if (dynamic_cast*>(base)) + if (!visitor(MWWorld::Ptr(base, this))) + return false; + } + return true; + } + + /// \todo add const version of forEach + + + // NOTE: does not account for moved references + // Should be phased out when we have const version of forEach + inline const CellRefList& getReadOnlyDoors() const + { + return mDoors; + } + inline const CellRefList& getReadOnlyStatics() const + { + return mStatics; } bool isExterior() const; @@ -193,47 +312,31 @@ namespace MWWorld void writeReferences (ESM::ESMWriter& writer) const; - void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + struct GetCellStoreCallback + { + public: + ///@note must return NULL if the cell is not found + virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; + }; + + /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references) + void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback); void respawn (); ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. - template - CellRefList& get() { - throw std::runtime_error ("Storage for type " + std::string(typeid(T).name())+ " does not exist in cells"); - } - - template - const CellRefList& getReadOnly() { - throw std::runtime_error ("Read Only CellRefList access not available for type " + std::string(typeid(T).name()) ); - } - bool isPointConnected(const int start, const int end) const; std::list aStarSearch(const int start, const int end) const; 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); + void listRefs(); - void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + void loadRefs(); - void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); + void loadRef (ESM::CellRef& ref, bool deleted); ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. @@ -381,12 +484,6 @@ namespace MWWorld return mWeapons; } - template<> - inline const CellRefList& CellStore::getReadOnly() - { - return mDoors; - } - bool operator== (const CellStore& left, const CellStore& right); bool operator!= (const CellStore& left, const CellStore& right); } diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellvisitors.hpp similarity index 78% rename from apps/openmw/mwworld/cellfunctors.hpp rename to apps/openmw/mwworld/cellvisitors.hpp index c7fdc793c..cfb07f749 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellvisitors.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWWORLD_CELLFUNCTORS_H -#define GAME_MWWORLD_CELLFUNCTORS_H +#ifndef GAME_MWWORLD_CELLVISITORS_H +#define GAME_MWWORLD_CELLVISITORS_H #include #include @@ -9,7 +9,7 @@ namespace MWWorld { - struct ListAndResetObjects + struct ListAndResetObjectsVisitor { std::vector mObjects; diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index e30246f7c..030a41891 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -11,46 +11,54 @@ namespace { - template - void listCellScripts (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) + + struct AddScriptsVisitor { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) + AddScriptsVisitor(MWWorld::LocalScripts& scripts) + : mScripts(scripts) { - if (!iter->mBase->mScript.empty() && !iter->mData.isDeleted()) - { - localScripts.add (iter->mBase->mScript, MWWorld::Ptr (&*iter, cell)); - } } - } + MWWorld::LocalScripts& mScripts; - // Adds scripts for items in containers (containers/npcs/creatures) - template - void listCellScriptsCont (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) - { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) + bool operator()(const MWWorld::Ptr& ptr) { + if (ptr.getRefData().isDeleted()) + return true; + + std::string script = ptr.getClass().getScript(ptr); + + if (!script.empty()) + mScripts.add(script, ptr); + + return true; + } + }; - MWWorld::Ptr containerPtr (&*iter, cell); + struct AddContainerItemScriptsVisitor + { + AddContainerItemScriptsVisitor(MWWorld::LocalScripts& scripts) + : mScripts(scripts) + { + } + MWWorld::LocalScripts& mScripts; + bool operator()(const MWWorld::Ptr& containerPtr) + { MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr); - for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3) + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { - std::string script = it3->getClass().getScript(*it3); + std::string script = it->getClass().getScript(*it); if(script != "") { - MWWorld::Ptr item = *it3; - item.mCell = cell; - localScripts.add (script, item); + MWWorld::Ptr item = *it; + item.mCell = containerPtr.getCell(); + mScripts.add (script, item); } } + return true; } - } + }; + } MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) {} @@ -116,26 +124,13 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) void MWWorld::LocalScripts::addCell (CellStore *cell) { - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScriptsCont (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); - listCellScripts (*this, cell->get(), cell); + AddScriptsVisitor addScriptsVisitor(*this); + cell->forEach(addScriptsVisitor); + + AddContainerItemScriptsVisitor addContainerItemScriptsVisitor(*this); + cell->forEachType(addContainerItemScriptsVisitor); + cell->forEachType(addContainerItemScriptsVisitor); + cell->forEachType(addContainerItemScriptsVisitor); } void MWWorld::LocalScripts::clear() diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 8acba43df..997b3fc94 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -182,7 +182,7 @@ namespace MWWorld mPosition = pos; } - const ESM::Position& RefData::getPosition() + const ESM::Position& RefData::getPosition() const { return mPosition; } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 6713334d7..19c31a14b 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -103,7 +103,7 @@ namespace MWWorld void disable(); void setPosition (const ESM::Position& pos); - const ESM::Position& getPosition(); + const ESM::Position& getPosition() const; void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \a data is diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 45c94b6d9..00cb8a810 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -22,7 +22,7 @@ #include "localscripts.hpp" #include "esmstore.hpp" #include "class.hpp" -#include "cellfunctors.hpp" +#include "cellvisitors.hpp" #include "cellstore.hpp" namespace @@ -78,7 +78,7 @@ namespace } } - struct InsertFunctor + struct InsertVisitor { MWWorld::CellStore& mCell; bool mRescale; @@ -86,13 +86,13 @@ namespace MWPhysics::PhysicsSystem& mPhysics; MWRender::RenderingManager& mRendering; - InsertFunctor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, + InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering); bool operator() (const MWWorld::Ptr& ptr); }; - InsertFunctor::InsertFunctor (MWWorld::CellStore& cell, bool rescale, + InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), @@ -100,7 +100,7 @@ namespace mRendering (rendering) {} - bool InsertFunctor::operator() (const MWWorld::Ptr& ptr) + bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) { if (mRescale) { @@ -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 AdjustPositionVisitor + { + bool operator() (const MWWorld::Ptr& ptr) + { + if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) + ptr.getClass().adjustPosition (ptr, false); + return true; + } + }; + } @@ -196,11 +206,11 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListAndResetObjects functor; + ListAndResetObjectsVisitor visitor; - (*iter)->forEach(functor); - for (std::vector::const_iterator iter2 (functor.mObjects.begin()); - iter2!=functor.mObjects.end(); ++iter2) + (*iter)->forEach(visitor); + for (std::vector::const_iterator iter2 (visitor.mObjects.begin()); + iter2!=visitor.mObjects.end(); ++iter2) { mPhysics->remove(*iter2); } @@ -551,8 +561,12 @@ namespace MWWorld void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { - InsertFunctor functor (cell, rescale, *loadingListener, *mPhysics, mRendering); - cell.forEach (functor); + InsertVisitor insertVisitor (cell, rescale, *loadingListener, *mPhysics, mRendering); + cell.forEach (insertVisitor); + + // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order + AdjustPositionVisitor adjustPosVisitor; + cell.forEach (adjustPosVisitor); } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e8855fa5c..9a32c63a3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -54,7 +54,6 @@ #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" -#include "cellfunctors.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" #include "actionteleport.hpp" @@ -690,12 +689,12 @@ namespace MWWorld return mWorldScene->searchPtrViaActorId (actorId); } - struct FindContainerFunctor + struct FindContainerVisitor { Ptr mContainedPtr; Ptr mResult; - FindContainerFunctor(const Ptr& containedPtr) : mContainedPtr(containedPtr) {} + FindContainerVisitor(const Ptr& containedPtr) : mContainedPtr(containedPtr) {} bool operator() (Ptr ptr) { @@ -721,11 +720,15 @@ namespace MWWorld const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - FindContainerFunctor functor(ptr); - (*cellIt)->forEachContainer(functor); + FindContainerVisitor visitor(ptr); + (*cellIt)->forEachType(visitor); + if (visitor.mResult.isEmpty()) + (*cellIt)->forEachType(visitor); + if (visitor.mResult.isEmpty()) + (*cellIt)->forEachType(visitor); - if (!functor.mResult.isEmpty()) - return functor.mResult; + if (!visitor.mResult.isEmpty()) + return visitor.mResult; } return Ptr(); @@ -1149,7 +1152,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); @@ -1165,17 +1168,16 @@ 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); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr); mPhysics->updatePtr(ptr, newPtr); @@ -1192,7 +1194,6 @@ namespace MWWorld addContainerScripts (newPtr, newCell); } } - ptr.getRefData().setCount(0); } } if (haveToMove && newPtr.getRefData().getBaseNode()) @@ -1708,27 +1709,32 @@ namespace MWWorld osg::Vec2f World::getNorthVector (CellStore* cell) { - MWWorld::CellRefList& statics = cell->get(); - MWWorld::LiveCellRef* ref = statics.find("northmarker"); - if (!ref) + MWWorld::Ptr northmarker = cell->search("northmarker"); + + if (northmarker.isEmpty()) return osg::Vec2f(0, 1); - osg::Quat orient (-ref->mData.getPosition().rot[2], osg::Vec3f(0,0,1)); + osg::Quat orient (-northmarker.getRefData().getPosition().rot[2], osg::Vec3f(0,0,1)); osg::Vec3f dir = orient * osg::Vec3f(0,1,0); osg::Vec2f d (dir.x(), dir.y()); return d; } - void World::getDoorMarkers (CellStore* cell, std::vector& out) + struct GetDoorMarkerVisitor { - MWWorld::CellRefList& doors = cell->get(); - CellRefList::List& refList = doors.mList; - for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + GetDoorMarkerVisitor(std::vector& out) + : mOut(out) { - MWWorld::LiveCellRef& ref = *it; + } - if (!ref.mData.isEnabled()) - continue; + std::vector& mOut; + + bool operator()(const MWWorld::Ptr& ptr) + { + MWWorld::LiveCellRef& ref = *static_cast* >(ptr.getBase()); + + if (!ref.mData.isEnabled() || ref.mData.isDeleted()) + return true; if (ref.mRef.getTeleport()) { @@ -1744,7 +1750,7 @@ namespace MWWorld else { cellid.mPaged = true; - positionToIndex( + MWBase::Environment::get().getWorld()->positionToIndex( ref.mRef.getDoorDest().pos[0], ref.mRef.getDoorDest().pos[1], cellid.mIndex.mX, @@ -1756,9 +1762,16 @@ namespace MWWorld newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; - out.push_back(newMarker); + mOut.push_back(newMarker); } + return true; } + }; + + void World::getDoorMarkers (CellStore* cell, std::vector& out) + { + GetDoorMarkerVisitor visitor(out); + cell->forEachType(visitor); } void World::setWaterHeight(const float height) @@ -2253,25 +2266,40 @@ namespace MWWorld return osg::Vec3f(0,1,0); } - void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + struct GetContainersOwnedByVisitor + { + GetContainersOwnedByVisitor(const MWWorld::Ptr& owner, std::vector& out) + : mOwner(owner) + , mOut(out) + { + } + + MWWorld::Ptr mOwner; + std::vector& mOut; + + bool operator()(const MWWorld::Ptr& ptr) + { + if (ptr.getRefData().isDeleted()) + return true; + + if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId())) + mOut.push_back(ptr); + + return true; + } + }; + + void World::getContainersOwnedBy (const MWWorld::Ptr& owner, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - MWWorld::CellRefList& containers = (*cellIt)->get(); - CellRefList::List& refList = containers.mList; - for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) - { - MWWorld::Ptr ptr (&*container, *cellIt); - if (ptr.getRefData().isDeleted()) - continue; - if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), npc.getCellRef().getRefId())) - out.push_back(ptr); - } + GetContainersOwnedByVisitor visitor (owner, out); + (*cellIt)->forEachType(visitor); } } - struct ListObjectsFunctor + struct ListObjectsVisitor { std::vector mObjects; @@ -2288,10 +2316,10 @@ namespace MWWorld const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - ListObjectsFunctor functor; - (*cellIt)->forEach(functor); + ListObjectsVisitor visitor; + (*cellIt)->forEach(visitor); - for (std::vector::iterator it = functor.mObjects.begin(); it != functor.mObjects.end(); ++it) + for (std::vector::iterator it = visitor.mObjects.begin(); it != visitor.mObjects.end(); ++it) if (Misc::StringUtils::ciEqual(it->getCellRef().getOwner(), npc.getCellRef().getRefId())) out.push_back(*it); } @@ -2342,7 +2370,8 @@ namespace MWWorld if (0 == cellStore) { return false; } - const DoorList &doors = cellStore->get().mList; + + const DoorList &doors = cellStore->getReadOnlyDoors().mList; for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { if (!it->mRef.getTeleport()) { continue; @@ -2364,7 +2393,7 @@ namespace MWWorld if (0 != source) { // Find door leading to our current teleport door // and use it destination to position inside cell. - const DoorList &doors = source->get().mList; + const DoorList &doors = source->getReadOnlyDoors().mList; for (DoorList::const_iterator jt = doors.begin(); jt != doors.end(); ++jt) { if (it->mRef.getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) @@ -2378,7 +2407,7 @@ namespace MWWorld } } // Fall back to the first static location. - const StaticList &statics = cellStore->get().mList; + const StaticList &statics = cellStore->getReadOnlyStatics().mList; if ( statics.begin() != statics.end() ) { pos = statics.begin()->mRef.getPosition(); return true; @@ -2747,7 +2776,7 @@ namespace MWWorld MWWorld::CellStore *next = getInterior( *i ); if ( !next ) continue; - const MWWorld::CellRefList& doors = next->getReadOnly(); + const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2807,7 +2836,7 @@ namespace MWWorld return closestMarker; } - const MWWorld::CellRefList& doors = next->getReadOnly(); + const MWWorld::CellRefList& doors = next->getReadOnlyDoors(); const CellRefList::List& doorList = doors.mList; // Check if any door in the cell leads to an exterior directly @@ -2831,7 +2860,6 @@ namespace MWWorld } } } - return MWWorld::Ptr(); } @@ -2888,9 +2916,9 @@ namespace MWWorld mWeatherManager->update(duration, paused); } - struct AddDetectedReference + struct AddDetectedReferenceVisitor { - AddDetectedReference(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) + AddDetectedReferenceVisitor(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) : mOut(out), mDetector(detector), mSquaredDist(squaredDist), mType(type) { } @@ -2899,7 +2927,7 @@ namespace MWWorld Ptr mDetector; float mSquaredDist; World::DetectionType mType; - bool operator() (MWWorld::Ptr ptr) + bool operator() (const MWWorld::Ptr& ptr) { if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2() >= mSquaredDist) return true; @@ -2930,7 +2958,7 @@ namespace MWWorld return true; } - bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector) + bool needToAdd (const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector) { if (mType == World::Detect_Creature) { @@ -2970,13 +2998,13 @@ namespace MWWorld dist = feetToGameUnits(dist); - AddDetectedReference functor (out, ptr, type, dist*dist); + AddDetectedReferenceVisitor visitor (out, ptr, type, dist*dist); const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) { MWWorld::CellStore* cellStore = *it; - cellStore->forEach(functor); + cellStore->forEach(visitor); } } @@ -3232,7 +3260,7 @@ namespace MWWorld interpreterContext.executeActivation(object, actor); } - struct ResetActorsFunctor + struct ResetActorsVisitor { bool operator() (Ptr ptr) { @@ -3254,8 +3282,8 @@ namespace MWWorld iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; - ResetActorsFunctor functor; - cellstore->forEach(functor); + ResetActorsVisitor visitor; + cellstore->forEach(visitor); } } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 76a82fe23..7acaed86d 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -3,12 +3,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -void ESM::RefNum::load (ESMReader& esm, bool wide) +void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag) { if (wide) - esm.getHNT (*this, "FRMR", 8); + esm.getHNT (*this, tag.c_str(), 8); else - esm.getHNT (mIndex, "FRMR"); + esm.getHNT (mIndex, tag.c_str()); } void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index c371a4f01..3c646cc61 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -16,7 +16,7 @@ namespace ESM unsigned int mIndex; int mContentFile; - void load (ESMReader& esm, bool wide = false); + void load (ESMReader& esm, bool wide = false, const std::string& tag = "FRMR"); void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const;