From cdfd305ac30325b7e20ec25b6a62f262132dd283 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 24 May 2024 16:59:48 +0200 Subject: [PATCH 1/2] Convert constant effect equipment slots to refnums --- apps/openmw/mwworld/cellstore.cpp | 12 +++++++++- apps/openmw/mwworld/magiceffects.cpp | 36 ++++++++++++++++++++++++---- apps/openmw/mwworld/magiceffects.hpp | 2 ++ apps/openmw/mwworld/player.cpp | 5 ++++ apps/openmw/mwworld/ptrregistry.hpp | 11 +++++++++ apps/openmw/mwworld/worldmodel.hpp | 2 ++ components/esm3/activespells.cpp | 9 +------ 7 files changed, 64 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 4cda009b13..8ec8a9ce3a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -264,8 +264,18 @@ namespace } else if (state.mVersion <= ESM::MaxOldCreatureStatsFormatVersion) { - if constexpr (std::is_same_v || std::is_same_v) + if constexpr (std::is_same_v) + MWWorld::convertStats(state.mCreatureStats); + else if constexpr (std::is_same_v) + { MWWorld::convertStats(state.mCreatureStats); + MWWorld::convertEnchantmentSlots(state.mCreatureStats, state.mInventory); + } + } + else if (state.mVersion <= ESM::MaxActiveSpellSlotIndexFormatVersion) + { + if constexpr (std::is_same_v) + MWWorld::convertEnchantmentSlots(state.mCreatureStats, state.mInventory); } if (state.mRef.mRefNum.hasContentFile()) diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index ce83b3e18f..728712da01 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -11,6 +11,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwworld/worldmodel.hpp" #include "../mwmechanics/magiceffects.hpp" @@ -101,13 +102,16 @@ namespace MWWorld } creatureStats.mActiveSpells.mSpells.emplace_back(params); } - std::multimap equippedItems; + std::multimap equippedItems; for (std::size_t i = 0; i < inventory.mItems.size(); ++i) { - const ESM::ObjectState& item = inventory.mItems[i]; + ESM::ObjectState& item = inventory.mItems[i]; auto slot = inventory.mEquipmentSlots.find(i); if (slot != inventory.mEquipmentSlots.end()) - equippedItems.emplace(item.mRef.mRefID, slot->second); + { + MWBase::Environment::get().getWorldModel()->assignSaveFileRefNum(item.mRef); + equippedItems.emplace(item.mRef.mRefID, item.mRef.mRefNum); + } } for (const auto& [id, oldMagnitudes] : inventory.mPermanentMagicEffectMagnitudes) { @@ -161,7 +165,7 @@ namespace MWWorld auto [begin, end] = equippedItems.equal_range(id); for (auto it = begin; it != end; ++it) { - params.mItem = { static_cast(it->second), 0 }; + params.mItem = it->second; creatureStats.mActiveSpells.mSpells.emplace_back(params); } } @@ -229,4 +233,28 @@ namespace MWWorld for (auto& setting : creatureStats.mAiSettings) setting.mMod = 0.f; } + + // Versions 17-27 wrote an equipment slot index to mItem + void convertEnchantmentSlots(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory) + { + for (auto& activeSpell : creatureStats.mActiveSpells.mSpells) + { + if (!activeSpell.mItem.isSet()) + continue; + if (activeSpell.mFlags & ESM::ActiveSpells::Flag_Equipment) + { + auto slotIndex = activeSpell.mItem.mIndex; + auto slot = std::find_if(inventory.mEquipmentSlots.begin(), inventory.mEquipmentSlots.end(), + [=](const auto& entry) { return entry.second == slotIndex; }); + if (slot != inventory.mEquipmentSlots.end() && slot->first < inventory.mItems.size()) + { + ESM::CellRef& ref = inventory.mItems[slot->first].mRef; + MWBase::Environment::get().getWorldModel()->assignSaveFileRefNum(ref); + activeSpell.mItem = ref.mRefNum; + continue; + } + } + activeSpell.mItem = {}; + } + } } diff --git a/apps/openmw/mwworld/magiceffects.hpp b/apps/openmw/mwworld/magiceffects.hpp index aefcd056e0..3acb14fff2 100644 --- a/apps/openmw/mwworld/magiceffects.hpp +++ b/apps/openmw/mwworld/magiceffects.hpp @@ -14,6 +14,8 @@ namespace MWWorld ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory, ESM::NpcStats* npcStats = nullptr); void convertStats(ESM::CreatureStats& creatureStats); + + void convertEnchantmentSlots(ESM::CreatureStats& creatureStats, ESM::InventoryState& inventory); } #endif diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 7849ba1458..323658007c 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -317,7 +317,12 @@ namespace MWWorld convertMagicEffects( player.mObject.mCreatureStats, player.mObject.mInventory, &player.mObject.mNpcStats); else if (reader.getFormatVersion() <= ESM::MaxOldCreatureStatsFormatVersion) + { convertStats(player.mObject.mCreatureStats); + convertEnchantmentSlots(player.mObject.mCreatureStats, player.mObject.mInventory); + } + else if (reader.getFormatVersion() <= ESM::MaxActiveSpellSlotIndexFormatVersion) + convertEnchantmentSlots(player.mObject.mCreatureStats, player.mObject.mInventory); if (!player.mObject.mEnabled) { diff --git a/apps/openmw/mwworld/ptrregistry.hpp b/apps/openmw/mwworld/ptrregistry.hpp index c5ce371538..897945133e 100644 --- a/apps/openmw/mwworld/ptrregistry.hpp +++ b/apps/openmw/mwworld/ptrregistry.hpp @@ -56,6 +56,17 @@ namespace MWWorld } } + // For fixing old saves + void assign(ESM::CellRef& ref) + { + if (!ref.mRefNum.isSet()) + { + CellRef temp(ref); + temp.getOrAssignRefNum(mLastGenerated); + ref.mRefNum = temp.getRefNum(); + } + } + private: std::size_t mRevision = 0; std::unordered_map mIndex; diff --git a/apps/openmw/mwworld/worldmodel.hpp b/apps/openmw/mwworld/worldmodel.hpp index c7b18671d3..904dd27411 100644 --- a/apps/openmw/mwworld/worldmodel.hpp +++ b/apps/openmw/mwworld/worldmodel.hpp @@ -81,6 +81,8 @@ namespace MWWorld void deregisterLiveCellRef(const LiveCellRefBase& ref) noexcept { mPtrRegistry.remove(ref); } + void assignSaveFileRefNum(ESM::CellRef& ref) { mPtrRegistry.assign(ref); } + template void forEachLoadedCellStore(Fn&& fn) { diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index c3d86c3ebf..7c84afe489 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -168,14 +168,7 @@ namespace ESM esm.getHNT(params.mFlags, "FLAG"); } if (esm.peekNextSub("ITEM")) - { - if (format <= MaxActiveSpellSlotIndexFormatVersion) - // Previous versions saved slot index in this record. - // Ignore these values as we can't use them - esm.getFormId(true, "ITEM"); - else - params.mItem = esm.getFormId(true, "ITEM"); - } + params.mItem = esm.getFormId(true, "ITEM"); } if (esm.isNextSub("WORS")) { From 11e8e1eeb54072efbba1318a9a1ecef26664f657 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 25 May 2024 09:58:46 +0200 Subject: [PATCH 2/2] Include creatures --- apps/openmw/mwworld/cellstore.cpp | 6 ++---- apps/openmw/mwworld/magiceffects.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 8ec8a9ce3a..6f3d23593b 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -264,9 +264,7 @@ namespace } else if (state.mVersion <= ESM::MaxOldCreatureStatsFormatVersion) { - if constexpr (std::is_same_v) - MWWorld::convertStats(state.mCreatureStats); - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v || std::is_same_v) { MWWorld::convertStats(state.mCreatureStats); MWWorld::convertEnchantmentSlots(state.mCreatureStats, state.mInventory); @@ -274,7 +272,7 @@ namespace } else if (state.mVersion <= ESM::MaxActiveSpellSlotIndexFormatVersion) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v || std::is_same_v) MWWorld::convertEnchantmentSlots(state.mCreatureStats, state.mInventory); } diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index 728712da01..38f17677ef 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -243,7 +243,7 @@ namespace MWWorld continue; if (activeSpell.mFlags & ESM::ActiveSpells::Flag_Equipment) { - auto slotIndex = activeSpell.mItem.mIndex; + std::int64_t slotIndex = activeSpell.mItem.mIndex; auto slot = std::find_if(inventory.mEquipmentSlots.begin(), inventory.mEquipmentSlots.end(), [=](const auto& entry) { return entry.second == slotIndex; }); if (slot != inventory.mEquipmentSlots.end() && slot->first < inventory.mItems.size())