diff --git a/apps/openmw/mwworld/cell.cpp b/apps/openmw/mwworld/cell.cpp index 56b7c1a49a..fa35af41a3 100644 --- a/apps/openmw/mwworld/cell.cpp +++ b/apps/openmw/mwworld/cell.cpp @@ -20,6 +20,7 @@ namespace MWWorld .mWorldspace{ Misc::StringUtils::lowerCase(cell.mEditorId) }, .mIndex{ cell.getGridX(), cell.getGridY() }, .mPaged = isExterior(),} + , mId(cell.mId) ,mMood{ .mAmbiantColor = cell.mLighting.ambient, .mDirectionalColor = cell.mLighting.directional, @@ -41,6 +42,7 @@ namespace MWWorld , mNameID(cell.mName) , mRegion(cell.mRegion) , mCellId(cell.getCellId()) + , mId(cell.mId) , mMood{ .mAmbiantColor = cell.mAmbi.mAmbient, .mDirectionalColor = cell.mAmbi.mSunlight, diff --git a/apps/openmw/mwworld/cell.hpp b/apps/openmw/mwworld/cell.hpp index 52823d4dab..818968fabb 100644 --- a/apps/openmw/mwworld/cell.hpp +++ b/apps/openmw/mwworld/cell.hpp @@ -49,6 +49,7 @@ namespace MWWorld std::string getDescription() const; const MoodData& getMood() const { return mMood; } float getWaterHeight() const { return mWaterHeight; } + const ESM::RefId& getId() const { return mId; }; private: bool mIsExterior; @@ -61,6 +62,7 @@ namespace MWWorld std::string mNameID; // The name that will be used by the script and console commands ESM::RefId mRegion; ESM::CellId mCellId; + ESM::RefId mId; MoodData mMood; float mWaterHeight; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index dce24bfec6..bd0ef53e24 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -468,13 +468,17 @@ namespace MWWorld // Cell //========================================================================= + const ESM::Cell* Store::search(const ESM::RefId& cellId) const + { + auto foundCellIt = mCells.find(cellId); + if (foundCellIt == mCells.end()) + return &foundCellIt->second; + return nullptr; + } + const ESM::Cell* Store::search(const ESM::Cell& cell) const { - if (cell.isExterior()) - { - return search(cell.getGridX(), cell.getGridY()); - } - return search(cell.mName); + return search(cell.mId); } // this method *must* be called right after esm3.loadCell() @@ -521,13 +525,13 @@ namespace MWWorld DynamicInt::const_iterator it = mInt.find(name); if (it != mInt.end()) { - return &(it->second); + return it->second; } DynamicInt::const_iterator dit = mDynamicInt.find(name); if (dit != mDynamicInt.end()) { - return &dit->second; + return dit->second; } return nullptr; @@ -537,11 +541,11 @@ namespace MWWorld std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) - return &(it->second); + return it->second; DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) - return &dit->second; + return dit->second; return nullptr; } @@ -549,7 +553,7 @@ namespace MWWorld { DynamicExt::const_iterator it = mExt.find(std::make_pair(x, y)); if (it != mExt.end()) - return &(it->second); + return (it->second); return nullptr; } const ESM::Cell* Store::searchOrCreate(int x, int y) @@ -557,11 +561,11 @@ namespace MWWorld std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) - return &(it->second); + return (it->second); DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) - return &dit->second; + return dit->second; ESM::Cell newCell; newCell.mData.mX = x; @@ -574,8 +578,11 @@ namespace MWWorld newCell.mCellId.mPaged = true; newCell.mCellId.mIndex.mX = x; newCell.mCellId.mIndex.mY = y; + newCell.updateId(); - return &mExt.insert(std::make_pair(key, newCell)).first->second; + ESM::Cell* newCellInserted = &mCells.insert(std::make_pair(newCell.mId, newCell)).first->second; + + return mExt.insert(std::make_pair(key, newCellInserted)).first->second; } const ESM::Cell* Store::find(std::string_view id) const { @@ -607,12 +614,12 @@ namespace MWWorld mSharedInt.clear(); mSharedInt.reserve(mInt.size()); for (auto& [_, cell] : mInt) - mSharedInt.push_back(&cell); + mSharedInt.push_back(cell); mSharedExt.clear(); mSharedExt.reserve(mExt.size()); for (auto& [_, cell] : mExt) - mSharedExt.push_back(&cell); + mSharedExt.push_back(cell); } RecordId Store::load(ESM::ESMReader& esm) { @@ -622,13 +629,17 @@ namespace MWWorld // are not available until both cells have been loaded at least partially! // All cells have a name record, even nameless exterior cells. - ESM::Cell cell; + ESM::Cell* emplacedCell = nullptr; bool isDeleted = false; + { + ESM::Cell cellToLoad; + cellToLoad.loadNameAndData(esm, isDeleted); + emplacedCell = &mCells.insert(std::make_pair(cellToLoad.mId, cellToLoad)).first->second; + } + ESM::Cell& cell = *emplacedCell; // Load the (x,y) coordinates of the cell, if it is an exterior cell, // so we can find the cell we need to merge with - cell.loadNameAndData(esm, isDeleted); - if (cell.mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. @@ -647,7 +658,7 @@ namespace MWWorld // spawn a new cell cell.loadCell(esm, true); - mInt[cell.mName] = cell; + mInt[cell.mName] = &cell; } } else @@ -700,19 +711,19 @@ namespace MWWorld else { // spawn a new cell - cell.loadCell(esm, false); + emplacedCell->loadCell(esm, false); // handle moved ref (MVRF) subrecords - handleMovedCellRefs(esm, &cell); + handleMovedCellRefs(esm, emplacedCell); // push the new references on the list of references to manage - cell.postLoad(esm); + emplacedCell->postLoad(esm); - mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; + mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = &cell; } } - return RecordId(ESM::RefId::stringRefId(cell.mName), isDeleted); + return RecordId(cell.mId, isDeleted); } Store::iterator Store::intBegin() const { @@ -785,6 +796,7 @@ namespace MWWorld } ESM::Cell* Store::insert(const ESM::Cell& cell) { + ESM::Cell* insertedCell = &mCells.emplace(cell.mId, cell).first->second; if (search(cell) != nullptr) { const std::string cellType = (cell.isExterior()) ? "exterior" : "interior"; @@ -795,16 +807,16 @@ namespace MWWorld std::pair key(cell.getGridX(), cell.getGridY()); // duplicate insertions are avoided by search(ESM::Cell &) - DynamicExt::iterator result = mDynamicExt.emplace(key, cell).first; - mSharedExt.push_back(&result->second); - return &result->second; + DynamicExt::iterator result = mDynamicExt.emplace(key, insertedCell).first; + mSharedExt.push_back(result->second); + return result->second; } else { // duplicate insertions are avoided by search(ESM::Cell &) - DynamicInt::iterator result = mDynamicInt.emplace(cell.mName, cell).first; - mSharedInt.push_back(&result->second); - return &result->second; + DynamicInt::iterator result = mDynamicInt.emplace(cell.mName, insertedCell).first; + mSharedInt.push_back(result->second); + return result->second; } } bool Store::erase(const ESM::Cell& cell) @@ -828,7 +840,7 @@ namespace MWWorld for (it = mDynamicInt.begin(); it != mDynamicInt.end(); ++it) { - mSharedInt.push_back(&it->second); + mSharedInt.push_back(it->second); } return true; @@ -847,7 +859,7 @@ namespace MWWorld for (it = mDynamicExt.begin(); it != mDynamicExt.end(); ++it) { - mSharedExt.push_back(&it->second); + mSharedExt.push_back(it->second); } return true; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index bb511f9635..b73aea0924 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -347,10 +347,12 @@ namespace MWWorld } }; - typedef std::unordered_map + typedef std::unordered_map DynamicInt; - typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; + typedef std::map, ESM::Cell*, DynamicExtCmp> DynamicExt; + + std::unordered_map mCells; DynamicInt mInt; DynamicExt mExt; @@ -367,6 +369,7 @@ namespace MWWorld public: typedef SharedIterator iterator; + const ESM::Cell* search(const ESM::RefId& id) const; const ESM::Cell* search(std::string_view id) const; const ESM::Cell* search(int x, int y) const; const ESM::Cell* searchStatic(int x, int y) const; diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index 79d59de902..506919f177 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -180,6 +180,7 @@ MWWorld::CellStore* MWWorld::WorldModel::getExterior(int x, int y) record.mData.mY = y; record.mWater = 0; record.mMapColor = 0; + record.updateId(); cell = MWBase::Environment::get().getWorld()->createRecord(record); } diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 90294aadaa..4539478e17 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -562,7 +562,7 @@ namespace REGISTER_TYPED_TEST_SUITE_P(StoreSaveLoadTest, shouldNotChangeRefId); - static_assert(std::tuple_size_v == 38); + static_assert(std::tuple_size_v == 39); INSTANTIATE_TYPED_TEST_SUITE_P( RecordTypesTest, StoreSaveLoadTest, typename AsTestingTypes::Type); diff --git a/components/esm3/loadcell.cpp b/components/esm3/loadcell.cpp index a35db8cc1a..1c4477acfd 100644 --- a/components/esm3/loadcell.cpp +++ b/components/esm3/loadcell.cpp @@ -57,6 +57,19 @@ namespace ESM loadCell(esm, saveContext); } + const ESM::RefId& Cell::updateId() + { + if (isExterior()) + { + mId = ESM::RefId::stringRefId("#" + std::to_string(mData.mX) + "," + std::to_string(mData.mY)); + } + else + { + mId = ESM::RefId::stringRefId(mName); + } + return mId; + } + void Cell::loadNameAndData(ESMReader& esm, bool& isDeleted) { isDeleted = false; @@ -105,6 +118,7 @@ namespace ESM mCellId.mIndex.mX = 0; mCellId.mIndex.mY = 0; } + updateId(); } void Cell::loadCell(ESMReader& esm, bool saveContext) diff --git a/components/esm3/loadcell.hpp b/components/esm3/loadcell.hpp index 3cf1834dfa..f49f4f083a 100644 --- a/components/esm3/loadcell.hpp +++ b/components/esm3/loadcell.hpp @@ -110,7 +110,7 @@ namespace ESM , mRefNumCounter(0) { } - + ESM::RefId mId; // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. std::string mName; @@ -192,6 +192,7 @@ namespace ESM ///< Set record to default state (does not touch the ID/index). const CellId& getCellId() const; + const ESM::RefId& updateId(); }; } #endif