From 8020bfcafddc39fd4ed7a6af2ea912a9c41ec246 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Fri, 20 Dec 2024 23:59:15 +0100 Subject: [PATCH] Track projectile casters using RefNum --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 4 +- apps/openmw/mwworld/projectilemanager.cpp | 79 +++++++++++++---------- apps/openmw/mwworld/projectilemanager.hpp | 8 +-- apps/openmw/mwworld/worldimp.cpp | 3 +- apps/openmw/mwworld/worldimp.hpp | 2 +- components/esm3/projectilestate.cpp | 10 ++- components/esm3/projectilestate.hpp | 2 +- 8 files changed, 64 insertions(+), 46 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 23fb98063f..c60d575c7e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -375,7 +375,7 @@ namespace MWBase virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0; virtual void disableDeferredPreviewRotation() = 0; - virtual void saveLoaded() = 0; + virtual void saveLoaded(const ESM::ESMReader& reader) = 0; virtual void setupPlayer() = 0; virtual void renderPlayer() = 0; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 7c5444fd4b..05a4540afe 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -594,7 +594,6 @@ void MWState::StateManager::loadGame(const Character* character, const std::file currentPercent = progressPercent; } } - actorIdConverter.apply(); mCharacterManager.setCurrentCharacter(character); mState = State_Running; @@ -604,7 +603,8 @@ void MWState::StateManager::loadGame(const Character* character, const std::file mLastSavegame = filepath; MWBase::Environment::get().getWindowManager()->setNewGame(false); - MWBase::Environment::get().getWorld()->saveLoaded(); + MWBase::Environment::get().getWorld()->saveLoaded(reader); + actorIdConverter.apply(); MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 203444fa61..c79c265e38 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -9,6 +9,8 @@ #include +#include +#include #include #include #include @@ -36,6 +38,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/worldmodel.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -289,10 +292,8 @@ namespace MWWorld state.mSpellId = spellId; state.mCasterHandle = caster; state.mItem = item; - if (caster.getClass().isActor()) - state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); - else - state.mActorId = -1; + MWBase::Environment::get().getWorldModel()->registerPtr(caster); + state.mCaster = caster.getCellRef().getRefNum(); std::string texture; @@ -342,7 +343,7 @@ namespace MWWorld const osg::Quat& orient, const Ptr& bow, float speed, float attackStrength) { ProjectileState state; - state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mCaster = actor.getCellRef().getRefNum(); state.mBowId = bow.getCellRef().getRefId(); state.mVelocity = orient * osg::Vec3f(0, 1, 0) * speed; state.mIdArrow = projectile.getCellRef().getRefId(); @@ -367,24 +368,24 @@ namespace MWWorld void ProjectileManager::updateCasters() { for (auto& state : mProjectiles) - mPhysics->setCaster(state.mProjectileId, state.getCaster()); + { + state.mCasterHandle = state.getCaster(); + mPhysics->setCaster(state.mProjectileId, state.mCasterHandle); + } for (auto& state : mMagicBolts) { - // casters are identified by actor id in the savegame. objects doesn't have one so they can't be identified - // back. - // TODO: should object-type caster be restored from savegame? - if (state.mActorId == -1) + if (!state.mCaster.isSet()) continue; - auto caster = state.getCaster(); - if (caster.isEmpty()) + state.mCasterHandle = state.getCaster(); + if (state.mCasterHandle.isEmpty()) { - Log(Debug::Error) << "Couldn't find caster with ID " << state.mActorId; + Log(Debug::Error) << "Couldn't find caster with ID " << state.mCaster; cleanupMagicBolt(state); continue; } - mPhysics->setCaster(state.mProjectileId, caster); + mPhysics->setCaster(state.mProjectileId, state.mCasterHandle); } } @@ -650,37 +651,37 @@ namespace MWWorld void ProjectileManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) const { - for (std::vector::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + for (const ProjectileState& projectile : mProjectiles) { writer.startRecord(ESM::REC_PROJ); ESM::ProjectileState state; - state.mId = it->mIdArrow; - state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition())); - state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude())); - state.mActorId = it->mActorId; + state.mId = projectile.mIdArrow; + state.mPosition = ESM::Vector3(osg::Vec3f(projectile.mNode->getPosition())); + state.mOrientation = ESM::Quaternion(osg::Quat(projectile.mNode->getAttitude())); + state.mCaster = projectile.mCaster; - state.mBowId = it->mBowId; - state.mVelocity = it->mVelocity; - state.mAttackStrength = it->mAttackStrength; + state.mBowId = projectile.mBowId; + state.mVelocity = projectile.mVelocity; + state.mAttackStrength = projectile.mAttackStrength; state.save(writer); writer.endRecord(ESM::REC_PROJ); } - for (std::vector::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + for (const MagicBoltState& bolt : mMagicBolts) { writer.startRecord(ESM::REC_MPRJ); ESM::MagicBoltState state; - state.mId = it->mIdMagic.at(0); - state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition())); - state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude())); - state.mActorId = it->mActorId; - state.mItem = it->mItem; - state.mSpellId = it->mSpellId; - state.mSpeed = it->mSpeed; + state.mId = bolt.mIdMagic.at(0); + state.mPosition = ESM::Vector3(osg::Vec3f(bolt.mNode->getPosition())); + state.mOrientation = ESM::Quaternion(osg::Quat(bolt.mNode->getAttitude())); + state.mCaster = bolt.mCaster; + state.mItem = bolt.mItem; + state.mSpellId = bolt.mSpellId; + state.mSpeed = bolt.mSpeed; state.save(writer); @@ -696,7 +697,7 @@ namespace MWWorld esm.load(reader); ProjectileState state; - state.mActorId = esm.mActorId; + state.mCaster = esm.mCaster; state.mBowId = esm.mBowId; state.mVelocity = esm.mVelocity; state.mIdArrow = esm.mId; @@ -736,7 +737,7 @@ namespace MWWorld MagicBoltState state; state.mIdMagic.push_back(esm.mId); state.mSpellId = esm.mSpellId; - state.mActorId = esm.mActorId; + state.mCaster = esm.mCaster; state.mToDelete = false; state.mItem = esm.mItem; std::string texture; @@ -798,12 +799,24 @@ namespace MWWorld return mMagicBolts.size() + mProjectiles.size(); } + void ProjectileManager::saveLoaded(const ESM::ESMReader& reader) + { + // Can't do this in readRecord because the vectors might get reallocated as they grow + if (reader.getActorIdConverter()) + { + for (ProjectileState& projectile : mProjectiles) + reader.getActorIdConverter()->convert(projectile.mCaster, projectile.mCaster.mIndex); + for (MagicBoltState& bolt : mMagicBolts) + reader.getActorIdConverter()->convert(bolt.mCaster, bolt.mCaster.mIndex); + } + } + MWWorld::Ptr ProjectileManager::State::getCaster() { if (!mCasterHandle.isEmpty()) return mCasterHandle; - return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); + return MWBase::Environment::get().getWorldModel()->getPtr(mCaster); } } diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 432f463dbd..dd26f1df91 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -68,6 +68,7 @@ namespace MWWorld void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord(ESM::ESMReader& reader, uint32_t type); size_t countSavedGameRecords() const; + void saveLoaded(const ESM::ESMReader& reader); private: osg::ref_ptr mParent; @@ -81,11 +82,7 @@ namespace MWWorld osg::ref_ptr mNode; std::shared_ptr mEffectAnimationTime; - int mActorId; - int mProjectileId; - - // TODO: this will break when the game is saved and reloaded, since there is currently - // no way to write identifiers for non-actors to a savegame. + ESM::RefNum mCaster; MWWorld::Ptr mCasterHandle; MWWorld::Ptr getCaster(); @@ -96,6 +93,7 @@ namespace MWWorld // MW-id of an arrow projectile ESM::RefId mIdArrow; + int mProjectileId; bool mToDelete; }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fcb479db45..8681b12ab2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2307,11 +2307,12 @@ namespace MWWorld return true; } - void World::saveLoaded() + void World::saveLoaded(const ESM::ESMReader& reader) { mStore.rebuildIdsIndex(); mStore.validateDynamic(); mTimeManager->setup(mGlobalVariables); + mProjectileManager->saveLoaded(reader); } void World::setupPlayer() diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4b8d5def1b..af757af63c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -467,7 +467,7 @@ namespace MWWorld void applyDeferredPreviewRotationToPlayer(float dt) override; void disableDeferredPreviewRotation() override; - void saveLoaded() override; + void saveLoaded(const ESM::ESMReader& reader) override; void setupPlayer() override; void renderPlayer() override; diff --git a/components/esm3/projectilestate.cpp b/components/esm3/projectilestate.cpp index e20cefa882..a8df70b776 100644 --- a/components/esm3/projectilestate.cpp +++ b/components/esm3/projectilestate.cpp @@ -11,7 +11,7 @@ namespace ESM esm.writeHNRefId("ID__", mId); esm.writeHNT("VEC3", mPosition); esm.writeHNT("QUAT", mOrientation); - esm.writeHNT("ACTO", mActorId); + esm.writeFormId(mCaster, true, "ACTO"); } void BaseProjectileState::load(ESMReader& esm) @@ -19,7 +19,13 @@ namespace ESM mId = esm.getHNRefId("ID__"); esm.getHNT("VEC3", mPosition.mValues); esm.getHNT("QUAT", mOrientation.mValues); - esm.getHNT(mActorId, "ACTO"); + if (esm.getFormatVersion() <= MaxActorIdSaveGameFormatVersion) + { + mCaster.mIndex = -1; + esm.getHNT(mCaster.mIndex, "ACTO"); + } + else + mCaster = esm.getFormId(true, "ACTO"); } void MagicBoltState::save(ESMWriter& esm) const diff --git a/components/esm3/projectilestate.hpp b/components/esm3/projectilestate.hpp index c89d55c0e5..4c34ce3ca1 100644 --- a/components/esm3/projectilestate.hpp +++ b/components/esm3/projectilestate.hpp @@ -24,7 +24,7 @@ namespace ESM Vector3 mPosition; Quaternion mOrientation; - int32_t mActorId; + RefNum mCaster; void load(ESMReader& esm); void save(ESMWriter& esm) const;