mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-19 16:30:53 +00:00
Use a RefMum to track spawned levelled creatures
This commit is contained in:
parent
b3d984a9b1
commit
1bcc403544
12 changed files with 133 additions and 54 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#include "creaturelevlist.hpp"
|
||||
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/creaturelevliststate.hpp>
|
||||
#include <components/esm3/loadlevlist.hpp>
|
||||
|
||||
|
|
@ -9,6 +10,7 @@
|
|||
#include "../mwworld/customdata.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/worldmodel.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
|
|
@ -20,9 +22,15 @@ namespace MWClass
|
|||
class CreatureLevListCustomData : public MWWorld::TypedCustomData<CreatureLevListCustomData>
|
||||
{
|
||||
public:
|
||||
// actorId of the creature we spawned
|
||||
int mSpawnActorId;
|
||||
bool mSpawn; // Should a new creature be spawned?
|
||||
ESM::RefNum mSpawnedActor;
|
||||
bool mSpawn = true; // Should a new creature be spawned?
|
||||
|
||||
MWWorld::Ptr getSpawnedPtr() const
|
||||
{
|
||||
if (mSpawnedActor.isSet())
|
||||
return MWBase::Environment::get().getWorldModel()->getPtr(mSpawnedActor);
|
||||
return {};
|
||||
}
|
||||
|
||||
CreatureLevListCustomData& asCreatureLevListCustomData() override { return *this; }
|
||||
const CreatureLevListCustomData& asCreatureLevListCustomData() const override { return *this; }
|
||||
|
|
@ -46,9 +54,7 @@ namespace MWClass
|
|||
return;
|
||||
|
||||
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
|
||||
MWWorld::Ptr creature = (customData.mSpawnActorId == -1)
|
||||
? MWWorld::Ptr()
|
||||
: MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||
MWWorld::Ptr creature = customData.getSpawnedPtr();
|
||||
if (!creature.isEmpty())
|
||||
MWBase::Environment::get().getWorld()->adjustPosition(creature, force);
|
||||
}
|
||||
|
|
@ -71,13 +77,7 @@ namespace MWClass
|
|||
if (customData.mSpawn)
|
||||
return;
|
||||
|
||||
MWWorld::Ptr creature;
|
||||
if (customData.mSpawnActorId != -1)
|
||||
{
|
||||
creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||
if (creature.isEmpty())
|
||||
creature = ptr.getCell()->getMovedActor(customData.mSpawnActorId);
|
||||
}
|
||||
MWWorld::Ptr creature = customData.getSpawnedPtr();
|
||||
if (!creature.isEmpty())
|
||||
{
|
||||
const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);
|
||||
|
|
@ -116,21 +116,17 @@ namespace MWClass
|
|||
if (!id.empty())
|
||||
{
|
||||
// Delete the previous creature
|
||||
if (customData.mSpawnActorId != -1)
|
||||
{
|
||||
MWWorld::Ptr creature
|
||||
= MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||
if (!creature.isEmpty())
|
||||
MWBase::Environment::get().getWorld()->deleteObject(creature);
|
||||
customData.mSpawnActorId = -1;
|
||||
}
|
||||
MWWorld::Ptr previous = customData.getSpawnedPtr();
|
||||
if (!previous.isEmpty())
|
||||
MWBase::Environment::get().getWorld()->deleteObject(previous);
|
||||
|
||||
MWWorld::ManualRef manualRef(store, id);
|
||||
manualRef.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());
|
||||
manualRef.getPtr().getCellRef().setScale(ptr.getCellRef().getScale());
|
||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(
|
||||
manualRef.getPtr(), ptr.getCell(), ptr.getRefData().getPosition());
|
||||
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(placed);
|
||||
customData.mSpawnedActor = placed.getCellRef().getRefNum();
|
||||
customData.mSpawn = false;
|
||||
}
|
||||
else
|
||||
|
|
@ -141,11 +137,7 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::unique_ptr<CreatureLevListCustomData> data = std::make_unique<CreatureLevListCustomData>();
|
||||
data->mSpawnActorId = -1;
|
||||
data->mSpawn = true;
|
||||
|
||||
ptr.getRefData().setCustomData(std::move(data));
|
||||
ptr.getRefData().setCustomData(std::make_unique<CreatureLevListCustomData>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,8 +149,16 @@ namespace MWClass
|
|||
ensureCustomData(ptr);
|
||||
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
|
||||
const ESM::CreatureLevListState& levListState = state.asCreatureLevListState();
|
||||
customData.mSpawnActorId = levListState.mSpawnActorId;
|
||||
customData.mSpawnedActor = levListState.mSpawnedActor;
|
||||
customData.mSpawn = levListState.mSpawn;
|
||||
if (state.mActorIdConverter)
|
||||
{
|
||||
if (state.mActorIdConverter->convert(customData.mSpawnedActor, customData.mSpawnedActor.mIndex))
|
||||
return;
|
||||
state.mActorIdConverter->addConverter([converter = state.mActorIdConverter, &customData]() {
|
||||
customData.mSpawnedActor = converter->convert(customData.mSpawnedActor.mIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureLevList::writeAdditionalState(const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
||||
|
|
@ -171,7 +171,7 @@ namespace MWClass
|
|||
|
||||
const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
|
||||
ESM::CreatureLevListState& levListState = state.asCreatureLevListState();
|
||||
levListState.mSpawnActorId = customData.mSpawnActorId;
|
||||
levListState.mSpawnedActor = customData.mSpawnedActor;
|
||||
levListState.mSpawn = customData.mSpawn;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
|
|
@ -469,6 +470,10 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
reader.setContentFileMapping(&contentFileMap);
|
||||
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
|
||||
|
||||
ESM::ActorIdConverter actorIdConverter;
|
||||
if (version <= ESM::MaxActorIdSaveGameFormatVersion)
|
||||
reader.setActorIdConverter(&actorIdConverter);
|
||||
|
||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
|
||||
listener.setProgressRange(100);
|
||||
|
|
@ -589,7 +594,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
currentPercent = progressPercent;
|
||||
}
|
||||
}
|
||||
|
||||
actorIdConverter.apply();
|
||||
mCharacterManager.setCurrentCharacter(character);
|
||||
|
||||
mState = State_Running;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm/format.hpp>
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/cellref.hpp>
|
||||
#include <components/esm3/cellstate.hpp>
|
||||
#include <components/esm3/containerstate.hpp>
|
||||
|
|
@ -275,6 +276,14 @@ namespace
|
|||
if constexpr (std::is_same_v<T, ESM::Creature> || std::is_same_v<T, ESM::NPC>)
|
||||
MWWorld::convertEnchantmentSlots(state.mCreatureStats, state.mInventory);
|
||||
}
|
||||
if (reader.getActorIdConverter())
|
||||
{
|
||||
if constexpr (std::is_same_v<T, ESM::Creature> || std::is_same_v<T, ESM::NPC>)
|
||||
{
|
||||
MWBase::Environment::get().getWorldModel()->assignSaveFileRefNum(state.mRef);
|
||||
reader.getActorIdConverter()->mMappings.emplace(state.mCreatureStats.mActorId, state.mRef.mRefNum);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.mRef.mRefNum.hasContentFile())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ add_component_dir (esm3
|
|||
weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
|
||||
aisequence magiceffects custommarkerstate stolenitems transport animationstate controlsstate mappings readerscache
|
||||
infoorder timestamp formatversion landrecorddata selectiongroup dialoguecondition
|
||||
refnum
|
||||
refnum actoridconverter
|
||||
)
|
||||
|
||||
add_component_dir (esmterrain
|
||||
|
|
|
|||
32
components/esm3/actoridconverter.cpp
Normal file
32
components/esm3/actoridconverter.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include "actoridconverter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void ActorIdConverter::apply()
|
||||
{
|
||||
for (auto& converter : mConverters)
|
||||
converter();
|
||||
}
|
||||
|
||||
ESM::RefNum ActorIdConverter::convert(int actorId) const
|
||||
{
|
||||
auto it = mMappings.find(actorId);
|
||||
if (it == mMappings.end())
|
||||
return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool ActorIdConverter::convert(ESM::RefNum& refNum, int actorId) const
|
||||
{
|
||||
if (actorId == -1)
|
||||
{
|
||||
refNum = {};
|
||||
return true;
|
||||
}
|
||||
auto it = mMappings.find(actorId);
|
||||
if (it == mMappings.end())
|
||||
return false;
|
||||
refNum = it->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
29
components/esm3/actoridconverter.hpp
Normal file
29
components/esm3/actoridconverter.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM3_ACTORIDCONVERTER_H
|
||||
#define OPENMW_COMPONENTS_ESM3_ACTORIDCONVERTER_H
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "refnum.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ActorIdConverter
|
||||
{
|
||||
std::vector<std::function<void()>> mConverters;
|
||||
|
||||
public:
|
||||
std::map<int, ESM::RefNum> mMappings;
|
||||
|
||||
void apply();
|
||||
|
||||
ESM::RefNum convert(int actorId) const;
|
||||
|
||||
bool convert(ESM::RefNum& refNum, int actorId) const;
|
||||
|
||||
void addConverter(std::function<void()>&& converter) { mConverters.emplace_back(converter); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -10,8 +10,13 @@ namespace ESM
|
|||
{
|
||||
ObjectState::load(esm);
|
||||
|
||||
mSpawnActorId = -1;
|
||||
esm.getHNOT(mSpawnActorId, "SPAW");
|
||||
if (esm.getFormatVersion() <= MaxActorIdSaveGameFormatVersion)
|
||||
{
|
||||
mSpawnedActor.mIndex = -1;
|
||||
esm.getHNOT(mSpawnedActor.mIndex, "SPAW");
|
||||
}
|
||||
else if (esm.peekNextSub("SPAW"))
|
||||
esm.getFormId(true, "SPAW");
|
||||
|
||||
mSpawn = false;
|
||||
esm.getHNOT(mSpawn, "RESP");
|
||||
|
|
@ -21,8 +26,8 @@ namespace ESM
|
|||
{
|
||||
ObjectState::save(esm, inInventory);
|
||||
|
||||
if (mSpawnActorId != -1)
|
||||
esm.writeHNT("SPAW", mSpawnActorId);
|
||||
if (mSpawnedActor.isSet())
|
||||
esm.writeFormId(mSpawnedActor, true, "SPAW");
|
||||
|
||||
if (mSpawn)
|
||||
esm.writeHNT("RESP", mSpawn);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace ESM
|
|||
|
||||
struct CreatureLevListState final : public ObjectState
|
||||
{
|
||||
int32_t mSpawnActorId;
|
||||
ESM::RefNum mSpawnedActor; // actor id in older saves
|
||||
bool mSpawn;
|
||||
|
||||
void load(ESMReader& esm) override;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ namespace ESM
|
|||
inline constexpr bool IsReadable<void> = false;
|
||||
|
||||
class ReadersCache;
|
||||
class ActorIdConverter; // For old save games
|
||||
|
||||
class ESMReader
|
||||
{
|
||||
|
|
@ -118,11 +119,13 @@ namespace ESM
|
|||
|
||||
// Used only when loading saves to adjust FormIds if load order was changes.
|
||||
void setContentFileMapping(const std::map<int, int>* mapping) { mContentFileMapping = mapping; }
|
||||
const std::map<int, int>* getContentFileMapping();
|
||||
|
||||
// Returns false if content file not found.
|
||||
bool applyContentFileMapping(FormId& id);
|
||||
|
||||
void setActorIdConverter(ActorIdConverter* converter) { mActorIdConverter = converter; }
|
||||
ActorIdConverter* getActorIdConverter() const { return mActorIdConverter; }
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Medium-level reading shortcuts
|
||||
|
|
@ -386,6 +389,8 @@ namespace ESM
|
|||
size_t mFileSize;
|
||||
|
||||
const std::map<int, int>* mContentFileMapping = nullptr;
|
||||
|
||||
ActorIdConverter* mActorIdConverter = nullptr;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ namespace ESM
|
|||
inline constexpr FormatVersion MaxOldCountFormatVersion = 30;
|
||||
inline constexpr FormatVersion MaxActiveSpellTypeVersion = 31;
|
||||
inline constexpr FormatVersion MaxPlayerBeforeCellDataFormatVersion = 32;
|
||||
inline constexpr FormatVersion CurrentSaveGameFormatVersion = 34;
|
||||
inline constexpr FormatVersion MaxActorIdSaveGameFormatVersion = 34;
|
||||
inline constexpr FormatVersion CurrentSaveGameFormatVersion = 35;
|
||||
|
||||
inline constexpr FormatVersion MinSupportedSaveGameFormatVersion = 5;
|
||||
inline constexpr FormatVersion OpenMW0_49MinSaveGameFormatVersion = 5;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace ESM
|
|||
void ObjectState::load(ESMReader& esm)
|
||||
{
|
||||
mVersion = esm.getFormatVersion();
|
||||
mActorIdConverter = esm.getActorIdConverter();
|
||||
|
||||
bool isDeleted;
|
||||
mRef.loadData(esm, isDeleted);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
namespace ESM
|
||||
{
|
||||
class ActorIdConverter;
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
struct ContainerState;
|
||||
|
|
@ -29,27 +30,18 @@ namespace ESM
|
|||
{
|
||||
CellRef mRef;
|
||||
|
||||
unsigned char mHasLocals;
|
||||
Locals mLocals;
|
||||
LuaScripts mLuaScripts;
|
||||
unsigned char mEnabled;
|
||||
Position mPosition;
|
||||
uint32_t mFlags;
|
||||
AnimationState mAnimationState;
|
||||
ActorIdConverter* mActorIdConverter = nullptr;
|
||||
FormatVersion mVersion = DefaultFormatVersion;
|
||||
uint32_t mFlags = 0;
|
||||
unsigned char mHasLocals = 0;
|
||||
unsigned char mEnabled = 0;
|
||||
|
||||
// Is there any class-specific state following the ObjectState
|
||||
bool mHasCustomState;
|
||||
|
||||
FormatVersion mVersion = DefaultFormatVersion;
|
||||
|
||||
AnimationState mAnimationState;
|
||||
|
||||
ObjectState()
|
||||
: mHasLocals(0)
|
||||
, mEnabled(0)
|
||||
, mFlags(0)
|
||||
, mHasCustomState(true)
|
||||
{
|
||||
}
|
||||
bool mHasCustomState = true;
|
||||
|
||||
/// @note Does not load the CellRef ID, it should already be loaded before calling this method
|
||||
virtual void load(ESMReader& esm);
|
||||
|
|
|
|||
Loading…
Reference in a new issue