1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-07 12:40:01 +00:00

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.
This commit is contained in:
cc9cii 2021-08-17 12:29:28 +10:00
parent bd995817ee
commit 47a841d3b7
5 changed files with 47 additions and 18 deletions

View File

@ -2,6 +2,7 @@
#include <stdexcept>
#include <algorithm>
#include <climits> // INT_MIN
#include <osgDB/WriteFile>
@ -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);

View File

@ -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<CreatureCustomData> data (new CreatureCustomData);
if (hasInventoryStore(ptr))
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
// 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<MWWorld::ContainerStore>();
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);
ptr.getRefData().setCustomData (std::move(data));
if (hasInventoryStore(ptr))
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
else
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
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<ESM::Creature>()->mBase->mId);
if(spellsInitialised)

View File

@ -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<NpcCustomData>());
// 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<NpcCustomData>());
}
}
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<ESM::NPC>()->mBase->mId);

View File

@ -1,6 +1,7 @@
#include "creaturestats.hpp"
#include <algorithm>
#include <climits>
#include <components/esm/creaturestats.hpp>
#include <components/esm/esmreader.hpp>
@ -562,22 +563,27 @@ namespace MWMechanics
void CreatureStats::readState (const ESM::CreatureStats& state)
{
for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].readState (state.mAttributes[i]);
// HACK: using mGoldPool as an indicator for lack of ACDT during .ess import
if (state.mGoldPool != INT_MIN)
{
for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].readState (state.mAttributes[i]);
for (int i=0; i<3; ++i)
mDynamic[i].readState (state.mDynamic[i]);
for (int i=0; i<3; ++i)
mDynamic[i].readState (state.mDynamic[i]);
mGoldPool = state.mGoldPool;
mTalkedTo = state.mTalkedTo;
mAttacked = state.mAttacked;
}
mLastRestock = MWWorld::TimeStamp(state.mTradeTime);
mGoldPool = state.mGoldPool;
mDead = state.mDead;
mDeathAnimationFinished = state.mDeathAnimationFinished;
mDied = state.mDied;
mMurdered = state.mMurdered;
mTalkedTo = state.mTalkedTo;
mAlarmed = state.mAlarmed;
mAttacked = state.mAttacked;
// TODO: rewrite. does this really need 3 separate bools?
mKnockdown = state.mKnockdown;
mKnockdownOneFrame = state.mKnockdownOneFrame;

View File

@ -70,6 +70,8 @@ namespace MWMechanics
MWWorld::TimeStamp mLastRestock;
// The pool of merchant gold (not in inventory)
// HACK: value of INT_MIN has a special meaning: indicates a converted .ess file
// (this is a workaround to avoid changing the save file format)
int mGoldPool;
int mActorId;