From 47a841d3b7fb700fc254150f73b378b166e0cb69 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 17 Aug 2021 12:29:28 +1000 Subject: [PATCH] Fix/workaround for Issue #3246 OpenMW save file assumes the presence of NPC/Creature data but the vanilla save file provides only the delta changes in most situations. The base data are not available without loading all the relevant dependency content files. Duplicating that code in the ESSImporter is not desirable. Ideally a flag should be set but that will mean a change in the save file format. For a minor change such as this doing so seems like an overkill. So a temporary workaround is introduced where the gold carried by the NPC/Creature is used as an indicator as the lack of ACDT data. --- apps/essimporter/converter.cpp | 5 +++++ apps/openmw/mwclass/creature.cpp | 25 +++++++++++++++-------- apps/openmw/mwclass/npc.cpp | 13 +++++++++--- apps/openmw/mwmechanics/creaturestats.cpp | 20 +++++++++++------- apps/openmw/mwmechanics/creaturestats.hpp | 2 ++ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 320f7224bc..874b936baf 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -2,6 +2,7 @@ #include #include +#include // INT_MIN #include @@ -369,6 +370,8 @@ namespace ESSImport // from the ESM with default values if (cellref.mHasACDT) convertACDT(cellref.mACDT, objstate.mCreatureStats); + else + objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT if (cellref.mHasACSC) convertACSC(cellref.mACSC, objstate.mCreatureStats); convertNpcData(cellref, objstate.mNpcStats); @@ -410,6 +413,8 @@ namespace ESSImport // from the ESM with default values if (cellref.mHasACDT) convertACDT(cellref.mACDT, objstate.mCreatureStats); + else + objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT if (cellref.mHasACSC) convertACSC(cellref.mACSC, objstate.mCreatureStats); convertCREC(crecIt->second, objstate); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d03ea70a87..8a9f33e1bd 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -748,26 +748,35 @@ namespace MWClass if (!state.mHasCustomState) return; + const ESM::CreatureState& creatureState = state.asCreatureState(); + if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) { - // Create a CustomData, but don't fill it from ESM records (not needed) - std::unique_ptr data (new CreatureCustomData); - - if (hasInventoryStore(ptr)) - data->mContainerStore = std::make_unique(); + // FIXME: the use of mGoldPool can be replaced with another flag the next time + // the save file format is changed + if (creatureState.mCreatureStats.mGoldPool == INT_MIN) + ensureCustomData(ptr); else - data->mContainerStore = std::make_unique(); + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::unique_ptr data (new CreatureCustomData); - ptr.getRefData().setCustomData (std::move(data)); + if (hasInventoryStore(ptr)) + data->mContainerStore = std::make_unique(); + else + data->mContainerStore = std::make_unique(); + + ptr.getRefData().setCustomData (std::move(data)); + } } } else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); - const ESM::CreatureState& creatureState = state.asCreatureState(); + customData.mContainerStore->readState (creatureState.mInventory); bool spellsInitialised = customData.mCreatureStats.getSpells().setSpells(ptr.get()->mBase->mId); if(spellsInitialised) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 736ae537fe..030c533b23 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1291,19 +1291,26 @@ namespace MWClass if (!state.mHasCustomState) return; + const ESM::NpcState& npcState = state.asNpcState(); + if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) { - // Create a CustomData, but don't fill it from ESM records (not needed) - ptr.getRefData().setCustomData(std::make_unique()); + // FIXME: the use of mGoldPool can be replaced with another flag the next time + // the save file format is changed + if (npcState.mCreatureStats.mGoldPool == INT_MIN) + ensureCustomData(ptr); + else + // Create a CustomData, but don't fill it from ESM records (not needed) + ptr.getRefData().setCustomData(std::make_unique()); } } else ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); - const ESM::NpcState& npcState = state.asNpcState(); + customData.mInventoryStore.readState (npcState.mInventory); customData.mNpcStats.readState (npcState.mNpcStats); bool spellsInitialised = customData.mNpcStats.getSpells().setSpells(ptr.get()->mBase->mId); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 1ff44fcbb0..8d99664f27 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -1,6 +1,7 @@ #include "creaturestats.hpp" #include +#include #include #include @@ -562,22 +563,27 @@ namespace MWMechanics void CreatureStats::readState (const ESM::CreatureStats& state) { - for (int i=0; i