From 73be45780585e1c3427a35b47c70a0b5e22fe26c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 15:37:05 +0200 Subject: [PATCH] Merge ESM::Cell fields by subrecord Fixes an issue with the Morrowind Patched mod where cell ambient values would become black due to the new cell records not including an AMBI subrecord. Also fixes a bug where mLeasedRefs was incorrectly cleared when overwriting a cell (*oldcell = *cell;) --- apps/openmw/mwworld/store.cpp | 87 ++++++++++++++++++++--------------- apps/openmw/mwworld/store.hpp | 2 + components/esm/loadcell.cpp | 55 +++++++++------------- components/esm/loadcell.hpp | 12 ++--- 4 files changed, 82 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 1156cbc15..19b204073 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -3,22 +3,8 @@ namespace MWWorld { - -void Store::load(ESM::ESMReader &esm, const std::string &id) +void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) { - // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, - // and we merge all this data into one Cell object. However, we can't simply search for the cell id, - // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they - // are not available until both cells have been loaded! So first, proceed as usual. - - // All cells have a name record, even nameless exterior cells. - std::string idLower = Misc::StringUtils::lowerCase(id); - ESM::Cell *cell = new ESM::Cell; - cell->mName = id; - - //First part of cell loading - cell->preLoad(esm); - //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; @@ -43,35 +29,56 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) else *iter = ref; } +} + +void Store::load(ESM::ESMReader &esm, const std::string &id) +{ + // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, + // and we merge all this data into one Cell object. However, we can't simply search for the cell id, + // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they + // are not available until both cells have been loaded at least partially! + + // All cells have a name record, even nameless exterior cells. + std::string idLower = Misc::StringUtils::lowerCase(id); + ESM::Cell cell; + cell.mName = id; - //Second part of cell loading - cell->postLoad(esm); + // 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.loadData(esm); - if(cell->mData.mFlags & ESM::Cell::Interior) + if(cell.mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(idLower)); if (oldcell) { - // push the new references on the list of references to manage - oldcell->mContextList.push_back(cell->mContextList.at(0)); - // copy list into new cell - cell->mContextList = oldcell->mContextList; - // have new cell replace old cell - ESM::Cell::merge(oldcell, cell); + // merge new cell into old cell + // push the new references on the list of references to manage (saveContext = true) + oldcell->mData = cell.mData; + oldcell->loadCell(esm, true); } else - mInt[idLower] = *cell; + { + // spawn a new cell + cell.loadCell(esm, true); + + mInt[idLower] = cell; + } } else { // Store exterior cells by grid position, try to merge with existing parent data. - ESM::Cell *oldcell = const_cast(search(cell->getGridX(), cell->getGridY())); + ESM::Cell *oldcell = const_cast(search(cell.getGridX(), cell.getGridY())); if (oldcell) { - // push the new references on the list of references to manage - oldcell->mContextList.push_back(cell->mContextList.at(0)); - // copy list into new cell - cell->mContextList = oldcell->mContextList; + // merge new cell into old cell + // push the new references on the list of references to manage (saveContext = true) + oldcell->mData = cell.mData; + oldcell->loadCell(esm, true); + + // handle moved ref (MVRF) subrecords + handleMovedCellRefs (esm, &cell); + // merge lists of leased references, use newer data in case of conflict - for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) { + for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { @@ -82,13 +89,21 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) *itold = *it; } } - cell->mMovedRefs = oldcell->mMovedRefs; - // have new cell replace old cell - ESM::Cell::merge(oldcell, cell); + + // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a + // reference to this cell, so the list for the new cell should be empty. The list for oldcell, + // however, could have leased refs in it and so should be kept. } else - mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; + { + // spawn a new cell + cell.loadCell(esm, true); + + // handle moved ref (MVRF) subrecords + handleMovedCellRefs (esm, &cell); + + mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; + } } - delete cell; } } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 1dfb2f976..a04267f49 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -591,6 +591,8 @@ namespace MWWorld return search(cell.mName); } + void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell); + public: ESMStore *mEsmStore; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 0830c5de6..83864569f 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -50,18 +50,14 @@ namespace ESM return ref.mRefNum == refNum; } - void Cell::load(ESMReader &esm, bool saveContext) { - // Ignore this for now, it might mean we should delete the entire - // cell? - // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! - if (esm.isNextSub("DELE")) { - esm.skipHSub(); - } - - esm.getHNT(mData, "DATA", 12); + loadData(esm); + loadCell(esm, saveContext); +} +void Cell::loadCell(ESMReader &esm, bool saveContext) +{ mNAM0 = 0; if (mData.mFlags & Interior) @@ -73,12 +69,10 @@ void Cell::load(ESMReader &esm, bool saveContext) esm.getHT(waterl); mWater = (float) waterl; mWaterInt = true; - mHasWaterLevelRecord = true; } else if (esm.isNextSub("WHGT")) { esm.getHT(mWater); - mHasWaterLevelRecord = true; } // Quasi-exterior cells have a region (which determines the @@ -107,6 +101,18 @@ void Cell::load(ESMReader &esm, bool saveContext) } } +void Cell::loadData(ESMReader &esm) +{ + // Ignore this for now, it might mean we should delete the entire + // cell? + // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! + if (esm.isNextSub("DELE")) { + esm.skipHSub(); + } + + esm.getHNT(mData, "DATA", 12); +} + void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool { this->load(esm, false); @@ -124,14 +130,12 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) { - if (mHasWaterLevelRecord) { - if (mWaterInt) { - int water = - (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); - esm.writeHNT("INTV", water); - } else { - esm.writeHNT("WHGT", mWater); - } + if (mWaterInt) { + int water = + (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); + esm.writeHNT("INTV", water); + } else { + esm.writeHNT("WHGT", mWater); } if (mData.mFlags & QuasiEx) @@ -228,19 +232,6 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFogDensity = 0; } - void Cell::merge(Cell *original, Cell *modified) - { - float waterLevel = original->mWater; - if (modified->mHasWaterLevelRecord) - { - waterLevel = modified->mWater; - } - // else: keep original water level, instead of resetting to 0 - - *original = *modified; - original->mWater = waterLevel; - } - CellId Cell::getCellId() const { CellId id; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 28204c9ee..fb4b6b28a 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,10 +78,7 @@ struct Cell float mFogDensity; }; - Cell() : mWater(0), mHasWaterLevelRecord(false) {} - - /// Merge \a modified into \a original - static void merge (Cell* original, Cell* modified); + Cell() : mWater(0) {} // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. @@ -93,8 +90,8 @@ struct Cell std::vector mContextList; // File position; multiple positions for multiple plugin support DATAstruct mData; AMBIstruct mAmbi; + float mWater; // Water level - bool mHasWaterLevelRecord; bool mWaterInt; int mMapColor; int mNAM0; @@ -109,7 +106,10 @@ struct Cell // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. - void load(ESMReader &esm, bool saveContext = true); + void load(ESMReader &esm, bool saveContext = true); // Load everything (except references) + void loadData(ESMReader &esm); // Load DATAstruct only + void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except DATAstruct and references + void save(ESMWriter &esm) const; bool isExterior() const