diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 27980f0705..5e844ffae7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -443,6 +443,8 @@ namespace MWBase virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0; virtual void disableDeferredPreviewRotation() = 0; + virtual void saveLoaded() = 0; + virtual void setupPlayer() = 0; virtual void renderPlayer() = 0; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f605344bf7..93f75c6f10 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -505,6 +505,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str character->getPath().filename().string()); MWBase::Environment::get().getWindowManager()->setNewGame(false); + MWBase::Environment::get().getWorld()->saveLoaded(); MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 90bc80b484..986c4f8580 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -48,6 +48,57 @@ namespace } } } + + std::vector getNPCsToReplace(const MWWorld::Store& factions, const MWWorld::Store& classes, const std::map& npcs) + { + // Cache first class from store - we will use it if current class is not found + std::string defaultCls; + auto it = classes.begin(); + if (it != classes.end()) + defaultCls = it->mId; + else + throw std::runtime_error("List of NPC classes is empty!"); + + // Validate NPCs for non-existing class and faction. + // We will replace invalid entries by fixed ones + std::vector npcsToReplace; + + for (auto it : npcs) + { + ESM::NPC npc = it.second; + bool changed = false; + + const std::string npcFaction = npc.mFaction; + if (!npcFaction.empty()) + { + const ESM::Faction *fact = factions.search(npcFaction); + if (!fact) + { + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it."; + npc.mFaction.clear(); + npc.mNpdt.mRank = 0; + changed = true; + } + } + + std::string npcClass = npc.mClass; + if (!npcClass.empty()) + { + const ESM::Class *cls = classes.search(npcClass); + if (!cls) + { + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement."; + npc.mClass = defaultCls; + changed = true; + } + } + + if (changed) + npcsToReplace.push_back(npc); + } + + return npcsToReplace; + } } namespace MWWorld @@ -218,49 +269,7 @@ int ESMStore::getRefCount(const std::string& id) const void ESMStore::validate() { - // Cache first class from store - we will use it if current class is not found - std::string defaultCls = ""; - Store::iterator it = mClasses.begin(); - if (it != mClasses.end()) - defaultCls = it->mId; - else - throw std::runtime_error("List of NPC classes is empty!"); - - // Validate NPCs for non-existing class and faction. - // We will replace invalid entries by fixed ones - std::vector npcsToReplace; - for (ESM::NPC npc : mNpcs) - { - bool changed = false; - - const std::string npcFaction = npc.mFaction; - if (!npcFaction.empty()) - { - const ESM::Faction *fact = mFactions.search(npcFaction); - if (!fact) - { - Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it."; - npc.mFaction.clear(); - npc.mNpdt.mRank = 0; - changed = true; - } - } - - std::string npcClass = npc.mClass; - if (!npcClass.empty()) - { - const ESM::Class *cls = mClasses.search(npcClass); - if (!cls) - { - Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement."; - npc.mClass = defaultCls; - changed = true; - } - } - - if (changed) - npcsToReplace.push_back(npc); - } + std::vector npcsToReplace = getNPCsToReplace(mFactions, mClasses, mNpcs.mStatic); for (const ESM::NPC &npc : npcsToReplace) { @@ -331,6 +340,14 @@ void ESMStore::validate() } } +void ESMStore::validateDynamic() +{ + std::vector npcsToReplace = getNPCsToReplace(mFactions, mClasses, mNpcs.mDynamic); + + for (const ESM::NPC &npc : npcsToReplace) + mNpcs.insert(npc); +} + int ESMStore::countSavedGameRecords() const { return 1 // DYNA (dynamic name counter) @@ -384,12 +401,14 @@ void ESMStore::validate() case ESM::REC_ENCH: case ESM::REC_SPEL: case ESM::REC_WEAP: - case ESM::REC_NPC_: case ESM::REC_LEVI: case ESM::REC_LEVC: + mStores[type]->read (reader); + return true; + case ESM::REC_NPC_: case ESM::REC_CREA: case ESM::REC_CONT: - mStores[type]->read (reader); + mStores[type]->read (reader, true); return true; case ESM::REC_DYNA: diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d69c56d8c0..26f497a527 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -80,8 +80,6 @@ namespace MWWorld std::map mStores; - ESM::NPC mPlayerTemplate; - unsigned int mDynamicCount; mutable std::map > mSpellListCache; @@ -172,16 +170,18 @@ namespace MWWorld for (std::map::iterator it = mStores.begin(); it != mStores.end(); ++it) it->second->clearDynamic(); - mNpcs.insert(mPlayerTemplate); + movePlayerRecord(); } void movePlayerRecord () { - mPlayerTemplate = *mNpcs.find("player"); - mNpcs.eraseStatic(mPlayerTemplate.mId); - mNpcs.insert(mPlayerTemplate); + auto player = mNpcs.find("player"); + mNpcs.insert(*player); } + /// Validate entries in store after loading a save + void validateDynamic(); + void load(ESM::ESMReader &esm, Loading::Listener* listener); template diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 69ca1f8c27..9d6552106b 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -250,9 +250,15 @@ namespace MWWorld } } template - T *Store::insert(const T &item) + T *Store::insert(const T &item, bool overrideOnly) { std::string id = Misc::StringUtils::lowerCase(item.mId); + if(overrideOnly) + { + auto it = mStatic.find(id); + if(it == mStatic.end()) + return nullptr; + } std::pair result = mDynamic.insert(std::pair(id, item)); T *ptr = &result.first->second; @@ -337,13 +343,13 @@ namespace MWWorld } } template - RecordId Store::read(ESM::ESMReader& reader) + RecordId Store::read(ESM::ESMReader& reader, bool overrideOnly) { T record; bool isDeleted = false; record.load (reader, isDeleted); - insert (record); + insert (record, overrideOnly); return RecordId(record.mId, isDeleted); } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index cb9f4f1e02..9cb1c7473b 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -46,7 +46,7 @@ namespace MWWorld virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {} - virtual RecordId read (ESM::ESMReader& reader) { return RecordId(); } + virtual RecordId read (ESM::ESMReader& reader, bool overrideOnly = false) { return RecordId(); } ///< Read into dynamic storage }; @@ -192,7 +192,7 @@ namespace MWWorld /// @note The record identifiers are listed in the order that the records were defined by the content files. void listIdentifier(std::vector &list) const override; - T *insert(const T &item); + T *insert(const T &item, bool overrideOnly = false); T *insertStatic(const T &item); bool eraseStatic(const std::string &id) override; @@ -201,7 +201,7 @@ namespace MWWorld RecordId load(ESM::ESMReader &esm) override; void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override; - RecordId read(ESM::ESMReader& reader) override; + RecordId read(ESM::ESMReader& reader, bool overrideOnly = false) override; }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b37d326f07..d6a4de71bd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2473,6 +2473,11 @@ namespace MWWorld mRendering->getCamera()->adjustCameraDistance(dist); } + void World::saveLoaded() + { + mStore.validateDynamic(); + } + void World::setupPlayer() { const ESM::NPC *player = mStore.get().find("player"); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 23153a31c4..e2d3449304 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -549,6 +549,8 @@ namespace MWWorld void applyDeferredPreviewRotationToPlayer(float dt) override; void disableDeferredPreviewRotation() override; + void saveLoaded() override; + void setupPlayer() override; void renderPlayer() override;