From 3e586ed693f834cd1f4c3fde6ed4c0f1c2082c7d Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Mon, 8 Dec 2025 18:43:16 -0500 Subject: [PATCH] Changed IRDTstruct (alchemical ingredient magic effects) to use RefIds instead of int for effectId. Changed various structs (ENAMstruct, EffectKey, SummonKey) to use RefId instead of strict StringRefId to account for null effects. --- apps/openmw/mwmechanics/activespells.cpp | 2 +- apps/openmw/mwmechanics/activespells.hpp | 2 +- apps/openmw/mwmechanics/alchemy.cpp | 12 +++--- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 4 +- apps/openmw/mwmechanics/magiceffects.hpp | 8 +++- apps/openmw/mwmechanics/spellcasting.cpp | 8 ++-- apps/openmw/mwmechanics/summoning.cpp | 47 ++++++++++++++++++----- apps/openmw/mwmechanics/summoning.hpp | 10 ++--- apps/openmw/mwworld/esmstore.cpp | 4 +- components/esm3/activespells.hpp | 2 +- components/esm3/effectlist.hpp | 4 +- components/esm3/loadingr.cpp | 30 ++++++++++++--- components/esm3/loadingr.hpp | 2 +- components/esm3/loadmgef.cpp | 2 + components/esm3/magiceffects.hpp | 8 ++-- 16 files changed, 97 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index aae756350d..81f1a9ae1d 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -646,7 +646,7 @@ namespace MWMechanics purge([=](const ActiveSpellParams& params) { return params.mActiveSpellId == id; }, ptr); } - void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, const ESM::MagicEffectId& effectId, ESM::RefId effectArg) + void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, const ESM::RefId& effectId, ESM::RefId effectArg) { purge( [=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) { diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 0824405f9a..6a82643e66 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -152,7 +152,7 @@ namespace MWMechanics void removeEffectsByActiveSpellId(const MWWorld::Ptr& ptr, const ESM::RefId& id); /// Remove all active effects with this effect id - void purgeEffect(const MWWorld::Ptr& ptr, const ESM::MagicEffectId& effectId, ESM::RefId effectArg = {}); + void purgeEffect(const MWWorld::Ptr& ptr, const ESM::RefId& effectId, ESM::RefId effectArg = {}); void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr); void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 3c08279463..a6534d9c93 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -30,12 +30,12 @@ namespace std::optional toKey(const ESM::Ingredient& ingredient, size_t i) { - if (ingredient.mData.mEffectID[i] < 0) + if (ingredient.mData.mEffectID[i].empty()) return {}; ESM::RefId arg = ESM::Skill::indexToRefId(ingredient.mData.mSkills[i]); if (arg.empty()) arg = ESM::Attribute::indexToRefId(ingredient.mData.mAttributes[i]); - return MWMechanics::EffectKey(ingredient.mData.mEffectID[i], arg); + return MWMechanics::EffectKey(*ingredient.mData.mEffectID[i].getIf(), arg); } bool containsEffect(const ESM::Ingredient& ingredient, const MWMechanics::EffectKey& effect) @@ -172,11 +172,11 @@ void MWMechanics::Alchemy::updateEffects() for (const auto& effectKey : effects) { const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().find(effectKey.mId); + = MWBase::Environment::get().getESMStore()->get().search(effectKey.mId); if (magicEffect->mData.mBaseCost <= 0) { - const std::string os = "invalid base cost for magic effect " + std::to_string(effectKey.mId); + const std::string os = "invalid base cost for magic effect " + std::string(ESM::MagicEffect::refIdToName(effectKey.mId)); throw std::runtime_error(os); } @@ -217,7 +217,7 @@ void MWMechanics::Alchemy::updateEffects() if (magnitude > 0 && duration > 0) { ESM::ENAMstruct effect; - effect.mEffectID = static_cast(effectKey.mId); + effect.mEffectID = effectKey.mId; effect.mAttribute = -1; effect.mSkill = -1; @@ -621,7 +621,7 @@ std::vector MWMechanics::Alchemy::effectsDescription( if (alchemySkill < fWortChanceValue * static_cast(i + 1)) break; - if (effectID != -1) + if (!effectID.empty()) { const ESM::Attribute* attribute = store->get().search(ESM::Attribute::indexToRefId(data.mAttributes[i])); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 83fd5a1f4d..3531b7fde1 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -649,7 +649,7 @@ namespace MWMechanics return mTimeOfDeath; } - std::multimap& CreatureStats::getSummonedCreatureMap() + std::multimap& CreatureStats::getSummonedCreatureMap() { return mSummonedCreatures; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 29c2c73e94..4cf4c2e421 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -82,7 +82,7 @@ namespace MWMechanics MWWorld::TimeStamp mTimeOfDeath; private: - std::multimap mSummonedCreatures; // + std::multimap mSummonedCreatures; // float mAwarenessTimer = 0.f; int mAwarenessRoll = -1; @@ -231,7 +231,7 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; - std::multimap& getSummonedCreatureMap(); // + std::multimap& getSummonedCreatureMap(); // enum Flag { diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 45de2c7beb..491f011807 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -23,11 +23,17 @@ namespace MWMechanics { struct EffectKey { - ESM::MagicEffectId mId; + ESM::RefId mId; ESM::RefId mArg; // skill or ability EffectKey(); + EffectKey(ESM::RefId id, ESM::RefId arg = {}) + : mId(id) + , mArg(arg) + { + } + EffectKey(ESM::MagicEffectId id, ESM::RefId arg = {}) : mId(id) , mArg(arg) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d17bd97744..398ad09d2b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -320,8 +320,8 @@ namespace MWMechanics ESM::RefId school = ESM::Skill::Alteration; if (!enchantment->mEffects.mList.empty()) { - short effectId = enchantment->mEffects.mList.front().mData.mEffectID; - const ESM::MagicEffect* magicEffect = store->get().find(effectId); + ESM::RefId effectId = enchantment->mEffects.mList.front().mData.mEffectID; + const ESM::MagicEffect* magicEffect = store->get().search(effectId); school = magicEffect->mData.mSchool; } @@ -509,7 +509,7 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); if (animation) { - animation->addEffect(castStaticModel.value(), ESM::MagicEffect::indexToName(effect->mIndex), false, {}, + animation->addEffect(castStaticModel.value(), ESM::MagicEffect::refIdToName(effect->mId), false, {}, effect->mParticle); } else @@ -585,7 +585,7 @@ namespace MWMechanics { const VFS::Path::Normalized castStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(castStatic->mModel)); - anim->addEffect(castStaticModel.value(), ESM::MagicEffect::indexToName(magicEffect.mIndex), loop, {}, + anim->addEffect(castStaticModel.value(), ESM::MagicEffect::refIdToName(magicEffect.mId), loop, {}, magicEffect.mParticle); } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index bf2da8206f..e6c9a85ff2 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -23,9 +23,41 @@ namespace MWMechanics { - static const std::map& getSummonMap() + bool isSummoningEffect(const ESM::RefId& effectId) { - static std::map summonMap; + if (effectId.empty()) + return false; + static const std::array summonEffects{ + ESM::MagicEffect::SummonAncestralGhost, + ESM::MagicEffect::SummonBonelord, + ESM::MagicEffect::SummonBonewalker, + ESM::MagicEffect::SummonCenturionSphere, + ESM::MagicEffect::SummonClannfear, + ESM::MagicEffect::SummonDaedroth, + ESM::MagicEffect::SummonDremora, + ESM::MagicEffect::SummonFabricant, + ESM::MagicEffect::SummonFlameAtronach, + ESM::MagicEffect::SummonFrostAtronach, + ESM::MagicEffect::SummonGoldenSaint, + ESM::MagicEffect::SummonGreaterBonewalker, + ESM::MagicEffect::SummonHunger, + ESM::MagicEffect::SummonScamp, + ESM::MagicEffect::SummonSkeletalMinion, + ESM::MagicEffect::SummonStormAtronach, + ESM::MagicEffect::SummonWingedTwilight, + ESM::MagicEffect::SummonWolf, + ESM::MagicEffect::SummonBear, + ESM::MagicEffect::SummonBonewolf, + ESM::MagicEffect::SummonCreature04, + ESM::MagicEffect::SummonCreature05, + }; + return (std::find(summonEffects.begin(), summonEffects.end(), *effectId.getIf()) + != summonEffects.end()); + } + + static const std::map& getSummonMap() + { + static std::map summonMap; if (summonMap.size() > 0) return summonMap; @@ -63,12 +95,7 @@ namespace MWMechanics return summonMap; } - bool isSummoningEffect(const ESM::MagicEffectId& effectId) - { - return getSummonMap().contains(effectId); - } - - ESM::RefId getSummonedCreature(const ESM::MagicEffectId& effectId) + ESM::RefId getSummonedCreature(const ESM::RefId& effectId) { const auto& summonMap = getSummonMap(); auto it = summonMap.find(effectId); @@ -79,7 +106,7 @@ namespace MWMechanics return ESM::RefId(); } - ESM::RefNum summonCreature(const ESM::MagicEffectId& effectId, const MWWorld::Ptr& summoner) + ESM::RefNum summonCreature(const ESM::RefId& effectId, const MWWorld::Ptr& summoner) { const ESM::RefId& creatureID = getSummonedCreature(effectId); ESM::RefNum creature; @@ -150,7 +177,7 @@ 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( diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 34fc68dffc..09dac36d39 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -10,8 +10,6 @@ namespace ESM { class RefId; class StringRefId; - - using MagicEffectId = StringRefId; } namespace MWWorld { @@ -20,13 +18,13 @@ namespace MWWorld namespace MWMechanics { - bool isSummoningEffect(const ESM::MagicEffectId& effectId); + bool isSummoningEffect(const ESM::RefId& effectId); - ESM::RefId getSummonedCreature(const ESM::MagicEffectId& effectId); + ESM::RefId getSummonedCreature(const ESM::RefId& effectId); - void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); - ESM::RefNum summonCreature(const ESM::MagicEffectId& effectId, const MWWorld::Ptr& summoner); + ESM::RefNum summonCreature(const ESM::RefId& effectId, const MWWorld::Ptr& summoner); void updateSummons(const MWWorld::Ptr& summoner, bool cleanup); } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ec55de0b91..ff3bf9fd5b 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -186,7 +186,7 @@ namespace iter->mData.mAttribute = -1; Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId << ": dropping unexpected attribute argument of " - << iter->mData.mEffectID.getRefIdString() << " effect"; + << ESM::MagicEffect::refIdToName(iter->mData.mEffectID) << " effect"; changed = true; } @@ -195,7 +195,7 @@ namespace iter->mData.mSkill = -1; Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId << ": dropping unexpected skill argument of " - <mData.mEffectID.getRefIdString() << " effect"; + << ESM::MagicEffect::refIdToName(iter->mData.mEffectID) << " effect"; changed = true; } diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index b53e77a6e6..3cd96e9a16 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -31,7 +31,7 @@ namespace ESM Flag_Invalid = 1 << 5 }; - MagicEffectId mEffectId; + RefId mEffectId; float mMagnitude; float mMinMagnitude; float mMaxMagnitude; diff --git a/components/esm3/effectlist.hpp b/components/esm3/effectlist.hpp index b02f14dfc8..f7350f40fc 100644 --- a/components/esm3/effectlist.hpp +++ b/components/esm3/effectlist.hpp @@ -11,15 +11,13 @@ namespace ESM class ESMReader; class ESMWriter; - using MagicEffectId = StringRefId; - /** Defines a spell effect. Shared between SPEL (Spells), ALCH (Potions) and ENCH (Item enchantments) records */ struct ENAMstruct { // Magical effect, serialized to int16 - ESM::MagicEffectId mEffectID; + ESM::RefId mEffectID; // Which skills/attributes are affected (for restore/drain spells // etc.) diff --git a/components/esm3/loadingr.cpp b/components/esm3/loadingr.cpp index 6a4753d8e4..b0b5065434 100644 --- a/components/esm3/loadingr.cpp +++ b/components/esm3/loadingr.cpp @@ -3,14 +3,26 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include #include namespace ESM { + template + constexpr bool loading = !std::is_const_v>; + template T> void decompose(T&& v, const auto& f) { - f(v.mWeight, v.mValue, v.mEffectID, v.mSkills, v.mAttributes); + int32_t ioEffectID[4]; + std::transform( + std::begin(v.mEffectID), std::end(v.mEffectID), std::begin(ioEffectID), ESM::MagicEffect::refIdToIndex); + f(v.mWeight, v.mValue, ioEffectID, v.mSkills, v.mAttributes); + if constexpr (loading) + { + std::transform( + std::begin(ioEffectID), std::end(ioEffectID), std::begin(v.mEffectID), ESM::MagicEffect::indexToRefId); + } } void Ingredient::load(ESMReader& esm, bool& isDeleted) @@ -63,15 +75,21 @@ namespace ESM // horrible hack to fix broken data in records for (int i = 0; i < 4; ++i) { - if (mData.mEffectID[i] != 85 && mData.mEffectID[i] != 22 && mData.mEffectID[i] != 17 - && mData.mEffectID[i] != 79 && mData.mEffectID[i] != 74) + if (mData.mEffectID[i] != ESM::MagicEffect::AbsorbAttribute && + mData.mEffectID[i] != ESM::MagicEffect::DamageAttribute && + mData.mEffectID[i] != ESM::MagicEffect::DrainAttribute && + mData.mEffectID[i] != ESM::MagicEffect::FortifyAttribute && + mData.mEffectID[i] != ESM::MagicEffect::RestoreAttribute) { mData.mAttributes[i] = -1; } // is this relevant in cycle from 0 to 4? - if (mData.mEffectID[i] != 89 && mData.mEffectID[i] != 26 && mData.mEffectID[i] != 21 - && mData.mEffectID[i] != 83 && mData.mEffectID[i] != 78) + if (mData.mEffectID[i] != ESM::MagicEffect::AbsorbSkill && + mData.mEffectID[i] != ESM::MagicEffect::DamageSkill && + mData.mEffectID[i] != ESM::MagicEffect::DrainSkill && + mData.mEffectID[i] != ESM::MagicEffect::FortifySkill && + mData.mEffectID[i] != ESM::MagicEffect::RestoreSkill) { mData.mSkills[i] = -1; } @@ -102,7 +120,7 @@ namespace ESM mData.mValue = 0; for (int i = 0; i < 4; ++i) { - mData.mEffectID[i] = 0; + mData.mEffectID[i] = ESM::MagicEffect::WaterBreathing; mData.mSkills[i] = 0; mData.mAttributes[i] = 0; } diff --git a/components/esm3/loadingr.hpp b/components/esm3/loadingr.hpp index 2a8748e067..289cf839a4 100644 --- a/components/esm3/loadingr.hpp +++ b/components/esm3/loadingr.hpp @@ -27,7 +27,7 @@ namespace ESM { float mWeight; int32_t mValue; - int32_t mEffectID[4]; // Effect, -1 means none + RefId mEffectID[4]; // Effect, EmptyRefId means none int32_t mSkills[4]; // SkillEnum related to effect int32_t mAttributes[4]; // Attribute related to effect }; diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index aac4981dc8..b90d985088 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -826,6 +826,8 @@ namespace ESM int MagicEffect::refIdToIndex(const RefId& effectId) { + if (effectId.empty()) + return -1; for (size_t i = 0; i < sMagicEffectIds.size(); ++i) if (sMagicEffectIds[i] == effectId) return static_cast(i); diff --git a/components/esm3/magiceffects.hpp b/components/esm3/magiceffects.hpp index 82194d0039..ab93288d92 100644 --- a/components/esm3/magiceffects.hpp +++ b/components/esm3/magiceffects.hpp @@ -12,13 +12,11 @@ namespace ESM class ESMReader; class ESMWriter; - using MagicEffectId = StringRefId; - // format 0, saved games only struct MagicEffects { // - std::map> mEffects; + std::map> mEffects; void load(ESMReader& esm); void save(ESMWriter& esm) const; @@ -26,14 +24,14 @@ namespace ESM struct SummonKey { - SummonKey(const ESM::MagicEffectId& effectId, const ESM::RefId& sourceId, int32_t index) + SummonKey(const ESM::RefId& effectId, const ESM::RefId& sourceId, int32_t index) : mEffectId(effectId) , mSourceId(sourceId) , mEffectIndex(index) { } - ESM::MagicEffectId mEffectId; + ESM::RefId mEffectId; ESM::RefId mSourceId; int32_t mEffectIndex; };