diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 22eaad8dc1..c363c2b808 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -294,7 +294,7 @@ namespace MWBase /// It only applies to the current form the NPC is in. virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; - virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0; + virtual void cleanupSummonedCreature(ESM::RefNum creature) = 0; virtual void confiscateStolenItemToOwner( const MWWorld::Ptr& player, const MWWorld::Ptr& item, const MWWorld::Ptr& victim, int count) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 1b6dd4cbfb..bb8ccc5536 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -312,7 +312,7 @@ namespace MWGui // Clean up summoned creatures as well auto& creatureMap = creatureStats.getSummonedCreatureMap(); for (const auto& creature : creatureMap) - MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(ptr, creature.second); + MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(creature.second); creatureMap.clear(); // Check if we are a summon and inform our master we've bit the dust @@ -323,7 +323,7 @@ namespace MWGui const auto& summoner = package->getTarget(); auto& summons = summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap(); auto it = std::find_if(summons.begin(), summons.end(), - [&](const auto& entry) { return entry.second == creatureStats.getActorId(); }); + [&](const auto& entry) { return entry.second == ptr.getCellRef().getRefNum(); }); if (it != summons.end()) { auto summon = *it; diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index d36b31a931..4f4437fbc0 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -545,9 +545,8 @@ namespace MWLua return sol::make_object(lua, LObject(itemPtr)); }); activeSpellT["caster"] - = sol::readonly_property([lua = state.lua_state()](const ActiveSpell& activeSpell) -> sol::object { - auto caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId( - activeSpell.mParams.getCasterActorId()); + = sol::readonly_property([lua = lua.lua_state()](const ActiveSpell& activeSpell) -> sol::object { + auto caster = MWBase::Environment::get().getWorldModel()->getPtr(activeSpell.mParams.getCaster()); if (caster.isEmpty()) return sol::nil; else diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 109b75e1d4..d2f4e7d2d4 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/worldmodel.hpp" namespace { @@ -103,20 +105,19 @@ namespace MWMechanics const MWWorld::Ptr& caster, const ESM::RefId& id, std::string_view sourceName, ESM::RefNum item) : mSourceSpellId(id) , mDisplayName(sourceName) - , mCasterActorId(-1) , mItem(item) , mFlags() , mWorsenings(-1) { if (!caster.isEmpty() && caster.getClass().isActor()) - mCasterActorId = caster.getClass().getCreatureStats(caster).getActorId(); + mCaster = caster.getCellRef().getRefNum(); } ActiveSpells::ActiveSpellParams::ActiveSpellParams( const ESM::Spell* spell, const MWWorld::Ptr& actor, bool ignoreResistances) : mSourceSpellId(spell->mId) , mDisplayName(spell->mName) - , mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId()) + , mCaster(actor.getCellRef().getRefNum()) , mFlags() , mWorsenings(-1) { @@ -131,7 +132,7 @@ namespace MWMechanics const MWWorld::ConstPtr& item, const ESM::Enchantment* enchantment, const MWWorld::Ptr& actor) : mSourceSpellId(item.getCellRef().getRefId()) , mDisplayName(item.getClass().getName(item)) - , mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId()) + , mCaster(actor.getCellRef().getRefNum()) , mItem(item.getCellRef().getRefNum()) , mFlags() , mWorsenings(-1) @@ -146,7 +147,7 @@ namespace MWMechanics , mSourceSpellId(params.mSourceSpellId) , mEffects(params.mEffects) , mDisplayName(params.mDisplayName) - , mCasterActorId(params.mCasterActorId) + , mCaster(params.mCaster) , mItem(params.mItem) , mFlags(params.mFlags) , mWorsenings(params.mWorsenings) @@ -157,7 +158,7 @@ namespace MWMechanics ActiveSpells::ActiveSpellParams::ActiveSpellParams(const ActiveSpellParams& params, const MWWorld::Ptr& actor) : mSourceSpellId(params.mSourceSpellId) , mDisplayName(params.mDisplayName) - , mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId()) + , mCaster(actor.getCellRef().getRefNum()) , mItem(params.mItem) , mFlags(params.mFlags) , mWorsenings(-1) @@ -171,7 +172,7 @@ namespace MWMechanics params.mSourceSpellId = mSourceSpellId; params.mEffects = mEffects; params.mDisplayName = mDisplayName; - params.mCasterActorId = mCasterActorId; + params.mCaster = mCaster; params.mItem = mItem; params.mFlags = mFlags; params.mWorsenings = mWorsenings; @@ -375,8 +376,7 @@ namespace MWMechanics bool ActiveSpells::updateActiveSpell( const MWWorld::Ptr& ptr, float duration, Collection::iterator& spellIt, UpdateContext& context) { - const auto caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId( - spellIt->mCasterActorId); // Maybe make this search outside active grid? + const auto caster = MWBase::Environment::get().getWorldModel()->getPtr(spellIt->mCaster); bool removedSpell = false; std::optional reflected; for (auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();) @@ -484,8 +484,8 @@ namespace MWMechanics if (!spell.hasFlag(ESM::ActiveSpells::Flag_Stackable)) { auto found = std::find_if(mSpells.begin(), mSpells.end(), [&](const auto& existing) { - return spell.mSourceSpellId == existing.mSourceSpellId - && spell.mCasterActorId == existing.mCasterActorId && spell.mItem == existing.mItem; + return spell.mSourceSpellId == existing.mSourceSpellId && spell.mCaster == existing.mCaster + && spell.mItem == existing.mItem; }); if (found != mSpells.end()) { @@ -659,9 +659,9 @@ namespace MWMechanics ptr); } - void ActiveSpells::purge(const MWWorld::Ptr& ptr, int casterActorId) + void ActiveSpells::purge(const MWWorld::Ptr& ptr, ESM::RefNum actor) { - purge([=](const ActiveSpellParams& params) { return params.mCasterActorId == casterActorId; }, ptr); + purge([=](const ActiveSpellParams& params) { return params.mCaster == actor; }, ptr); } void ActiveSpells::clear(const MWWorld::Ptr& ptr) @@ -698,6 +698,22 @@ namespace MWMechanics } for (const ESM::ActiveSpells::ActiveSpellParams& spell : state.mQueue) mQueue.emplace_back(ActiveSpellParams{ spell }); + if (state.mActorIdConverter) + { + const auto convertSummons = [converter = state.mActorIdConverter](auto& collection) { + for (ActiveSpellParams& params : collection) + { + converter->convert(params.mCaster, params.mCaster.mIndex); + for (ESM::ActiveEffect& effect : params.mEffects) + { + if (ESM::RefNum* refNum = std::get_if(&effect.mArg)) + converter->convert(*refNum, refNum->mIndex); + } + } + }; + convertSummons(mSpells); + convertSummons(mQueue); + } } void ActiveSpells::unloadActor(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 32046256fd..c465e28556 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -37,7 +37,7 @@ namespace MWMechanics ESM::RefId mSourceSpellId; std::vector mEffects; std::string mDisplayName; - int mCasterActorId; + ESM::RefNum mCaster; ESM::RefNum mItem; ESM::ActiveSpells::Flags mFlags; int mWorsenings; @@ -69,7 +69,7 @@ namespace MWMechanics const std::vector& getEffects() const { return mEffects; } std::vector& getEffects() { return mEffects; } - int getCasterActorId() const { return mCasterActorId; } + ESM::RefNum getCaster() const { return mCaster; } int getWorsenings() const { return mWorsenings; } @@ -157,8 +157,8 @@ namespace MWMechanics void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr); void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr); - /// Remove all effects that were cast by \a casterActorId - void purge(const MWWorld::Ptr& ptr, int casterActorId); + /// Remove all effects that were cast by \a actor + void purge(const MWWorld::Ptr& ptr, ESM::RefNum actor); /// Remove all spells void clear(const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9e0279e8b9..0f1fe34701 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -179,7 +179,7 @@ namespace { if (effect.mEffectId != ESM::MagicEffect::Soultrap || effect.mMagnitude <= 0.f) continue; - MWWorld::Ptr caster = world->searchPtrViaActorId(params.getCasterActorId()); + MWWorld::Ptr caster = MWBase::Environment::get().getWorldModel()->getPtr(params.getCaster()); if (caster.isEmpty() || !caster.getClass().isActor()) continue; @@ -791,8 +791,7 @@ namespace MWMechanics { bool actorKilled = false; - MWWorld::Ptr caster - = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.getCasterActorId()); + MWWorld::Ptr caster = MWBase::Environment::get().getWorldModel()->getPtr(spell.getCaster()); if (caster.isEmpty()) continue; for (const auto& effect : spell.getEffects()) @@ -833,7 +832,7 @@ namespace MWMechanics if (!creature.isInCell()) return; - if (!creatureStats.getSummonedCreatureMap().empty() || !creatureStats.getSummonedCreatureGraveyard().empty()) + if (!creatureStats.getSummonedCreatureMap().empty()) updateSummons(creature, mTimerDisposeSummonsCorpses == 0.f); } @@ -1816,7 +1815,7 @@ namespace MWMechanics = stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Vampirism).getMagnitude(); stats.getActiveSpells().clear(actor.getPtr()); // Make sure spell effects are removed - purgeSpellEffects(stats.getActorId()); + purgeSpellEffects(actor.getPtr().getCellRef().getRefNum()); stats.getMagicEffects().add(ESM::MagicEffect::Vampirism, vampirism); @@ -1834,9 +1833,9 @@ namespace MWMechanics } } - void Actors::cleanupSummonedCreature(MWMechanics::CreatureStats& casterStats, int creatureActorId) const + void Actors::cleanupSummonedCreature(ESM::RefNum creature) const { - const MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId); + const MWWorld::Ptr ptr = MWBase::Environment::get().getWorldModel()->getPtr(creature); if (!ptr.isEmpty()) { MWBase::Environment::get().getWorld()->deleteObject(ptr); @@ -1849,24 +1848,16 @@ namespace MWMechanics ptr.getRefData().getPosition().asVec3()); // Remove the summoned creature's summoned creatures as well - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); - auto& creatureMap = stats.getSummonedCreatureMap(); - for (const auto& creature : creatureMap) - cleanupSummonedCreature(stats, creature.second); + auto& creatureMap = ptr.getClass().getCreatureStats(ptr).getSummonedCreatureMap(); + for (const auto& [_, refNum] : creatureMap) + cleanupSummonedCreature(refNum); creatureMap.clear(); } - else if (creatureActorId != -1) - { - // We didn't find the creature. It's probably in an inactive cell. - // Add to graveyard so we can delete it when the cell becomes active. - std::vector& graveyard = casterStats.getSummonedCreatureGraveyard(); - graveyard.push_back(creatureActorId); - } - purgeSpellEffects(creatureActorId); + purgeSpellEffects(creature); } - void Actors::purgeSpellEffects(int casterActorId) const + void Actors::purgeSpellEffects(ESM::RefNum creature) const { for (const Actor& actor : mActors) { @@ -1874,7 +1865,7 @@ namespace MWMechanics continue; MWMechanics::ActiveSpells& spells = actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActiveSpells(); - spells.purge(actor.getPtr(), casterActorId); + spells.purge(actor.getPtr(), creature); } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 3e34ed0d67..a941aa0395 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -35,7 +35,6 @@ namespace MWMechanics { class Actor; class CharacterController; - class CreatureStats; class SidingCache; class Actors @@ -128,7 +127,7 @@ namespace MWMechanics bool isAnyObjectInRange(const osg::Vec3f& position, float radius) const; - void cleanupSummonedCreature(CreatureStats& casterStats, int creatureActorId) const; + void cleanupSummonedCreature(ESM::RefNum creature) const; /// Returns the list of actors which are siding with the given actor in fights /**ie AiFollow or AiEscort is active and the target is the actor **/ @@ -189,7 +188,7 @@ namespace MWMechanics void killDeadActors(); - void purgeSpellEffects(int casterActorId) const; + void purgeSpellEffects(ESM::RefNum creature) const; void predictAndAvoidCollisions(float duration) const; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 3702c981ae..fb6deaa40f 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -78,7 +78,7 @@ namespace MWMechanics AiFollow::AiFollow(const ESM::AiSequence::AiFollow* follow) : TypedAiPackage( - makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded).withRepeat(follow->mRepeat)) + makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded).withRepeat(follow->mRepeat)) , mAlwaysFollow(follow->mAlwaysFollow) , mDuration(follow->mData.mDuration) , mRemainingDuration(follow->mRemainingDuration) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 30b840dd22..fbae265cc6 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -181,6 +181,7 @@ namespace MWMechanics bool mShortcutProhibited = false; // shortcutting may be prohibited after unsuccessful attempt friend class AiSequence; + private: bool isNearInactiveCell(osg::Vec3f position); }; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 8be31dbd66..2f15b35dd5 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -41,7 +41,8 @@ namespace MWMechanics if (target.isEmpty() || !target.getCellRef().getCount() || !target.getRefData().isEnabled()) return true; - // This is equivalent to checking if the actor is registered with the mechanics manager since every actor has a script + // This is equivalent to checking if the actor is registered with the mechanics manager since every actor has a + // script if (const MWLua::LocalScripts* scripts = target.getRefData().getLuaScripts()) { if (!scripts->isActive()) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 3329d87cbf..f8f8685b42 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -550,7 +551,6 @@ namespace MWMechanics mMagicEffects.writeState(state.mMagicEffects); state.mSummonedCreatures = mSummonedCreatures; - state.mSummonGraveyard = mSummonGraveyard; state.mHasAiSettings = true; for (size_t i = 0; i < state.mAiSettings.size(); ++i) @@ -596,7 +596,7 @@ namespace MWMechanics mActorId = state.mActorId; mDeathAnimation = state.mDeathAnimation; mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); - // mHitAttemptActorId = state.mHitAttemptActorId; + // mHitAttemptActor = state.mHitAttemptActor; mSpells.readState(state.mSpells, this); mActiveSpells.readState(state.mActiveSpells); @@ -604,13 +604,19 @@ namespace MWMechanics mMagicEffects.readState(state.mMagicEffects); mSummonedCreatures = state.mSummonedCreatures; - mSummonGraveyard = state.mSummonGraveyard; if (state.mHasAiSettings) for (size_t i = 0; i < state.mAiSettings.size(); ++i) mAiSettings[i].readState(state.mAiSettings[i]); if (state.mRecalcDynamicStats) recalculateMagicka(); + if (state.mAiSequence.mActorIdConverter) + { + for (auto& [_, refNum] : mSummonedCreatures) + state.mAiSequence.mActorIdConverter->convert(refNum, refNum.mIndex); + auto& graveyard = state.mAiSequence.mActorIdConverter->mGraveyard; + graveyard.insert(graveyard.end(), state.mSummonGraveyard.begin(), state.mSummonGraveyard.end()); + } } void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) @@ -677,16 +683,11 @@ namespace MWMechanics return mTimeOfDeath; } - std::multimap& CreatureStats::getSummonedCreatureMap() + std::multimap& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } - std::vector& CreatureStats::getSummonedCreatureGraveyard() - { - return mSummonGraveyard; - } - void CreatureStats::updateAwareness(float duration) { mAwarenessTimer += duration; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index ab8e03a294..5e13a86c11 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -84,11 +84,7 @@ namespace MWMechanics MWWorld::TimeStamp mTimeOfDeath; private: - std::multimap mSummonedCreatures; // - - // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. - // This may be necessary when the creature is in an inactive cell. - std::vector mSummonGraveyard; + std::multimap mSummonedCreatures; // float mAwarenessTimer = 0.f; int mAwarenessRoll = -1; @@ -237,8 +233,7 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; - std::multimap& getSummonedCreatureMap(); // - std::vector& getSummonedCreatureGraveyard(); // ActorIds + std::multimap& getSummonedCreatureMap(); // enum Flag { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 3db7015d63..ad6a9c7c45 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -2042,9 +2042,9 @@ namespace MWMechanics skill.setModifier(acrobatics->mWerewolfValue - skill.getModified()); } - void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) + void MechanicsManager::cleanupSummonedCreature(ESM::RefNum creature) { - mActors.cleanupSummonedCreature(caster.getClass().getCreatureStats(caster), creatureActorId); + mActors.cleanupSummonedCreature(creature); } void MechanicsManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 53eee5ed83..83da60d99c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -227,7 +227,7 @@ namespace MWMechanics void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) override; void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) override; - void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) override; + void cleanupSummonedCreature(ESM::RefNum creature) override; void confiscateStolenItemToOwner( const MWWorld::Ptr& player, const MWWorld::Ptr& item, const MWWorld::Ptr& victim, int count) override; diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index a1beca14bb..13a84ba4a0 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -33,6 +33,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/worldmodel.hpp" namespace { @@ -1106,6 +1107,7 @@ namespace MWMechanics const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spellParams, const ESM::ActiveEffect& effect) { const auto world = MWBase::Environment::get().getWorld(); + const auto worldModel = MWBase::Environment::get().getWorldModel(); auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); switch (effect.mEffectId) { @@ -1192,14 +1194,14 @@ namespace MWMechanics case ESM::MagicEffect::SummonCreature04: case ESM::MagicEffect::SummonCreature05: { - int actorId = effect.getActorId(); - if (actorId != -1) - MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, actorId); + ESM::RefNum actor = effect.getActor(); + if (actor.isSet()) + MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(actor); auto& summons = target.getClass().getCreatureStats(target).getSummonedCreatureMap(); auto [begin, end] = summons.equal_range(effect.mEffectId); for (auto it = begin; it != end; ++it) { - if (it->second == actorId) + if (it->second == actor) { summons.erase(it); break; @@ -1284,7 +1286,7 @@ namespace MWMechanics break; case ESM::MagicEffect::AbsorbAttribute: { - const auto caster = world->searchPtrViaActorId(spellParams.getCasterActorId()); + const auto caster = worldModel->getPtr(spellParams.getCaster()); restoreAttribute(target, effect, effect.mMagnitude); if (!caster.isEmpty()) fortifyAttribute(caster, effect, -effect.mMagnitude); @@ -1294,7 +1296,7 @@ namespace MWMechanics { if (target.getClass().isNpc()) restoreSkill(target, effect, effect.mMagnitude); - const auto caster = world->searchPtrViaActorId(spellParams.getCasterActorId()); + const auto caster = worldModel->getPtr(spellParams.getCaster()); if (!caster.isEmpty() && caster.getClass().isNpc()) fortifySkill(caster, effect, -effect.mMagnitude); } diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 7041254c4e..1e811a0658 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -82,10 +82,10 @@ namespace bool isSpellActive(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const ESM::RefId& id) { - int actorId = caster.getClass().getCreatureStats(caster).getActorId(); + ESM::RefNum actor = caster.getCellRef().getRefNum(); const auto& active = target.getClass().getCreatureStats(target).getActiveSpells(); return std::find_if(active.begin(), active.end(), [&](const auto& spell) { - return spell.getCasterActorId() == actorId && spell.getSourceSpellId() == id; + return spell.getCaster() == actor && spell.getSourceSpellId() == id; }) != active.end(); } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 78d4976040..9f7b59b030 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -13,6 +13,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/worldmodel.hpp" #include "../mwrender/animation.hpp" @@ -80,10 +81,10 @@ namespace MWMechanics return ESM::RefId(); } - int summonCreature(int effectId, const MWWorld::Ptr& summoner) + ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner) { const ESM::RefId& creatureID = getSummonedCreature(effectId); - int creatureActorId = -1; + ESM::RefNum creature; if (!creatureID.empty()) { try @@ -91,13 +92,12 @@ namespace MWMechanics auto world = MWBase::Environment::get().getWorld(); MWWorld::ManualRef ref(world->getStore(), creatureID, 1); MWWorld::Ptr placed = world->safePlaceObject(ref.getPtr(), summoner, summoner.getCell(), 0, 120.f); - - MWMechanics::CreatureStats& summonedCreatureStats = placed.getClass().getCreatureStats(placed); + MWBase::Environment::get().getWorldModel()->registerPtr(placed); + creature = placed.getCellRef().getRefNum(); // Make the summoned creature follow its master and help in fights AiFollow package(summoner); - summonedCreatureStats.getAiSequence().stack(package, placed); - creatureActorId = summonedCreatureStats.getActorId(); + placed.getClass().getCreatureStats(placed).getAiSequence().stack(package, placed); MWRender::Animation* anim = world->getAnimation(placed); if (anim) @@ -117,9 +117,9 @@ namespace MWMechanics // log } - summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap().emplace(effectId, creatureActorId); + summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap().emplace(effectId, creature); } - return creatureActorId; + return creature; } void updateSummons(const MWWorld::Ptr& summoner, bool cleanup) @@ -127,24 +127,18 @@ namespace MWMechanics MWMechanics::CreatureStats& creatureStats = summoner.getClass().getCreatureStats(summoner); auto& creatureMap = creatureStats.getSummonedCreatureMap(); - std::vector graveyard = creatureStats.getSummonedCreatureGraveyard(); - creatureStats.getSummonedCreatureGraveyard().clear(); - - for (const int creature : graveyard) - MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, creature); - if (!cleanup) return; for (auto it = creatureMap.begin(); it != creatureMap.end();) { - if (it->second == -1) + if (!it->second.isSet()) { // Keep the spell effect active if we failed to spawn anything it++; continue; } - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorldModel()->getPtr(it->second); if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()) { @@ -158,15 +152,15 @@ namespace MWMechanics } } - void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon) + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon) { auto& creatureStats = summoner.getClass().getCreatureStats(summoner); creatureStats.getActiveSpells().purge( [summon](const auto& spell, const auto& effect) { - return effect.mEffectId == summon.first && effect.getActorId() == summon.second; + return effect.mEffectId == summon.first && effect.getActor() == summon.second; }, summoner); - MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, summon.second); + MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summon.second); } } diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index a341ee6e93..3992f8399f 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -3,6 +3,9 @@ #include #include + +#include + namespace ESM { class RefId; @@ -18,9 +21,9 @@ namespace MWMechanics ESM::RefId getSummonedCreature(int effectId); - void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); - int summonCreature(int effectId, const MWWorld::Ptr& summoner); + ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner); void updateSummons(const MWWorld::Ptr& summoner, bool cleanup); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 05a4540afe..7e515bffe7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -651,6 +651,12 @@ void MWState::StateManager::loadGame(const Character* character, const std::file MWBase::Environment::get().getWorldScene()->markCellAsUnchanged(); MWBase::Environment::get().getLuaManager()->gameLoaded(); + for (int actorId : actorIdConverter.mGraveyard) + { + auto mapped = actorIdConverter.mMappings.find(actorId); + if (mapped != actorIdConverter.mMappings.end()) + MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mapped->second); + } } catch (const SaveVersionTooNewError& e) { diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index 6323dcb772..ff38d909ae 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -61,7 +61,7 @@ namespace MWWorld ESM::ActiveSpells::ActiveSpellParams params; params.mSourceSpellId = id; params.mDisplayName = spell->mName; - params.mCasterActorId = creatureStats.mActorId; + params.mCaster.mIndex = creatureStats.mActorId; if (spell->mData.mType == ESM::Spell::ST_Ability) params.mFlags = ESM::Compatibility::ActiveSpells::Type_Ability_Flags; else @@ -137,7 +137,7 @@ namespace MWWorld ESM::ActiveSpells::ActiveSpellParams params; params.mSourceSpellId = id; params.mDisplayName = std::move(name); - params.mCasterActorId = creatureStats.mActorId; + params.mCaster.mIndex = creatureStats.mActorId; params.mFlags = ESM::Compatibility::ActiveSpells::Type_Enchantment_Flags; params.mWorsenings = -1; params.mNextWorsening = ESM::TimeStamp(); @@ -196,7 +196,7 @@ namespace MWWorld { if (effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex) { - effect.mArg = actorId; + effect.mArg = ESM::RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }; effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; found = true; break; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a8cbdb447b..7fa88b5784 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -344,6 +345,8 @@ namespace MWWorld MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(mPlayer); mPlayer.load(player.mObject); MWBase::Environment::get().getWorldModel()->registerPtr(getPlayer()); + if (ESM::ActorIdConverter* converter = reader.getActorIdConverter()) + converter->mMappings.emplace(player.mObject.mCreatureStats.mActorId, mPlayer.mRef.getRefNum()); for (size_t i = 0; i < mSaveAttributes.size(); ++i) mSaveAttributes[i] = player.mSaveAttributes[i]; diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index 7c84afe489..55ce37f95d 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -70,25 +70,6 @@ namespace ESM return false; } - struct ToInt - { - int effectId; - - int operator()(const ESM::RefId& id) const - { - if (!id.empty()) - { - if (affectsAttribute(effectId)) - return ESM::Attribute::refIdToIndex(id); - else if (affectsSkill(effectId)) - return ESM::Skill::refIdToIndex(id); - } - return -1; - } - - int operator()(int actor) const { return actor; } - }; - void saveImpl(ESMWriter& esm, const std::vector& spells, NAME tag) { for (const auto& params : spells) @@ -96,7 +77,7 @@ namespace ESM esm.writeHNRefId(tag, params.mSourceSpellId); esm.writeHNRefId("SPID", params.mActiveSpellId); - esm.writeHNT("CAST", params.mCasterActorId); + esm.writeFormId(params.mCaster, true, "CAST"); esm.writeHNString("DISP", params.mDisplayName); esm.writeHNT("FLAG", params.mFlags); if (params.mItem.isSet()) @@ -110,9 +91,16 @@ namespace ESM for (auto& effect : params.mEffects) { esm.writeHNT("MGEF", effect.mEffectId); - int arg = std::visit(ToInt{ effect.mEffectId }, effect.mArg); - if (arg != -1) - esm.writeHNT("ARG_", arg); + if (const ESM::RefId* id = std::get_if(&effect.mArg)) + { + if (!id->empty()) + esm.writeHNRefId("ARG_", *id); + } + else if (const ESM::RefNum* actor = std::get_if(&effect.mArg)) + { + if (actor->isSet()) + esm.writeFormId(*actor, true, "SUM_"); + } esm.writeHNT("MAGN", effect.mMagnitude); esm.writeHNT("MAGN", effect.mMinMagnitude); esm.writeHNT("MAGN", effect.mMaxMagnitude); @@ -134,7 +122,10 @@ namespace ESM params.mSourceSpellId = esm.getRefId(); if (format > MaxActiveSpellTypeVersion) params.mActiveSpellId = esm.getHNRefId("SPID"); - esm.getHNT(params.mCasterActorId, "CAST"); + if (format <= MaxActorIdSaveGameFormatVersion) + esm.getHNT(params.mCaster.mIndex, "CAST"); + else + params.mCaster = esm.getFormId("CAST"); params.mDisplayName = esm.getHNString("DISP"); if (format <= MaxClearModifiersFormatVersion) params.mFlags = Compatibility::ActiveSpells::Type_Temporary_Flags; @@ -186,17 +177,24 @@ namespace ESM { ActiveEffect effect; esm.getHT(effect.mEffectId); - int32_t arg = -1; - esm.getHNOT(arg, "ARG_"); - if (arg >= 0) + if (format <= MaxActorIdSaveGameFormatVersion) { - if (isSummon(effect.mEffectId)) - effect.mArg = arg; - else if (affectsAttribute(effect.mEffectId)) - effect.mArg = ESM::Attribute::indexToRefId(arg); - else if (affectsSkill(effect.mEffectId)) - effect.mArg = ESM::Skill::indexToRefId(arg); + int32_t arg = -1; + esm.getHNOT(arg, "ARG_"); + if (arg >= 0) + { + if (isSummon(effect.mEffectId)) + effect.mArg = RefNum{ .mIndex = static_cast(arg), .mContentFile = -1 }; + else if (affectsAttribute(effect.mEffectId)) + effect.mArg = ESM::Attribute::indexToRefId(arg); + else if (affectsSkill(effect.mEffectId)) + effect.mArg = ESM::Skill::indexToRefId(arg); + } } + else if (esm.peekNextSub("ARG_")) + effect.mArg = esm.getHNRefId("ARG_"); + else if (esm.peekNextSub("SUM_")) + effect.mArg = esm.getFormId("SUM_"); esm.getHNT(effect.mMagnitude, "MAGN"); if (format <= MaxClearModifiersFormatVersion) { @@ -235,21 +233,22 @@ namespace ESM void ActiveSpells::load(ESMReader& esm) { + mActorIdConverter = esm.getActorIdConverter(); loadImpl(esm, mSpells, "ID__"); loadImpl(esm, mQueue, "QID_"); } RefId ActiveEffect::getSkillOrAttribute() const { - if (const auto* id = std::get_if(&mArg)) + if (const ESM::RefId* id = std::get_if(&mArg)) return *id; return {}; } - int ActiveEffect::getActorId() const + RefNum ActiveEffect::getActor() const { - if (const auto* id = std::get_if(&mArg)) - return *id; - return -1; + if (const ESM::RefNum* actor = std::get_if(&mArg)) + return *actor; + return {}; } } diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index 3058e8e4de..0db1b0e61b 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -14,6 +14,7 @@ namespace ESM { class ESMReader; class ESMWriter; + class ActorIdConverter; // Parameters of an effect concerning lasting effects. // Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc. @@ -34,14 +35,14 @@ namespace ESM float mMagnitude; float mMinMagnitude; float mMaxMagnitude; - std::variant mArg; // skill, attribute, or summon + std::variant mArg; // skill, attribute, or summon float mDuration; float mTimeLeft; int32_t mEffectIndex; int32_t mFlags; RefId getSkillOrAttribute() const; - int getActorId() const; + RefNum getActor() const; }; // format 0, saved games only @@ -65,7 +66,7 @@ namespace ESM RefId mSourceSpellId; std::vector mEffects; std::string mDisplayName; - int32_t mCasterActorId; + RefNum mCaster; RefNum mItem; Flags mFlags; int32_t mWorsenings; @@ -74,6 +75,7 @@ namespace ESM std::vector mSpells; std::vector mQueue; + ActorIdConverter* mActorIdConverter = nullptr; void load(ESMReader& esm); void save(ESMWriter& esm) const; diff --git a/components/esm3/actoridconverter.hpp b/components/esm3/actoridconverter.hpp index 07a78bc2de..09b7e71aa1 100644 --- a/components/esm3/actoridconverter.hpp +++ b/components/esm3/actoridconverter.hpp @@ -14,6 +14,7 @@ namespace ESM public: std::map mMappings; + std::vector mGraveyard; void apply(); diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index 44c3bd993b..77c6644638 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -251,11 +251,6 @@ namespace ESM esm.writeHNT("ACID", actorId); } - for (int32_t key : mSummonGraveyard) - { - esm.writeHNT("GRAV", key); - } - esm.writeHNT("AISE", mHasAiSettings); if (mHasAiSettings) { diff --git a/components/esm3/creaturestats.hpp b/components/esm3/creaturestats.hpp index 6e65a52354..cba74e065d 100644 --- a/components/esm3/creaturestats.hpp +++ b/components/esm3/creaturestats.hpp @@ -43,7 +43,7 @@ namespace ESM std::array, 4> mAiSettings; std::map mSummonedCreatureMap; - std::multimap mSummonedCreatures; + std::multimap mSummonedCreatures; std::vector mSummonGraveyard; TimeStamp mTradeTime;