diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 53245be247..b2345a96ea 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -844,7 +844,12 @@ namespace MWWorld if (type == 0) { Log(Debug::Warning) << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)"; - reader.skipHSubUntil("OBJE"); + // Skip until the next OBJE or MVRF + while(reader.hasMoreSubs() && !reader.peekNextSub("OBJE") && !reader.peekNextSub("MVRF")) + { + reader.getSubName(); + reader.skipHSub(); + } continue; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 4b9bdf7426..3bfa01a3da 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -109,6 +109,21 @@ namespace return npcsToReplace; } + + // Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no longer exists however. + // So instead of removing the item altogether, we're only removing the script. + template + void removeMissingScripts(const MWWorld::Store& scripts, std::map& items) + { + for(auto& [id, item] : items) + { + if(!item.mScript.empty() && !scripts.search(item.mScript)) + { + item.mScript.clear(); + Log(Debug::Verbose) << "Item '" << id << "' (" << item.mName << ") has nonexistent script '" << item.mScript << "', ignoring it."; + } + } + } } namespace MWWorld @@ -366,6 +381,32 @@ void ESMStore::validateDynamic() for (const ESM::NPC &npc : npcsToReplace) mNpcs.insert(npc); + + removeMissingScripts(mScripts, mArmors.mDynamic); + removeMissingScripts(mScripts, mBooks.mDynamic); + removeMissingScripts(mScripts, mClothes.mDynamic); + removeMissingScripts(mScripts, mWeapons.mDynamic); + + removeMissingObjects(mCreatureLists); + removeMissingObjects(mItemLists); +} + +// Leveled lists can be modified by scripts. This removes items that no longer exist (presumably because the plugin was removed) from modified lists +template +void ESMStore::removeMissingObjects(Store& store) +{ + for(auto& [id, list] : store.mDynamic) + { + std::remove_if(list.mList.begin(), list.mList.end(), [&] (const auto& item) + { + if(!find(item.mId)) + { + Log(Debug::Verbose) << "Leveled list '" << id << "' has nonexistent object '" << item.mId << "', ignoring it."; + return true; + } + return false; + }); + } } int ESMStore::countSavedGameRecords() const diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 608b5489ea..6ad479f8bf 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -89,6 +89,9 @@ namespace MWWorld void validate(); void countRecords(); + + template + void removeMissingObjects(Store& store); public: /// \todo replace with SharedIterator typedef std::map::const_iterator iterator;