From 85c15ff6433af02c5033b5928240f9f935bee484 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Sun, 23 Nov 2025 16:39:09 -0500 Subject: [PATCH 01/14] Changed magic effects to use RefIds in loadmgef --- components/esm3/loadmgef.cpp | 702 ++++++++++++++++++++++------------- components/esm3/loadmgef.hpp | 331 ++++++++--------- 2 files changed, 606 insertions(+), 427 deletions(-) diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index fd75d071cb..07f59d2cfd 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -4,6 +4,7 @@ #include "esmwriter.hpp" #include "loadskil.hpp" +#include #include namespace ESM @@ -25,14 +26,171 @@ namespace ESM 0x1048, 0x1048, 0x1048, 0x1048 }; } + const MagicEffectId WaterBreathing("WaterBreathing"); + const MagicEffectId SwiftSwim("SwiftSwim"); + const MagicEffectId WaterWalking("WaterWalking"); + const MagicEffectId Shield("Shield"); + const MagicEffectId FireShield("FireShield"); + const MagicEffectId LightningShield("LightningShield"); + const MagicEffectId FrostShield("FrostShield"); + const MagicEffectId Burden("Burden"); + const MagicEffectId Feather("Feather"); + const MagicEffectId Jump("Jump"); + const MagicEffectId Levitate("Levitate"); + const MagicEffectId SlowFall("SlowFall"); + const MagicEffectId Lock("Lock"); + const MagicEffectId Open("Open"); + const MagicEffectId FireDamage("FireDamage"); + const MagicEffectId ShockDamage("ShockDamage"); + const MagicEffectId FrostDamage("FrostDamage"); + const MagicEffectId DrainAttribute("DrainAttribute"); + const MagicEffectId DrainHealth("DrainHealth"); + const MagicEffectId DrainMagicka("DrainMagicka"); + const MagicEffectId DrainFatigue("DrainFatigue"); + const MagicEffectId DrainSkill("DrainSkill"); + const MagicEffectId DamageAttribute("DamageAttribute"); + const MagicEffectId DamageHealth("DamageHealth"); + const MagicEffectId DamageMagicka("DamageMagicka"); + const MagicEffectId DamageFatigue("DamageFatigue"); + const MagicEffectId DamageSkill("DamageSkill"); + const MagicEffectId Poison("Poison"); + const MagicEffectId WeaknessToFire("WeaknessToFire"); + const MagicEffectId WeaknessToFrost("WeaknessToFrost"); + const MagicEffectId WeaknessToShock("WeaknessToShock"); + const MagicEffectId WeaknessToMagicka("WeaknessToMagicka"); + const MagicEffectId WeaknessToCommonDisease("WeaknessToCommonDisease"); + const MagicEffectId WeaknessToBlightDisease("WeaknessToBlightDisease"); + const MagicEffectId WeaknessToCorprusDisease("WeaknessToCorprusDisease"); + const MagicEffectId WeaknessToPoison("WeaknessToPoison"); + const MagicEffectId WeaknessToNormalWeapons("WeaknessToNormalWeapons"); + const MagicEffectId DisintegrateWeapon("DisintegrateWeapon"); + const MagicEffectId DisintegrateArmor("DisintegrateArmor"); + const MagicEffectId Invisibility("Invisibility"); + const MagicEffectId Chameleon("Chameleon"); + const MagicEffectId Light("Light"); + const MagicEffectId Sanctuary("Sanctuary"); + const MagicEffectId NightEye("NightEye"); + const MagicEffectId Charm("Charm"); + const MagicEffectId Paralyze("Paralyze"); + const MagicEffectId Silence("Silence"); + const MagicEffectId Blind("Blind"); + const MagicEffectId Sound("Sound"); + const MagicEffectId CalmHumanoid("CalmHumanoid"); + const MagicEffectId CalmCreature("CalmCreature"); + const MagicEffectId FrenzyHumanoid("FrenzyHumanoid"); + const MagicEffectId FrenzyCreature("FrenzyCreature"); + const MagicEffectId DemoralizeHumanoid("DemoralizeHumanoid"); + const MagicEffectId DemoralizeCreature("DemoralizeCreature"); + const MagicEffectId RallyHumanoid("RallyHumanoid"); + const MagicEffectId RallyCreature("RallyCreature"); + const MagicEffectId Dispel("Dispel"); + const MagicEffectId Soultrap("Soultrap"); + const MagicEffectId Telekinesis("Telekinesis"); + const MagicEffectId Mark("Mark"); + const MagicEffectId Recall("Recall"); + const MagicEffectId DivineIntervention("DivineIntervention"); + const MagicEffectId AlmsiviIntervention("AlmsiviIntervention"); + const MagicEffectId DetectAnimal("DetectAnimal"); + const MagicEffectId DetectEnchantment("DetectEnchantment"); + const MagicEffectId DetectKey("DetectKey"); + const MagicEffectId SpellAbsorption("SpellAbsorption"); + const MagicEffectId Reflect("Reflect"); + const MagicEffectId CureCommonDisease("CureCommonDisease"); + const MagicEffectId CureBlightDisease("CureBlightDisease"); + const MagicEffectId CureCorprusDisease("CureCorprusDisease"); + const MagicEffectId CurePoison("CurePoison"); + const MagicEffectId CureParalyzation("CureParalyzation"); + const MagicEffectId RestoreAttribute("RestoreAttribute"); + const MagicEffectId RestoreHealth("RestoreHealth"); + const MagicEffectId RestoreMagicka("RestoreMagicka"); + const MagicEffectId RestoreFatigue("RestoreFatigue"); + const MagicEffectId RestoreSkill("RestoreSkill"); + const MagicEffectId FortifyAttribute("FortifyAttribute"); + const MagicEffectId FortifyHealth("FortifyHealth"); + const MagicEffectId FortifyMagicka("FortifyMagicka"); + const MagicEffectId FortifyFatigue("FortifyFatigue"); + const MagicEffectId FortifySkill("FortifySkill"); + const MagicEffectId FortifyMaximumMagicka("FortifyMaximumMagicka"); + const MagicEffectId AbsorbAttribute("AbsorbAttribute"); + const MagicEffectId AbsorbHealth("AbsorbHealth"); + const MagicEffectId AbsorbMagicka("AbsorbMagicka"); + const MagicEffectId AbsorbFatigue("AbsorbFatigue"); + const MagicEffectId AbsorbSkill("AbsorbSkill"); + const MagicEffectId ResistFire("ResistFire"); + const MagicEffectId ResistFrost("ResistFrost"); + const MagicEffectId ResistShock("ResistShock"); + const MagicEffectId ResistMagicka("ResistMagicka"); + const MagicEffectId ResistCommonDisease("ResistCommonDisease"); + const MagicEffectId ResistBlightDisease("ResistBlightDisease"); + const MagicEffectId ResistCorprusDisease("ResistCorprusDisease"); + const MagicEffectId ResistPoison("ResistPoison"); + const MagicEffectId ResistNormalWeapons("ResistNormalWeapons"); + const MagicEffectId ResistParalysis("ResistParalysis"); + const MagicEffectId RemoveCurse("RemoveCurse"); + const MagicEffectId TurnUndead("TurnUndead"); + const MagicEffectId SummonScamp("SummonScamp"); + const MagicEffectId SummonClannfear("SummonClannfear"); + const MagicEffectId SummonDaedroth("SummonDaedroth"); + const MagicEffectId SummonDremora("SummonDremora"); + const MagicEffectId SummonAncestralGhost("SummonAncestralGhost"); + const MagicEffectId SummonSkeletalMinion("SummonSkeletalMinion"); + const MagicEffectId SummonBonewalker("SummonBonewalker"); + const MagicEffectId SummonGreaterBonewalker("SummonGreaterBonewalker"); + const MagicEffectId SummonBonelord("SummonBonelord"); + const MagicEffectId SummonWingedTwilight("SummonWingedTwilight"); + const MagicEffectId SummonHunger("SummonHunger"); + const MagicEffectId SummonGoldenSaint("SummonGoldenSaint"); + const MagicEffectId SummonFlameAtronach("SummonFlameAtronach"); + const MagicEffectId SummonFrostAtronach("SummonFrostAtronach"); + const MagicEffectId SummonStormAtronach("SummonStormAtronach"); + const MagicEffectId FortifyAttack("FortifyAttack"); + const MagicEffectId CommandCreature("CommandCreature"); + const MagicEffectId CommandHumanoid("CommandHumanoid"); + const MagicEffectId BoundDagger("BoundDagger"); + const MagicEffectId BoundLongsword("BoundLongsword"); + const MagicEffectId BoundMace("BoundMace"); + const MagicEffectId BoundBattleAxe("BoundBattleAxe"); + const MagicEffectId BoundSpear("BoundSpear"); + const MagicEffectId BoundLongbow("BoundLongbow"); + const MagicEffectId ExtraSpell("ExtraSpell"); + const MagicEffectId BoundCuirass("BoundCuirass"); + const MagicEffectId BoundHelm("BoundHelm"); + const MagicEffectId BoundBoots("BoundBoots"); + const MagicEffectId BoundShield("BoundShield"); + const MagicEffectId BoundGloves("BoundGloves"); + const MagicEffectId Corprus("Corprus"); + const MagicEffectId Vampirism("Vampirism"); + const MagicEffectId SummonCenturionSphere("SummonCenturionSphere"); + const MagicEffectId SunDamage("SunDamage"); + const MagicEffectId StuntedMagicka("StuntedMagicka"); + + // Tribunal only + const MagicEffectId SummonFabricant("SummonFabricant"); + + // Bloodmoon only + const MagicEffectId SummonWolf("SummonWolf"); + const MagicEffectId SummonBear("SummonBear"); + const MagicEffectId SummonBonewolf("SummonBonewolf"); + const MagicEffectId SummonCreature04("SummonCreature04"); + const MagicEffectId SummonCreature05("SummonCreature05"); + + template T> + void decompose(T&& v, const auto& f) + { + f(v.mSchool, v.mBaseCost, v.mFlags, v.mRed, v.mGreen, v.mBlue, v.mUnknown1, v.mSpeed, v.mUnknown2) + } + void MagicEffect::load(ESMReader& esm, bool& isDeleted) { isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future) mRecordFlags = esm.getRecordFlags(); - esm.getHNT(mIndex, "INDX"); + int16_t index = -1; + esm.getHNT(index, "INDX"); + if (index < 0 || index >= Length) + esm.fail("Invalid Index!"); - mId = indexToRefId(mIndex); + mId = *indexToRefId(index).getIf(); esm.getSubNameIs("MEDT"); esm.getSubHeader(); @@ -52,8 +210,8 @@ namespace ESM { // don't allow mods to change fixed flags in the legacy format mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight); - if (mIndex >= 0 && mIndex < NumberOfHardcodedFlags) - mData.mFlags |= HardcodedFlags[mIndex]; + if (index >= 0 && index < NumberOfHardcodedFlags) + mData.mFlags |= HardcodedFlags[index]; } // vanilla MW accepts the _SND subrecords before or after DESC... I hope @@ -103,7 +261,7 @@ namespace ESM } void MagicEffect::save(ESMWriter& esm, bool /*isDeleted*/) const { - esm.writeHNT("INDX", mIndex); + esm.writeHNT("INDX", refIdToIndex(mId)); esm.startSubRecord("MEDT"); esm.writeT(MagicSchool::skillRefIdToIndex(mData.mSchool)); @@ -134,104 +292,151 @@ namespace ESM namespace { - std::map makeEffectsMap() + std::map makeResistancesMap() { - std::map effects; + std::map effects; - effects[MagicEffect::Effects::DisintegrateArmor] = MagicEffect::Effects::Sanctuary; - effects[MagicEffect::Effects::DisintegrateWeapon] = MagicEffect::Effects::Sanctuary; + effects[MagicEffect::DisintegrateArmor] = MagicEffect::Sanctuary; + effects[MagicEffect::DisintegrateWeapon] = MagicEffect::Sanctuary; - for (short i = MagicEffect::Effects::DrainAttribute; i <= MagicEffect::Effects::DamageSkill; ++i) - effects[i] = MagicEffect::Effects::ResistMagicka; - for (short i = MagicEffect::Effects::AbsorbAttribute; i <= MagicEffect::Effects::AbsorbSkill; ++i) - effects[i] = MagicEffect::Effects::ResistMagicka; - for (short i = MagicEffect::Effects::WeaknessToFire; i <= MagicEffect::Effects::WeaknessToNormalWeapons; - ++i) - effects[i] = MagicEffect::Effects::ResistMagicka; + effects[MagicEffect::DrainAttribute] = MagicEffect::ResistMagicka; + effects[MagicEffect::DrainHealth] = MagicEffect::ResistMagicka; + effects[MagicEffect::DrainMagicka] = MagicEffect::ResistMagicka; + effects[MagicEffect::DrainFatigue] = MagicEffect::ResistMagicka; + effects[MagicEffect::DrainSkill] = MagicEffect::ResistMagicka; + effects[MagicEffect::DamageAttribute] = MagicEffect::ResistMagicka; + effects[MagicEffect::DamageHealth] = MagicEffect::ResistMagicka; + effects[MagicEffect::DamageMagicka] = MagicEffect::ResistMagicka; + effects[MagicEffect::DamageFatigue] = MagicEffect::ResistMagicka; + effects[MagicEffect::DamageSkill] = MagicEffect::ResistMagicka; - effects[MagicEffect::Effects::Burden] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::Charm] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::Silence] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::Blind] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::Sound] = MagicEffect::Effects::ResistMagicka; + effects[MagicEffect::AbsorbAttribute] = MagicEffect::ResistMagicka; + effects[MagicEffect::AbsorbHealth] = MagicEffect::ResistMagicka; + effects[MagicEffect::AbsorbMagicka] = MagicEffect::ResistMagicka; + effects[MagicEffect::AbsorbFatigue] = MagicEffect::ResistMagicka; + effects[MagicEffect::AbsorbSkill] = MagicEffect::ResistMagicka; - for (short i = 0; i < 2; ++i) - { - effects[MagicEffect::Effects::CalmHumanoid + i] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::FrenzyHumanoid + i] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::DemoralizeHumanoid + i] = MagicEffect::Effects::ResistMagicka; - effects[MagicEffect::Effects::RallyHumanoid + i] = MagicEffect::Effects::ResistMagicka; - } + effects[MagicEffect::WeaknessToFire] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToFrost] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToShock] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToMagicka] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToCommonDisease] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToBlightDisease] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToCorprusDisease] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToPoison] = MagicEffect::ResistMagicka; + effects[MagicEffect::WeaknessToNormalWeapons] = MagicEffect::ResistMagicka; - effects[MagicEffect::Effects::TurnUndead] = MagicEffect::Effects::ResistMagicka; + effects[MagicEffect::Burden] = MagicEffect::ResistMagicka; + effects[MagicEffect::Charm] = MagicEffect::ResistMagicka; + effects[MagicEffect::Silence] = MagicEffect::ResistMagicka; + effects[MagicEffect::Blind] = MagicEffect::ResistMagicka; + effects[MagicEffect::Sound] = MagicEffect::ResistMagicka; - effects[MagicEffect::Effects::FireDamage] = MagicEffect::Effects::ResistFire; - effects[MagicEffect::Effects::FrostDamage] = MagicEffect::Effects::ResistFrost; - effects[MagicEffect::Effects::ShockDamage] = MagicEffect::Effects::ResistShock; - effects[MagicEffect::Effects::Vampirism] = MagicEffect::Effects::ResistCommonDisease; - effects[MagicEffect::Effects::Corprus] = MagicEffect::Effects::ResistCorprusDisease; - effects[MagicEffect::Effects::Poison] = MagicEffect::Effects::ResistPoison; - effects[MagicEffect::Effects::Paralyze] = MagicEffect::Effects::ResistParalysis; + effects[MagicEffect::CalmHumanoid] = MagicEffect::ResistMagicka; + effects[MagicEffect::CalmCreature] = MagicEffect::ResistMagicka; + effects[MagicEffect::FrenzyHumanoid] = MagicEffect::ResistMagicka; + effects[MagicEffect::FrenzyCreature] = MagicEffect::ResistMagicka; + effects[MagicEffect::DemoralizeHumanoid] = MagicEffect::ResistMagicka; + effects[MagicEffect::DemoralizeCreature] = MagicEffect::ResistMagicka; + effects[MagicEffect::RallyHumanoid] = MagicEffect::ResistMagicka; + effects[MagicEffect::RallyCreature] = MagicEffect::ResistMagicka; + + effects[MagicEffect::TurnUndead] = MagicEffect::ResistMagicka; + + effects[MagicEffect::FireDamage] = MagicEffect::ResistFire; + effects[MagicEffect::FrostDamage] = MagicEffect::ResistFrost; + effects[MagicEffect::ShockDamage] = MagicEffect::ResistShock; + effects[MagicEffect::Vampirism] = MagicEffect::ResistCommonDisease; + effects[MagicEffect::Corprus] = MagicEffect::ResistCorprusDisease; + effects[MagicEffect::Poison] = MagicEffect::ResistPoison; + effects[MagicEffect::Paralyze] = MagicEffect::ResistParalysis; return effects; } } - short MagicEffect::getResistanceEffect(short effect) + RefId MagicEffect::getResistanceEffect(const MagicEffectId& effectId) { // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // - static const std::map effects = makeEffectsMap(); + static const std::map effects = makeResistancesMap(); - if (const auto it = effects.find(effect); it != effects.end()) + if (const auto it = effects.find(effectId); it != effects.end()) return it->second; - return -1; + return {}; } - short MagicEffect::getWeaknessEffect(short effect) + namespace { - static std::map effects; - if (effects.empty()) + std::map makeWeaknessesMap() { - for (short i = DrainAttribute; i <= DamageSkill; ++i) - effects[i] = WeaknessToMagicka; - for (short i = AbsorbAttribute; i <= AbsorbSkill; ++i) - effects[i] = WeaknessToMagicka; - for (short i = WeaknessToFire; i <= WeaknessToNormalWeapons; ++i) - effects[i] = WeaknessToMagicka; + static std::map effects; - effects[Burden] = WeaknessToMagicka; - effects[Charm] = WeaknessToMagicka; - effects[Silence] = WeaknessToMagicka; - effects[Blind] = WeaknessToMagicka; - effects[Sound] = WeaknessToMagicka; + effects[MagicEffect::DrainAttribute] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DrainHealth] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DrainMagicka] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DrainFatigue] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DrainSkill] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DamageAttribute] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DamageHealth] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DamageMagicka] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DamageFatigue] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DamageSkill] = MagicEffect::WeaknessToMagicka; - for (short i = 0; i < 2; ++i) - { - effects[CalmHumanoid + i] = WeaknessToMagicka; - effects[FrenzyHumanoid + i] = WeaknessToMagicka; - effects[DemoralizeHumanoid + i] = WeaknessToMagicka; - effects[RallyHumanoid + i] = WeaknessToMagicka; - } + effects[MagicEffect::AbsorbAttribute] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::AbsorbHealth] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::AbsorbMagicka] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::AbsorbFatigue] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::AbsorbSkill] = MagicEffect::WeaknessToMagicka; - effects[TurnUndead] = WeaknessToMagicka; + effects[MagicEffect::WeaknessToFire] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToFrost] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToShock] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToMagicka] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToCommonDisease] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToBlightDisease] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToCorprusDisease] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToPoison] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::WeaknessToNormalWeapons] = MagicEffect::WeaknessToMagicka; - effects[FireDamage] = WeaknessToFire; - effects[FrostDamage] = WeaknessToFrost; - effects[ShockDamage] = WeaknessToShock; - effects[Vampirism] = WeaknessToCommonDisease; - effects[Corprus] = WeaknessToCorprusDisease; - effects[Poison] = WeaknessToPoison; + effects[MagicEffect::Burden] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::Charm] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::Silence] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::Blind] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::Sound] = MagicEffect::WeaknessToMagicka; - effects[Paralyze] = -1; + effects[MagicEffect::CalmHumanoid] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::CalmCreature] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::FrenzyHumanoid] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::FrenzyCreature] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DemoralizeHumanoid] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::DemoralizeCreature] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::RallyHumanoid] = MagicEffect::WeaknessToMagicka; + effects[MagicEffect::RallyCreature] = MagicEffect::WeaknessToMagicka; + + effects[MagicEffect::TurnUndead] = MagicEffect::WeaknessToMagicka; + + effects[MagicEffect::FireDamage] = MagicEffect::WeaknessToFire; + effects[MagicEffect::FrostDamage] = MagicEffect::WeaknessToFrost; + effects[MagicEffect::ShockDamage] = MagicEffect::WeaknessToShock; + effects[MagicEffect::Vampirism] = MagicEffect::WeaknessToCommonDisease; + effects[MagicEffect::Corprus] = MagicEffect::WeaknessToCorprusDisease; + effects[MagicEffect::Poison] = MagicEffect::WeaknessToPoison; + + return effects; } + } - if (effects.find(effect) != effects.end()) - return effects[effect]; - else - return -1; + RefId MagicEffect::getWeaknessEffect(const MagicEffectId& effectId) + { + static const std::map effects = makeWeaknessesMap(); + + if (const auto it = effects.find(effectId); it != effects.end()) + return it->second; + + return {}; } // Map effect ID to GMST name @@ -386,173 +591,170 @@ namespace ESM "sEffectSummonCreature05", }; - // Map effect ID to identifying name - const std::array MagicEffect::sIndexNames = { - "WaterBreathing", - "SwiftSwim", - "WaterWalking", - "Shield", - "FireShield", - "LightningShield", - "FrostShield", - "Burden", - "Feather", - "Jump", - "Levitate", - "SlowFall", - "Lock", - "Open", - "FireDamage", - "ShockDamage", - "FrostDamage", - "DrainAttribute", - "DrainHealth", - "DrainMagicka", - "DrainFatigue", - "DrainSkill", - "DamageAttribute", - "DamageHealth", - "DamageMagicka", - "DamageFatigue", - "DamageSkill", - "Poison", - "WeaknessToFire", - "WeaknessToFrost", - "WeaknessToShock", - "WeaknessToMagicka", - "WeaknessToCommonDisease", - "WeaknessToBlightDisease", - "WeaknessToCorprusDisease", - "WeaknessToPoison", - "WeaknessToNormalWeapons", - "DisintegrateWeapon", - "DisintegrateArmor", - "Invisibility", - "Chameleon", - "Light", - "Sanctuary", - "NightEye", - "Charm", - "Paralyze", - "Silence", - "Blind", - "Sound", - "CalmHumanoid", - "CalmCreature", - "FrenzyHumanoid", - "FrenzyCreature", - "DemoralizeHumanoid", - "DemoralizeCreature", - "RallyHumanoid", - "RallyCreature", - "Dispel", - "Soultrap", - "Telekinesis", - "Mark", - "Recall", - "DivineIntervention", - "AlmsiviIntervention", - "DetectAnimal", - "DetectEnchantment", - "DetectKey", - "SpellAbsorption", - "Reflect", - "CureCommonDisease", - "CureBlightDisease", - "CureCorprusDisease", - "CurePoison", - "CureParalyzation", - "RestoreAttribute", - "RestoreHealth", - "RestoreMagicka", - "RestoreFatigue", - "RestoreSkill", - "FortifyAttribute", - "FortifyHealth", - "FortifyMagicka", - "FortifyFatigue", - "FortifySkill", - "FortifyMaximumMagicka", - "AbsorbAttribute", - "AbsorbHealth", - "AbsorbMagicka", - "AbsorbFatigue", - "AbsorbSkill", - "ResistFire", - "ResistFrost", - "ResistShock", - "ResistMagicka", - "ResistCommonDisease", - "ResistBlightDisease", - "ResistCorprusDisease", - "ResistPoison", - "ResistNormalWeapons", - "ResistParalysis", - "RemoveCurse", - "TurnUndead", - "SummonScamp", - "SummonClannfear", - "SummonDaedroth", - "SummonDremora", - "SummonAncestralGhost", - "SummonSkeletalMinion", - "SummonBonewalker", - "SummonGreaterBonewalker", - "SummonBonelord", - "SummonWingedTwilight", - "SummonHunger", - "SummonGoldenSaint", - "SummonFlameAtronach", - "SummonFrostAtronach", - "SummonStormAtronach", - "FortifyAttack", - "CommandCreature", - "CommandHumanoid", - "BoundDagger", - "BoundLongsword", - "BoundMace", - "BoundBattleAxe", - "BoundSpear", - "BoundLongbow", - "ExtraSpell", - "BoundCuirass", - "BoundHelm", - "BoundBoots", - "BoundShield", - "BoundGloves", - "Corprus", - "Vampirism", - "SummonCenturionSphere", - "SunDamage", - "StuntedMagicka", + static const std::array magicEffectIds + { + MagicEffect::WaterBreathing, + MagicEffect::SwiftSwim, + MagicEffect::WaterWalking, + MagicEffect::Shield, + MagicEffect::FireShield, + MagicEffect::LightningShield, + MagicEffect::FrostShield, + MagicEffect::Burden, + MagicEffect::Feather, + MagicEffect::Jump, + MagicEffect::Levitate, + MagicEffect::SlowFall, + MagicEffect::Lock, + MagicEffect::Open, + MagicEffect::FireDamage, + MagicEffect::ShockDamage, + MagicEffect::FrostDamage, + MagicEffect::DrainAttribute, + MagicEffect::DrainHealth, + MagicEffect::DrainMagicka, + MagicEffect::DrainFatigue, + MagicEffect::DrainSkill, + MagicEffect::DamageAttribute, + MagicEffect::DamageHealth, + MagicEffect::DamageMagicka, + MagicEffect::DamageFatigue, + MagicEffect::DamageSkill, + MagicEffect::Poison, + MagicEffect::WeaknessToFire, + MagicEffect::WeaknessToFrost, + MagicEffect::WeaknessToShock, + MagicEffect::WeaknessToMagicka, + MagicEffect::WeaknessToCommonDisease, + MagicEffect::WeaknessToBlightDisease, + MagicEffect::WeaknessToCorprusDisease, + MagicEffect::WeaknessToPoison, + MagicEffect::WeaknessToNormalWeapons, + MagicEffect::DisintegrateWeapon, + MagicEffect::DisintegrateArmor, + MagicEffect::Invisibility, + MagicEffect::Chameleon, + MagicEffect::Light, + MagicEffect::Sanctuary, + MagicEffect::NightEye, + MagicEffect::Charm, + MagicEffect::Paralyze, + MagicEffect::Silence, + MagicEffect::Blind, + MagicEffect::Sound, + MagicEffect::CalmHumanoid, + MagicEffect::CalmCreature, + MagicEffect::FrenzyHumanoid, + MagicEffect::FrenzyCreature, + MagicEffect::DemoralizeHumanoid, + MagicEffect::DemoralizeCreature, + MagicEffect::RallyHumanoid, + MagicEffect::RallyCreature, + MagicEffect::Dispel, + MagicEffect::Soultrap, + MagicEffect::Telekinesis, + MagicEffect::Mark, + MagicEffect::Recall, + MagicEffect::DivineIntervention, + MagicEffect::AlmsiviIntervention, + MagicEffect::DetectAnimal, + MagicEffect::DetectEnchantment, + MagicEffect::DetectKey, + MagicEffect::SpellAbsorption, + MagicEffect::Reflect, + MagicEffect::CureCommonDisease, + MagicEffect::CureBlightDisease, + MagicEffect::CureCorprusDisease, + MagicEffect::CurePoison, + MagicEffect::CureParalyzation, + MagicEffect::RestoreAttribute, + MagicEffect::RestoreHealth, + MagicEffect::RestoreMagicka, + MagicEffect::RestoreFatigue, + MagicEffect::RestoreSkill, + MagicEffect::FortifyAttribute, + MagicEffect::FortifyHealth, + MagicEffect::FortifyMagicka, + MagicEffect::FortifyFatigue, + MagicEffect::FortifySkill, + MagicEffect::FortifyMaximumMagicka, + MagicEffect::AbsorbAttribute, + MagicEffect::AbsorbHealth, + MagicEffect::AbsorbMagicka, + MagicEffect::AbsorbFatigue, + MagicEffect::AbsorbSkill, + MagicEffect::ResistFire, + MagicEffect::ResistFrost, + MagicEffect::ResistShock, + MagicEffect::ResistMagicka, + MagicEffect::ResistCommonDisease, + MagicEffect::ResistBlightDisease, + MagicEffect::ResistCorprusDisease, + MagicEffect::ResistPoison, + MagicEffect::ResistNormalWeapons, + MagicEffect::ResistParalysis, + MagicEffect::RemoveCurse, + MagicEffect::TurnUndead, + MagicEffect::SummonScamp, + MagicEffect::SummonClannfear, + MagicEffect::SummonDaedroth, + MagicEffect::SummonDremora, + MagicEffect::SummonAncestralGhost, + MagicEffect::SummonSkeletalMinion, + MagicEffect::SummonBonewalker, + MagicEffect::SummonGreaterBonewalker, + MagicEffect::SummonBonelord, + MagicEffect::SummonWingedTwilight, + MagicEffect::SummonHunger, + MagicEffect::SummonGoldenSaint, + MagicEffect::SummonFlameAtronach, + MagicEffect::SummonFrostAtronach, + MagicEffect::SummonStormAtronach, + MagicEffect::FortifyAttack, + MagicEffect::CommandCreature, + MagicEffect::CommandHumanoid, + MagicEffect::BoundDagger, + MagicEffect::BoundLongsword, + MagicEffect::BoundMace, + MagicEffect::BoundBattleAxe, + MagicEffect::BoundSpear, + MagicEffect::BoundLongbow, + MagicEffect::ExtraSpell, + MagicEffect::BoundCuirass, + MagicEffect::BoundHelm, + MagicEffect::BoundBoots, + MagicEffect::BoundShield, + MagicEffect::BoundGloves, + MagicEffect::Corprus, + MagicEffect::Vampirism, + MagicEffect::SummonCenturionSphere, + MagicEffect::SunDamage, + MagicEffect::StuntedMagicka, // tribunal - "SummonFabricant", + MagicEffect::SummonFabricant, // bloodmoon - "SummonWolf", - "SummonBear", - "SummonBonewolf", - "SummonCreature04", - "SummonCreature05", - }; + MagicEffect::SummonWolf, + MagicEffect::SummonBear, + MagicEffect::SummonBonewolf, + MagicEffect::SummonCreature04, + MagicEffect::SummonCreature05, + }; template - static std::map initStringToIntMap(const Collection& strings) + static std::map initStringToIntMap(const Collection& strings) { - std::map map; + std::map map; for (size_t i = 0; i < strings.size(); i++) - map[strings[i]] = static_cast(i); + map[strings[i]] = static_cast(i); return map; } - const std::map MagicEffect::sGmstEffectIdToIndexMap + const std::map MagicEffect::sGmstEffectIdToIndexMap = initStringToIntMap(MagicEffect::sGmstEffectIds); - const std::map MagicEffect::sIndexNameToIndexMap - = initStringToIntMap(MagicEffect::sIndexNames); - class FindSecond { std::string_view mName; @@ -573,16 +775,17 @@ namespace ESM MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { + short index = refIdToIndex(mId); if (mData.mFlags & NoMagnitude) return MDT_None; - if (mIndex == 84) + if (index == 84) return MDT_TimesInt; - if (mIndex == 59 || (mIndex >= 64 && mIndex <= 66)) + if (index == 59 || (index >= 64 && index <= 66)) return MDT_Feet; - if (mIndex == 118 || mIndex == 119) + if (index == 118 || index == 119) return MDT_Level; - if ((mIndex >= 28 && mIndex <= 36) || (mIndex >= 90 && mIndex <= 99) || mIndex == 40 || mIndex == 47 - || mIndex == 57 || mIndex == 68) + if ((index >= 28 && index <= 36) || (index >= 90 && index <= 99) || index == 40 || index == 47 + || index == 57 || index == 68) return MDT_Percentage; return MDT_Points; @@ -620,7 +823,7 @@ namespace ESM return color; } - const std::string& MagicEffect::indexToGmstString(int effectID) + const std::string& MagicEffect::indexToGmstString(short effectID) { if (effectID < 0 || static_cast(effectID) >= sGmstEffectIds.size()) throw std::runtime_error(std::string("Unimplemented effect ID ") + std::to_string(effectID)); @@ -628,24 +831,7 @@ namespace ESM return sGmstEffectIds[effectID]; } - std::string_view MagicEffect::indexToName(int effectID) - { - if (effectID < 0 || static_cast(effectID) >= sIndexNames.size()) - throw std::runtime_error(std::string("Unimplemented effect ID ") + std::to_string(effectID)); - - return sIndexNames[effectID]; - } - - int MagicEffect::indexNameToIndex(std::string_view effect) - { - auto name = sIndexNameToIndexMap.find(effect); - if (name == sIndexNameToIndexMap.end()) - return -1; - - return name->second; - } - - int MagicEffect::effectGmstIdToIndex(std::string_view gmstId) + short MagicEffect::effectGmstIdToIndex(std::string_view gmstId) { auto name = sGmstEffectIdToIndexMap.find(gmstId); if (name == sGmstEffectIdToIndexMap.end()) @@ -654,10 +840,18 @@ namespace ESM return name->second; } - RefId MagicEffect::indexToRefId(int index) + RefId MagicEffect::indexToRefId(short index) { - if (index == -1) - return RefId(); - return RefId::index(sRecordId, static_cast(index)); + if (index < 0 || index >= Length) + return {}; + return magicEffectIds[index]; + } + + short MagicEffect::refIdToIndex(const RefId& effectId) + { + for (size_t i = 0; i < magicEffectIds.size(); ++i) + if (magicEffectIds[i] == effectId) + return static_cast(i); + return -1; } } diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index 9f028e4bdc..1962de217f 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -18,6 +18,8 @@ namespace ESM class ESMReader; class ESMWriter; + using MagicEffectId = StringRefId; + struct MagicEffect { constexpr static RecNameInts sRecordId = REC_MGEF; @@ -26,7 +28,7 @@ namespace ESM static std::string_view getRecordType() { return "MagicEffect"; } uint32_t mRecordFlags; - RefId mId; + MagicEffectId mId; enum Flags { @@ -83,10 +85,10 @@ namespace ESM float mUnknown2; // Called "Size Cap" in CS }; // 36 bytes - /// Returns the effect that provides resistance against \a effect (or -1 if there's none) - static short getResistanceEffect(short effect); - /// Returns the effect that induces weakness against \a effect (or -1 if there's none) - static short getWeaknessEffect(short effect); + /// Returns the effect that provides resistance against \a effect (or empty RefId if there's none) + static RefId getResistanceEffect(const MagicEffectId& effect); + /// Returns the effect that induces weakness against \a effect (or empty RefId if there's none) + static RefId getWeaknessEffect(const MagicEffectId& effect); MagnitudeDisplayType getMagnitudeDisplayType() const; @@ -98,17 +100,6 @@ namespace ESM ESM::RefId mCastSound, mBoltSound, mHitSound, mAreaSound; // Sounds std::string mDescription; - // Index of this magical effect. Corresponds to one of the - // hard-coded effects in the original engine: - // 0-136 in Morrowind - // 137 in Tribunal - // 138-140 in Bloodmoon (also changes 64?) - // 141-142 are summon effects introduced in bloodmoon, but not used - // there. They can be redefined in mods by setting the name in GMST - // sEffectSummonCreature04/05 creature id in - // sMagicCreature04ID/05ID. - int32_t mIndex; - void load(ESMReader& esm, bool& isDeleted); void save(ESMWriter& esm, bool isDeleted = false) const; @@ -117,170 +108,164 @@ namespace ESM osg::Vec4f getColor() const; - enum Effects : short - { - WaterBreathing = 0, - SwiftSwim = 1, - WaterWalking = 2, - Shield = 3, - FireShield = 4, - LightningShield = 5, - FrostShield = 6, - Burden = 7, - Feather = 8, - Jump = 9, - Levitate = 10, - SlowFall = 11, - Lock = 12, - Open = 13, - FireDamage = 14, - ShockDamage = 15, - FrostDamage = 16, - DrainAttribute = 17, - DrainHealth = 18, - DrainMagicka = 19, - DrainFatigue = 20, - DrainSkill = 21, - DamageAttribute = 22, - DamageHealth = 23, - DamageMagicka = 24, - DamageFatigue = 25, - DamageSkill = 26, - Poison = 27, - WeaknessToFire = 28, - WeaknessToFrost = 29, - WeaknessToShock = 30, - WeaknessToMagicka = 31, - WeaknessToCommonDisease = 32, - WeaknessToBlightDisease = 33, - WeaknessToCorprusDisease = 34, - WeaknessToPoison = 35, - WeaknessToNormalWeapons = 36, - DisintegrateWeapon = 37, - DisintegrateArmor = 38, - Invisibility = 39, - Chameleon = 40, - Light = 41, - Sanctuary = 42, - NightEye = 43, - Charm = 44, - Paralyze = 45, - Silence = 46, - Blind = 47, - Sound = 48, - CalmHumanoid = 49, - CalmCreature = 50, - FrenzyHumanoid = 51, - FrenzyCreature = 52, - DemoralizeHumanoid = 53, - DemoralizeCreature = 54, - RallyHumanoid = 55, - RallyCreature = 56, - Dispel = 57, - Soultrap = 58, - Telekinesis = 59, - Mark = 60, - Recall = 61, - DivineIntervention = 62, - AlmsiviIntervention = 63, - DetectAnimal = 64, - DetectEnchantment = 65, - DetectKey = 66, - SpellAbsorption = 67, - Reflect = 68, - CureCommonDisease = 69, - CureBlightDisease = 70, - CureCorprusDisease = 71, - CurePoison = 72, - CureParalyzation = 73, - RestoreAttribute = 74, - RestoreHealth = 75, - RestoreMagicka = 76, - RestoreFatigue = 77, - RestoreSkill = 78, - FortifyAttribute = 79, - FortifyHealth = 80, - FortifyMagicka = 81, - FortifyFatigue = 82, - FortifySkill = 83, - FortifyMaximumMagicka = 84, - AbsorbAttribute = 85, - AbsorbHealth = 86, - AbsorbMagicka = 87, - AbsorbFatigue = 88, - AbsorbSkill = 89, - ResistFire = 90, - ResistFrost = 91, - ResistShock = 92, - ResistMagicka = 93, - ResistCommonDisease = 94, - ResistBlightDisease = 95, - ResistCorprusDisease = 96, - ResistPoison = 97, - ResistNormalWeapons = 98, - ResistParalysis = 99, - RemoveCurse = 100, - TurnUndead = 101, - SummonScamp = 102, - SummonClannfear = 103, - SummonDaedroth = 104, - SummonDremora = 105, - SummonAncestralGhost = 106, - SummonSkeletalMinion = 107, - SummonBonewalker = 108, - SummonGreaterBonewalker = 109, - SummonBonelord = 110, - SummonWingedTwilight = 111, - SummonHunger = 112, - SummonGoldenSaint = 113, - SummonFlameAtronach = 114, - SummonFrostAtronach = 115, - SummonStormAtronach = 116, - FortifyAttack = 117, - CommandCreature = 118, - CommandHumanoid = 119, - BoundDagger = 120, - BoundLongsword = 121, - BoundMace = 122, - BoundBattleAxe = 123, - BoundSpear = 124, - BoundLongbow = 125, - ExtraSpell = 126, - BoundCuirass = 127, - BoundHelm = 128, - BoundBoots = 129, - BoundShield = 130, - BoundGloves = 131, - Corprus = 132, - Vampirism = 133, - SummonCenturionSphere = 134, - SunDamage = 135, - StuntedMagicka = 136, + static const MagicEffectId WaterBreathing; + static const MagicEffectId SwiftSwim; + static const MagicEffectId WaterWalking; + static const MagicEffectId Shield; + static const MagicEffectId FireShield; + static const MagicEffectId LightningShield; + static const MagicEffectId FrostShield; + static const MagicEffectId Burden; + static const MagicEffectId Feather; + static const MagicEffectId Jump; + static const MagicEffectId Levitate; + static const MagicEffectId SlowFall; + static const MagicEffectId Lock; + static const MagicEffectId Open; + static const MagicEffectId FireDamage; + static const MagicEffectId ShockDamage; + static const MagicEffectId FrostDamage; + static const MagicEffectId DrainAttribute; + static const MagicEffectId DrainHealth; + static const MagicEffectId DrainMagicka; + static const MagicEffectId DrainFatigue; + static const MagicEffectId DrainSkill; + static const MagicEffectId DamageAttribute; + static const MagicEffectId DamageHealth; + static const MagicEffectId DamageMagicka; + static const MagicEffectId DamageFatigue; + static const MagicEffectId DamageSkill; + static const MagicEffectId Poison; + static const MagicEffectId WeaknessToFire; + static const MagicEffectId WeaknessToFrost; + static const MagicEffectId WeaknessToShock; + static const MagicEffectId WeaknessToMagicka; + static const MagicEffectId WeaknessToCommonDisease; + static const MagicEffectId WeaknessToBlightDisease; + static const MagicEffectId WeaknessToCorprusDisease; + static const MagicEffectId WeaknessToPoison; + static const MagicEffectId WeaknessToNormalWeapons; + static const MagicEffectId DisintegrateWeapon; + static const MagicEffectId DisintegrateArmor; + static const MagicEffectId Invisibility; + static const MagicEffectId Chameleon; + static const MagicEffectId Light; + static const MagicEffectId Sanctuary; + static const MagicEffectId NightEye; + static const MagicEffectId Charm; + static const MagicEffectId Paralyze; + static const MagicEffectId Silence; + static const MagicEffectId Blind; + static const MagicEffectId Sound; + static const MagicEffectId CalmHumanoid; + static const MagicEffectId CalmCreature; + static const MagicEffectId FrenzyHumanoid; + static const MagicEffectId FrenzyCreature; + static const MagicEffectId DemoralizeHumanoid; + static const MagicEffectId DemoralizeCreature; + static const MagicEffectId RallyHumanoid; + static const MagicEffectId RallyCreature; + static const MagicEffectId Dispel; + static const MagicEffectId Soultrap; + static const MagicEffectId Telekinesis; + static const MagicEffectId Mark; + static const MagicEffectId Recall; + static const MagicEffectId DivineIntervention; + static const MagicEffectId AlmsiviIntervention; + static const MagicEffectId DetectAnimal; + static const MagicEffectId DetectEnchantment; + static const MagicEffectId DetectKey; + static const MagicEffectId SpellAbsorption; + static const MagicEffectId Reflect; + static const MagicEffectId CureCommonDisease; + static const MagicEffectId CureBlightDisease; + static const MagicEffectId CureCorprusDisease; + static const MagicEffectId CurePoison; + static const MagicEffectId CureParalyzation; + static const MagicEffectId RestoreAttribute; + static const MagicEffectId RestoreHealth; + static const MagicEffectId RestoreMagicka; + static const MagicEffectId RestoreFatigue; + static const MagicEffectId RestoreSkill; + static const MagicEffectId FortifyAttribute; + static const MagicEffectId FortifyHealth; + static const MagicEffectId FortifyMagicka; + static const MagicEffectId FortifyFatigue; + static const MagicEffectId FortifySkill; + static const MagicEffectId FortifyMaximumMagicka; + static const MagicEffectId AbsorbAttribute; + static const MagicEffectId AbsorbHealth; + static const MagicEffectId AbsorbMagicka; + static const MagicEffectId AbsorbFatigue; + static const MagicEffectId AbsorbSkill; + static const MagicEffectId ResistFire; + static const MagicEffectId ResistFrost; + static const MagicEffectId ResistShock; + static const MagicEffectId ResistMagicka; + static const MagicEffectId ResistCommonDisease; + static const MagicEffectId ResistBlightDisease; + static const MagicEffectId ResistCorprusDisease; + static const MagicEffectId ResistPoison; + static const MagicEffectId ResistNormalWeapons; + static const MagicEffectId ResistParalysis; + static const MagicEffectId RemoveCurse; + static const MagicEffectId TurnUndead; + static const MagicEffectId SummonScamp; + static const MagicEffectId SummonClannfear; + static const MagicEffectId SummonDaedroth; + static const MagicEffectId SummonDremora; + static const MagicEffectId SummonAncestralGhost; + static const MagicEffectId SummonSkeletalMinion; + static const MagicEffectId SummonBonewalker; + static const MagicEffectId SummonGreaterBonewalker; + static const MagicEffectId SummonBonelord; + static const MagicEffectId SummonWingedTwilight; + static const MagicEffectId SummonHunger; + static const MagicEffectId SummonGoldenSaint; + static const MagicEffectId SummonFlameAtronach; + static const MagicEffectId SummonFrostAtronach; + static const MagicEffectId SummonStormAtronach; + static const MagicEffectId FortifyAttack; + static const MagicEffectId CommandCreature; + static const MagicEffectId CommandHumanoid; + static const MagicEffectId BoundDagger; + static const MagicEffectId BoundLongsword; + static const MagicEffectId BoundMace; + static const MagicEffectId BoundBattleAxe; + static const MagicEffectId BoundSpear; + static const MagicEffectId BoundLongbow; + static const MagicEffectId ExtraSpell; + static const MagicEffectId BoundCuirass; + static const MagicEffectId BoundHelm; + static const MagicEffectId BoundBoots; + static const MagicEffectId BoundShield; + static const MagicEffectId BoundGloves; + static const MagicEffectId Corprus; + static const MagicEffectId Vampirism; + static const MagicEffectId SummonCenturionSphere; + static const MagicEffectId SunDamage; + static const MagicEffectId StuntedMagicka; - // Tribunal only - SummonFabricant = 137, + // Tribunal only + static const MagicEffectId SummonFabricant; - // Bloodmoon only - SummonWolf = 138, - SummonBear = 139, - SummonBonewolf = 140, - SummonCreature04 = 141, - SummonCreature05 = 142, + // Bloodmoon only + static const MagicEffectId SummonWolf; + static const MagicEffectId SummonBear; + static const MagicEffectId SummonBonewolf; + static const MagicEffectId SummonCreature04; + static const MagicEffectId SummonCreature05; - Length - }; + static constexpr short Length = 143; static const std::array sGmstEffectIds; - static const std::array sIndexNames; - static const std::map sGmstEffectIdToIndexMap; - static const std::map sIndexNameToIndexMap; + static const std::map sGmstEffectIdToIndexMap; - static const std::string& indexToGmstString(int effectID); - static std::string_view indexToName(int effectID); - static int indexNameToIndex(std::string_view effect); - static int effectGmstIdToIndex(std::string_view gmstId); + static const std::string& indexToGmstString(short effectID); + static short effectGmstIdToIndex(std::string_view gmstId); - static RefId indexToRefId(int index); + static RefId indexToRefId(short index); + static short refIdToIndex(const RefId& effectId); }; } #endif From bf5e7ba3f6826c38abe8c573c52067756d66441e Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Tue, 25 Nov 2025 10:22:40 -0500 Subject: [PATCH 02/14] Changed ActiveSpells and ENAM struct to use only RefIds. Serialization still uses indexes for backward compatibility. --- apps/esmtool/record.cpp | 6 +- apps/opencs/model/world/collection.hpp | 5 +- apps/opencs/model/world/columnimp.cpp | 2 +- apps/openmw/mwmechanics/magiceffects.hpp | 4 +- components/esm3/activespells.cpp | 99 +++++++++++++----------- components/esm3/activespells.hpp | 4 +- components/esm3/effectlist.cpp | 11 ++- components/esm3/effectlist.hpp | 6 +- components/esm3/loadmgef.cpp | 84 ++++++++++---------- components/esm3/loadmgef.hpp | 18 +++-- 10 files changed, 137 insertions(+), 102 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 48d18a235a..444d255647 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -153,7 +153,7 @@ namespace int i = 0; for (const ESM::IndexedENAMstruct& effect : effects.mList) { - std::cout << " Effect[" << i << "]: " << magicEffectLabel(effect.mData.mEffectID) << " (" + std::cout << " Effect[" << i << "]: " << magicEffectLabel(ESM::MagicEffect::refIdToIndex(effect.mData.mEffectID)) << " (" << effect.mData.mEffectID << ")" << std::endl; if (effect.mData.mSkill != -1) std::cout << " Skill: " << skillLabel(effect.mData.mSkill) << " (" << (int)effect.mData.mSkill << ")" @@ -973,7 +973,9 @@ namespace EsmTool template <> void Record::print() { - std::cout << " Index: " << magicEffectLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; + std::cout << " Id: " << mData.mId << std::endl; + std::cout << " Index: " << magicEffectLabel(ESM::MagicEffect::refIdToIndex(mData.mId)) << " (" + << ESM::MagicEffect::refIdToIndex(mData.mId) << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Flags: " << magicEffectFlags(mData.mData.mFlags) << std::endl; diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9d3658cdf0..1350666cfc 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -80,8 +80,9 @@ namespace CSMWorld inline void setRecordId(const ESM::RefId& id, ESM::MagicEffect& record) { - int index = ESM::MagicEffect::indexNameToIndex(id.getRefIdString()); - record.mId = ESM::RefId::index(ESM::REC_MGEF, static_cast(index)); + if (const auto* magicEffectId = id.getIf()) + record.mId = *magicEffectId; + throw std::runtime_error("Invalid magic effect id: " + id.toDebugString()); } inline void setRecordId(const ESM::RefId& id, ESM::Skill& record) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index baf2e5d8e3..7080f2f7be 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -30,7 +30,7 @@ namespace CSMWorld switch (value.getRecordType()) { case ESM::REC_MGEF: - return std::string(ESM::MagicEffect::sIndexNames[value.getValue()]); + return std::string(ESM::MagicEffect::indexToName(value.getValue())); default: break; } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 80b858644e..f05ddc3720 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -21,12 +21,12 @@ namespace MWMechanics { struct EffectKey { - int mId; + ESM::RefId mId; ESM::RefId mArg; // skill or ability EffectKey(); - EffectKey(int id, ESM::RefId arg = {}) + EffectKey(ESM::RefId id, ESM::RefId arg = {}) : mId(id) , mArg(arg) { diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index ea6c4fe8ea..55212da1c6 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -13,60 +13,72 @@ namespace ESM { namespace { - bool isSummon(int effectId) + bool isSummon(const RefId& effectId) { - switch (effectId) + static const std::array summonEffects { - case MagicEffect::SummonScamp: - case MagicEffect::SummonClannfear: - case MagicEffect::SummonDaedroth: - case MagicEffect::SummonDremora: - case MagicEffect::SummonAncestralGhost: - case MagicEffect::SummonSkeletalMinion: - case MagicEffect::SummonBonewalker: - case MagicEffect::SummonGreaterBonewalker: - case MagicEffect::SummonBonelord: - case MagicEffect::SummonWingedTwilight: - case MagicEffect::SummonHunger: - case MagicEffect::SummonGoldenSaint: - case MagicEffect::SummonFlameAtronach: - case MagicEffect::SummonFrostAtronach: - case MagicEffect::SummonStormAtronach: - case MagicEffect::SummonCenturionSphere: - case MagicEffect::SummonFabricant: - case MagicEffect::SummonWolf: - case MagicEffect::SummonBear: - case MagicEffect::SummonBonewolf: - case MagicEffect::SummonCreature04: - case MagicEffect::SummonCreature05: + MagicEffect::SummonScamp, + MagicEffect::SummonClannfear, + MagicEffect::SummonDaedroth, + MagicEffect::SummonDremora, + MagicEffect::SummonAncestralGhost, + MagicEffect::SummonSkeletalMinion, + MagicEffect::SummonBonewalker, + MagicEffect::SummonGreaterBonewalker, + MagicEffect::SummonBonelord, + MagicEffect::SummonWingedTwilight, + MagicEffect::SummonHunger, + MagicEffect::SummonGoldenSaint, + MagicEffect::SummonFlameAtronach, + MagicEffect::SummonFrostAtronach, + MagicEffect::SummonStormAtronach, + MagicEffect::SummonCenturionSphere, + MagicEffect::SummonFabricant, + MagicEffect::SummonWolf, + MagicEffect::SummonBear, + MagicEffect::SummonBonewolf, + MagicEffect::SummonCreature04, + MagicEffect::SummonCreature05, + }; + if (effectId.empty()) + return false; + for (size_t i = 0; i < summonEffects.size(); ++i) + if (summonEffects[i] == effectId) return true; - } return false; } - bool affectsAttribute(int effectId) + bool affectsAttribute(const RefId& effectId) { - switch (effectId) + static const std::array affectsAttributeEffects { - case MagicEffect::DrainAttribute: - case MagicEffect::DamageAttribute: - case MagicEffect::RestoreAttribute: - case MagicEffect::FortifyAttribute: - case MagicEffect::AbsorbAttribute: + MagicEffect::DrainAttribute, + MagicEffect::DamageAttribute, + MagicEffect::RestoreAttribute, + MagicEffect::FortifyAttribute, + MagicEffect::AbsorbAttribute, + }; + if (effectId.empty()) + return false; + for (size_t i = 0; i < affectsAttributeEffects.size(); ++i) + if (affectsAttributeEffects[i] == effectId) return true; - } return false; } - bool affectsSkill(int effectId) + bool affectsSkill(const RefId& effectId) { - switch (effectId) + static const std::array affectsSkillEffects { - case MagicEffect::DrainSkill: - case MagicEffect::DamageSkill: - case MagicEffect::RestoreSkill: - case MagicEffect::FortifySkill: - case MagicEffect::AbsorbSkill: + MagicEffect::DrainSkill, + MagicEffect::DamageSkill, + MagicEffect::RestoreSkill, + MagicEffect::FortifySkill, + MagicEffect::AbsorbSkill, + }; + if (effectId.empty()) + return false; + for (size_t i = 0; i < affectsSkillEffects.size(); ++i) + if (affectsSkillEffects[i] == effectId) return true; - } return false; } @@ -105,7 +117,7 @@ namespace ESM esm.writeHNT("MAGN", effect.mMinMagnitude); esm.writeHNT("MAGN", effect.mMaxMagnitude); esm.writeHNT("DURA", effect.mDuration); - esm.writeHNT("EIND", effect.mEffectIndex); + esm.writeHNT("EIND", static_cast(-1)); esm.writeHNT("LEFT", effect.mTimeLeft); esm.writeHNT("FLAG", effect.mFlags); } @@ -207,8 +219,7 @@ namespace ESM esm.getHNT(effect.mMaxMagnitude, "MAGN"); } esm.getHNT(effect.mDuration, "DURA"); - effect.mEffectIndex = -1; - esm.getHNOT(effect.mEffectIndex, "EIND"); + esm.getHNOT(effectIdx, "EIND"); if (format <= MaxOldTimeLeftFormatVersion) effect.mTimeLeft = effect.mDuration; else diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index 0db1b0e61b..0275535593 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -7,7 +7,6 @@ #include "timestamp.hpp" #include -#include #include namespace ESM @@ -31,14 +30,13 @@ namespace ESM Flag_Invalid = 1 << 5 }; - int32_t mEffectId; + RefId mEffectId; float mMagnitude; float mMinMagnitude; float mMaxMagnitude; std::variant mArg; // skill, attribute, or summon float mDuration; float mTimeLeft; - int32_t mEffectIndex; int32_t mFlags; RefId getSkillOrAttribute() const; diff --git a/components/esm3/effectlist.cpp b/components/esm3/effectlist.cpp index 5a1fa91f28..221abb67f9 100644 --- a/components/esm3/effectlist.cpp +++ b/components/esm3/effectlist.cpp @@ -3,14 +3,23 @@ #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.mEffectID, v.mSkill, v.mAttribute, v.mRange, v.mArea, v.mDuration, v.mMagnMin, v.mMagnMax); + int16_t ioEffectID = static_cast(ESM::MagicEffect::refIdToIndex(v.mEffectID)); + f(ioEffectID, v.mSkill, v.mAttribute, v.mRange, v.mArea, v.mDuration, v.mMagnMin, v.mMagnMax); + if constexpr (loading) + { + v.mEffectID = ESM::MagicEffect::indexToRefId(ioEffectID); + } } void EffectList::load(ESMReader& esm) diff --git a/components/esm3/effectlist.hpp b/components/esm3/effectlist.hpp index 8eb347d6c8..7c57be074d 100644 --- a/components/esm3/effectlist.hpp +++ b/components/esm3/effectlist.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace ESM { class ESMReader; @@ -14,8 +16,8 @@ namespace ESM */ struct ENAMstruct { - // Magical effect, hard-coded ID - int16_t mEffectID; + // Magical effect, serialized to int16 + RefId mEffectID; // Which skills/attributes are affected (for restore/drain spells // etc.) diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index 07f59d2cfd..037620fbcc 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -174,18 +174,12 @@ namespace ESM const MagicEffectId SummonCreature04("SummonCreature04"); const MagicEffectId SummonCreature05("SummonCreature05"); - template T> - void decompose(T&& v, const auto& f) - { - f(v.mSchool, v.mBaseCost, v.mFlags, v.mRed, v.mGreen, v.mBlue, v.mUnknown1, v.mSpeed, v.mUnknown2) - } - void MagicEffect::load(ESMReader& esm, bool& isDeleted) { isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future) mRecordFlags = esm.getRecordFlags(); - int16_t index = -1; + int32_t index = -1; esm.getHNT(index, "INDX"); if (index < 0 || index >= Length) esm.fail("Invalid Index!"); @@ -591,7 +585,7 @@ namespace ESM "sEffectSummonCreature05", }; - static const std::array magicEffectIds + static const std::array sMagicEffectIds { MagicEffect::WaterBreathing, MagicEffect::SwiftSwim, @@ -743,39 +737,21 @@ namespace ESM }; template - static std::map initStringToIntMap(const Collection& strings) + static std::map initStringToIntMap(const Collection& strings) { - std::map map; + std::map map; for (size_t i = 0; i < strings.size(); i++) - map[strings[i]] = static_cast(i); + map[strings[i]] = static_cast(i); return map; } - const std::map MagicEffect::sGmstEffectIdToIndexMap + const std::map MagicEffect::sGmstEffectIdToIndexMap = initStringToIntMap(MagicEffect::sGmstEffectIds); - class FindSecond - { - std::string_view mName; - - public: - FindSecond(std::string_view name) - : mName(name) - { - } - - bool operator()(const std::pair& item) const - { - if (Misc::StringUtils::ciEqual(item.second, mName)) - return true; - return false; - } - }; - MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { - short index = refIdToIndex(mId); + int index = refIdToIndex(mId); if (mData.mFlags & NoMagnitude) return MDT_None; if (index == 84) @@ -823,7 +799,7 @@ namespace ESM return color; } - const std::string& MagicEffect::indexToGmstString(short effectID) + const std::string& MagicEffect::indexToGmstString(int effectID) { if (effectID < 0 || static_cast(effectID) >= sGmstEffectIds.size()) throw std::runtime_error(std::string("Unimplemented effect ID ") + std::to_string(effectID)); @@ -831,7 +807,7 @@ namespace ESM return sGmstEffectIds[effectID]; } - short MagicEffect::effectGmstIdToIndex(std::string_view gmstId) + int MagicEffect::effectGmstIdToIndex(std::string_view gmstId) { auto name = sGmstEffectIdToIndexMap.find(gmstId); if (name == sGmstEffectIdToIndexMap.end()) @@ -840,18 +816,48 @@ namespace ESM return name->second; } - RefId MagicEffect::indexToRefId(short index) + RefId MagicEffect::indexToRefId(int index) { if (index < 0 || index >= Length) return {}; - return magicEffectIds[index]; + return sMagicEffectIds[index]; } - short MagicEffect::refIdToIndex(const RefId& effectId) + int MagicEffect::refIdToIndex(const RefId& effectId) { - for (size_t i = 0; i < magicEffectIds.size(); ++i) - if (magicEffectIds[i] == effectId) - return static_cast(i); + for (size_t i = 0; i < sMagicEffectIds.size(); ++i) + if (sMagicEffectIds[i] == effectId) + return static_cast(i); + return -1; + } + + RefId MagicEffect::nameToRefId(std::string_view name) + { + for (const RefId& effect : sMagicEffectIds) + if (effect.getRefIdString() == name) + return effect; + return {}; + } + + std::string_view MagicEffect::refIdToName(const RefId& effectId) + { + if (!effectId.empty() && effectId.getIf()) + return effectId.getRefIdString(); + return {}; + } + + std::string_view MagicEffect::indexToName(int index) + { + if (index < 0 || index >= Length) + return {}; + return sMagicEffectIds[index].getValue(); + } + + int MagicEffect::indexNameToIndex(std::string_view name) + { + for (size_t i = 0; i < sMagicEffectIds.size(); ++i) + if (sMagicEffectIds[i].getValue() == name) + return static_cast(i); return -1; } } diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index 1962de217f..d5e62ee400 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -256,16 +256,22 @@ namespace ESM static const MagicEffectId SummonCreature04; static const MagicEffectId SummonCreature05; - static constexpr short Length = 143; + static constexpr int Length = 143; static const std::array sGmstEffectIds; - static const std::map sGmstEffectIdToIndexMap; + static const std::map sGmstEffectIdToIndexMap; - static const std::string& indexToGmstString(short effectID); - static short effectGmstIdToIndex(std::string_view gmstId); + static const std::string& indexToGmstString(int effectID); + static int effectGmstIdToIndex(std::string_view gmstId); - static RefId indexToRefId(short index); - static short refIdToIndex(const RefId& effectId); + static RefId indexToRefId(int index); + static int refIdToIndex(const RefId& effectId); + + static RefId nameToRefId(std::string_view name); + static std::string_view refIdToName(const RefId& effectId); + + static std::string_view indexToName(int index); + static int indexNameToIndex(std::string_view name); }; } #endif From 90fce49d17e825de561d80d460ea998f3e2b8fa0 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Sun, 30 Nov 2025 09:01:48 -0500 Subject: [PATCH 03/14] Changed MagicEffect from IndexedStore to TypedDynamicStore in ESMStore. --- apps/openmw/mwmechanics/magiceffects.cpp | 12 +++++------- apps/openmw/mwworld/esmstore.cpp | 6 +++--- apps/openmw/mwworld/store.cpp | 11 +++++++---- apps/openmw/mwworld/store.hpp | 10 +++++++--- components/esm3/loadmgef.cpp | 19 ++++++++++--------- components/esm3/loadmgef.hpp | 4 ++-- 6 files changed, 34 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index e4210ed8fc..bba9e57781 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -26,7 +26,7 @@ namespace namespace MWMechanics { EffectKey::EffectKey() - : mId(0) + : mId(ESM::RefId{}) { } @@ -48,7 +48,7 @@ namespace MWMechanics std::string EffectKey::toString() const { const auto& store = MWBase::Environment::get().getESMStore(); - const ESM::MagicEffect* magicEffect = store->get().find(mId); + const ESM::MagicEffect* magicEffect = store->get().search(mId); return getMagicEffectString( *magicEffect, store->get().search(mArg), store->get().search(mArg)); } @@ -57,11 +57,9 @@ namespace MWMechanics { if (left.mId < right.mId) return true; - - if (left.mId > right.mId) - return false; - - return left.mArg < right.mArg; + if (left.mId == right.mId) + return left.mArg < right.mArg; + return false; } bool operator==(const EffectKey& left, const EffectKey& right) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 773e4a80e8..ec55de0b91 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 " - << ESM::MagicEffect::indexToGmstString(iter->mData.mEffectID) << " effect"; + << iter->mData.mEffectID.getRefIdString() << " effect"; changed = true; } @@ -195,7 +195,7 @@ namespace iter->mData.mSkill = -1; Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId << ": dropping unexpected skill argument of " - << ESM::MagicEffect::indexToGmstString(iter->mData.mEffectID) << " effect"; + <mData.mEffectID.getRefIdString() << " effect"; changed = true; } @@ -525,7 +525,7 @@ namespace MWWorld store->setUp(); getWritable().setUp(get()); - getWritable().setUp(); + getWritable().setUp(get()); getWritable().setUp(get()); getWritable().updateLandPositions(get()); getWritable().preprocessReferences(get()); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 9d030dcea9..6158918193 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -110,9 +110,6 @@ namespace MWWorld return ptr; } - // Need to instantiate these before they're used - template class IndexedStore; - template TypedDynamicStore::TypedDynamicStore() { @@ -380,6 +377,9 @@ namespace MWWorld } } + // Need to instantiate these before they're used + template class TypedDynamicStore; + // LandTexture //========================================================================= Store::Store() = default; @@ -978,7 +978,10 @@ namespace MWWorld // Magic effect //========================================================================= - Store::Store() {} + void Store::setUp(const MWWorld::Store& settings) + { + // MGEF record is complete. No further instantiation of fields is required. + } // Attribute //========================================================================= diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 2ccf26f734..28e0dab777 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ namespace ESM { struct LandTexture; - struct MagicEffect; struct WeaponType; class ESMReader; class ESMWriter; @@ -453,10 +453,14 @@ namespace MWWorld }; template <> - class Store : public IndexedStore + class Store : public TypedDynamicStore { + using TypedDynamicStore::setUp; + public: - Store(); + Store() = default; + + void setUp(const MWWorld::Store& settings); }; template <> diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index 037620fbcc..aac4981dc8 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -799,21 +799,22 @@ namespace ESM return color; } - const std::string& MagicEffect::indexToGmstString(int effectID) + std::string_view MagicEffect::refIdToGmstString(const RefId& effectId) { - if (effectID < 0 || static_cast(effectID) >= sGmstEffectIds.size()) - throw std::runtime_error(std::string("Unimplemented effect ID ") + std::to_string(effectID)); - - return sGmstEffectIds[effectID]; + if (effectId.empty()) + return {}; + int index = refIdToIndex(effectId); + if (index < 0 || index >= Length) + return effectId.getRefIdString(); + return sGmstEffectIds[index]; } - int MagicEffect::effectGmstIdToIndex(std::string_view gmstId) + RefId MagicEffect::effectGmstIdToRefId(std::string_view gmstId) { auto name = sGmstEffectIdToIndexMap.find(gmstId); if (name == sGmstEffectIdToIndexMap.end()) - throw std::runtime_error("Unimplemented effect " + std::string(gmstId)); - - return name->second; + return {}; + return sMagicEffectIds[name->second]; } RefId MagicEffect::indexToRefId(int index) diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index d5e62ee400..e732d2872e 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -261,8 +261,8 @@ namespace ESM static const std::array sGmstEffectIds; static const std::map sGmstEffectIdToIndexMap; - static const std::string& indexToGmstString(int effectID); - static int effectGmstIdToIndex(std::string_view gmstId); + static std::string_view refIdToGmstString(const RefId& effectId); + static RefId effectGmstIdToRefId(std::string_view gmstId); static RefId indexToRefId(int index); static int refIdToIndex(const RefId& effectId); From c581ab32429cc46f325a6e0d435a09f5aa1d54e7 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Tue, 2 Dec 2025 19:09:48 -0500 Subject: [PATCH 04/14] Changed magic effects to use RefIds in activespells, magiceffects, creaturestats, and summoning. --- apps/openmw/mwmechanics/activespells.cpp | 6 +-- apps/openmw/mwmechanics/activespells.hpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 4 +- apps/openmw/mwmechanics/magiceffects.cpp | 43 ++++++-------------- apps/openmw/mwmechanics/magiceffects.hpp | 6 ++- apps/openmw/mwmechanics/summoning.cpp | 24 ++++++------ apps/openmw/mwmechanics/summoning.hpp | 11 ++++-- apps/openmw/mwscript/statsextensions.cpp | 48 +++++++++++------------ apps/openmw/mwworld/worldimp.cpp | 2 +- components/esm3/activespells.cpp | 35 ++++++----------- components/esm3/activespells.hpp | 4 +- components/esm3/creaturestats.cpp | 10 +++-- components/esm3/creaturestats.hpp | 2 +- components/esm3/effectlist.hpp | 4 +- components/esm3/magiceffects.cpp | 6 ++- components/esm3/magiceffects.hpp | 8 ++-- 17 files changed, 101 insertions(+), 116 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index d2f4e7d2d4..aae756350d 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -358,7 +358,7 @@ namespace MWMechanics if (Settings::game().mClassicCalmSpellsBehavior) { - ESM::MagicEffect::Effects effect + ESM::MagicEffectId effect = ptr.getClass().isNpc() ? ESM::MagicEffect::CalmHumanoid : ESM::MagicEffect::CalmCreature; if (creatureStats.getMagicEffects().getOrDefault(effect).getMagnitude() > 0.f) creatureStats.getAiSequence().stopCombat(); @@ -427,7 +427,7 @@ namespace MWMechanics const VFS::Path::Normalized reflectStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(reflectStatic->mModel)); animation->addEffect( - reflectStaticModel, ESM::MagicEffect::indexToName(ESM::MagicEffect::Reflect), false); + reflectStaticModel, ESM::MagicEffect::refIdToName(ESM::MagicEffect::Reflect), false); } caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(*reflected); } @@ -646,7 +646,7 @@ namespace MWMechanics purge([=](const ActiveSpellParams& params) { return params.mActiveSpellId == id; }, ptr); } - void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, int effectId, ESM::RefId effectArg) + void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, const ESM::MagicEffectId& 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 c465e28556..0824405f9a 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, int effectId, ESM::RefId effectArg = {}); + void purgeEffect(const MWWorld::Ptr& ptr, const ESM::MagicEffectId& 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/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index e52abefed6..83fd5a1f4d 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 5441fdc28f..29c2c73e94 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.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index bba9e57781..046e77ab42 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -26,7 +26,7 @@ namespace namespace MWMechanics { EffectKey::EffectKey() - : mId(ESM::RefId{}) + : mId(ESM::MagicEffect::WaterWalking) { } @@ -55,11 +55,7 @@ namespace MWMechanics bool operator<(const EffectKey& left, const EffectKey& right) { - if (left.mId < right.mId) - return true; - if (left.mId == right.mId) - return left.mArg < right.mArg; - return false; + return std::tie(left.mId, left.mArg) < std::tie(right.mId, right.mArg); } bool operator==(const EffectKey& left, const EffectKey& right) @@ -187,34 +183,21 @@ namespace MWMechanics if (targetsSkill || targetsAttribute) { - switch (effect.mIndex) - { - case ESM::MagicEffect::AbsorbAttribute: - case ESM::MagicEffect::AbsorbSkill: - spellLine = windowManager->getGameSettingString("sAbsorb", {}); - break; - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::DamageSkill: - spellLine = windowManager->getGameSettingString("sDamage", {}); - break; - case ESM::MagicEffect::DrainAttribute: - case ESM::MagicEffect::DrainSkill: - spellLine = windowManager->getGameSettingString("sDrain", {}); - break; - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::FortifySkill: - spellLine = windowManager->getGameSettingString("sFortify", {}); - break; - case ESM::MagicEffect::RestoreAttribute: - case ESM::MagicEffect::RestoreSkill: - spellLine = windowManager->getGameSettingString("sRestore", {}); - break; - } + if (effect.mId == ESM::MagicEffect::AbsorbAttribute || effect.mId == ESM::MagicEffect::AbsorbSkill) + spellLine = windowManager->getGameSettingString("sAbsorb", {}); + else if (effect.mId == ESM::MagicEffect::DamageAttribute || effect.mId == ESM::MagicEffect::DamageSkill) + spellLine = windowManager->getGameSettingString("sDamage", {}); + else if (effect.mId == ESM::MagicEffect::DrainAttribute || effect.mId == ESM::MagicEffect::DrainSkill) + spellLine = windowManager->getGameSettingString("sDrain", {}); + else if (effect.mId == ESM::MagicEffect::FortifyAttribute || effect.mId == ESM::MagicEffect::FortifySkill) + spellLine = windowManager->getGameSettingString("sFortify", {}); + else if (effect.mId == ESM::MagicEffect::RestoreAttribute || effect.mId == ESM::MagicEffect::RestoreSkill) + spellLine = windowManager->getGameSettingString("sRestore", {}); } if (spellLine.empty()) { - auto& effectIDStr = ESM::MagicEffect::indexToGmstString(effect.mIndex); + auto effectIDStr = ESM::MagicEffect::refIdToGmstString(effect.mId); spellLine = windowManager->getGameSettingString(effectIDStr, {}); } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index f05ddc3720..45de2c7beb 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -15,18 +15,20 @@ namespace ESM struct MagicEffect; struct MagicEffects; struct Skill; + + using MagicEffectId = StringRefId; } namespace MWMechanics { struct EffectKey { - ESM::RefId mId; + ESM::MagicEffectId mId; ESM::RefId mArg; // skill or ability EffectKey(); - EffectKey(ESM::RefId id, ESM::RefId arg = {}) + EffectKey(ESM::MagicEffectId id, ESM::RefId arg = {}) : mId(id) , mArg(arg) { diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 9f7b59b030..bf2da8206f 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -23,21 +23,14 @@ namespace MWMechanics { - bool isSummoningEffect(int effectId) + static const std::map& getSummonMap() { - return ((effectId >= ESM::MagicEffect::SummonScamp && effectId <= ESM::MagicEffect::SummonStormAtronach) - || (effectId == ESM::MagicEffect::SummonCenturionSphere) - || (effectId >= ESM::MagicEffect::SummonFabricant && effectId <= ESM::MagicEffect::SummonCreature05)); - } - - static const std::map& getSummonMap() - { - static std::map summonMap; + static std::map summonMap; if (summonMap.size() > 0) return summonMap; - const std::map summonMapToGameSetting{ + const std::map summonMapToGameSetting{ { ESM::MagicEffect::SummonAncestralGhost, "sMagicAncestralGhostID" }, { ESM::MagicEffect::SummonBonelord, "sMagicBonelordID" }, { ESM::MagicEffect::SummonBonewalker, "sMagicLeastBonewalkerID" }, @@ -70,7 +63,12 @@ namespace MWMechanics return summonMap; } - ESM::RefId getSummonedCreature(int effectId) + bool isSummoningEffect(const ESM::MagicEffectId& effectId) + { + return getSummonMap().contains(effectId); + } + + ESM::RefId getSummonedCreature(const ESM::MagicEffectId& effectId) { const auto& summonMap = getSummonMap(); auto it = summonMap.find(effectId); @@ -81,7 +79,7 @@ namespace MWMechanics return ESM::RefId(); } - ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner) + ESM::RefNum summonCreature(const ESM::MagicEffectId& effectId, const MWWorld::Ptr& summoner) { const ESM::RefId& creatureID = getSummonedCreature(effectId); ESM::RefNum creature; @@ -152,7 +150,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 3992f8399f..34fc68dffc 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -9,6 +9,9 @@ namespace ESM { class RefId; + class StringRefId; + + using MagicEffectId = StringRefId; } namespace MWWorld { @@ -17,13 +20,13 @@ namespace MWWorld namespace MWMechanics { - bool isSummoningEffect(int effectId); + bool isSummoningEffect(const ESM::MagicEffectId& effectId); - ESM::RefId getSummonedCreature(int effectId); + ESM::RefId getSummonedCreature(const ESM::MagicEffectId& effectId); - void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); - ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner); + ESM::RefNum summonCreature(const ESM::MagicEffectId& effectId, const MWWorld::Ptr& summoner); void updateSummons(const MWWorld::Ptr& summoner, bool cleanup); } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 195e6a5404..cc5d593592 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1569,30 +1569,30 @@ namespace MWScript interpreter.installSegment5>(Compiler::Stats::opcodeGetStatExplicit); static const MagicEffect sMagicEffects[] = { - { ESM::MagicEffect::ResistMagicka, ESM::MagicEffect::WeaknessToMagicka }, - { ESM::MagicEffect::ResistFire, ESM::MagicEffect::WeaknessToFire }, - { ESM::MagicEffect::ResistFrost, ESM::MagicEffect::WeaknessToFrost }, - { ESM::MagicEffect::ResistShock, ESM::MagicEffect::WeaknessToShock }, - { ESM::MagicEffect::ResistCommonDisease, ESM::MagicEffect::WeaknessToCommonDisease }, - { ESM::MagicEffect::ResistBlightDisease, ESM::MagicEffect::WeaknessToBlightDisease }, - { ESM::MagicEffect::ResistCorprusDisease, ESM::MagicEffect::WeaknessToCorprusDisease }, - { ESM::MagicEffect::ResistPoison, ESM::MagicEffect::WeaknessToPoison }, - { ESM::MagicEffect::ResistParalysis, -1 }, - { ESM::MagicEffect::ResistNormalWeapons, ESM::MagicEffect::WeaknessToNormalWeapons }, - { ESM::MagicEffect::WaterBreathing, -1 }, - { ESM::MagicEffect::Chameleon, -1 }, - { ESM::MagicEffect::WaterWalking, -1 }, - { ESM::MagicEffect::SwiftSwim, -1 }, - { ESM::MagicEffect::Jump, -1 }, - { ESM::MagicEffect::Levitate, -1 }, - { ESM::MagicEffect::Shield, -1 }, - { ESM::MagicEffect::Sound, -1 }, - { ESM::MagicEffect::Silence, -1 }, - { ESM::MagicEffect::Blind, -1 }, - { ESM::MagicEffect::Paralyze, -1 }, - { ESM::MagicEffect::Invisibility, -1 }, - { ESM::MagicEffect::FortifyAttack, -1 }, - { ESM::MagicEffect::Sanctuary, -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistMagicka), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToMagicka) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistFire), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToFire) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistFrost), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToFrost) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistShock), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToShock) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistCommonDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToCommonDisease) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistBlightDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToBlightDisease) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistCorprusDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToCorprusDisease) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistPoison), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToPoison) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistParalysis), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistNormalWeapons), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToNormalWeapons) }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WaterBreathing), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Chameleon), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WaterWalking), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::SwiftSwim), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Jump), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Levitate), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Shield), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Sound), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Silence), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Blind), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Paralyze), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Invisibility), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::FortifyAttack), -1 }, + { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Sanctuary), -1 }, }; for (int i = 0; i < 24; ++i) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 94bfaa3997..89ab4c97ee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3108,7 +3108,7 @@ namespace MWWorld const MWWorld::Class& cls = ptr.getClass(); if (cls.isActor()) { - std::set playing; + std::set playing; for (const auto& params : cls.getCreatureStats(ptr).getActiveSpells()) { for (const auto& effect : params.getEffects()) diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index 55212da1c6..826ac00d70 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -13,10 +13,9 @@ namespace ESM { namespace { - bool isSummon(const RefId& effectId) + bool isSummon(const ESM::RefId& effectId) { - static const std::array summonEffects - { + static const std::array summonEffects{ MagicEffect::SummonScamp, MagicEffect::SummonClannfear, MagicEffect::SummonDaedroth, @@ -40,34 +39,23 @@ namespace ESM MagicEffect::SummonCreature04, MagicEffect::SummonCreature05, }; - if (effectId.empty()) - return false; - for (size_t i = 0; i < summonEffects.size(); ++i) - if (summonEffects[i] == effectId) - return true; - return false; + return std::find(summonEffects.begin(), summonEffects.end(), effectId) != summonEffects.end(); } - bool affectsAttribute(const RefId& effectId) + bool affectsAttribute(const ESM::RefId& effectId) { - static const std::array affectsAttributeEffects - { + static const std::array affectsAttributeEffects{ MagicEffect::DrainAttribute, MagicEffect::DamageAttribute, MagicEffect::RestoreAttribute, MagicEffect::FortifyAttribute, MagicEffect::AbsorbAttribute, }; - if (effectId.empty()) - return false; - for (size_t i = 0; i < affectsAttributeEffects.size(); ++i) - if (affectsAttributeEffects[i] == effectId) - return true; - return false; + return std::find(affectsAttributeEffects.begin(), affectsAttributeEffects.end(), effectId) + != affectsAttributeEffects.end(); } - bool affectsSkill(const RefId& effectId) + bool affectsSkill(const ESM::RefId& effectId) { - static const std::array affectsSkillEffects - { + static const std::array affectsSkillEffects{ MagicEffect::DrainSkill, MagicEffect::DamageSkill, MagicEffect::RestoreSkill, @@ -117,7 +105,7 @@ namespace ESM esm.writeHNT("MAGN", effect.mMinMagnitude); esm.writeHNT("MAGN", effect.mMaxMagnitude); esm.writeHNT("DURA", effect.mDuration); - esm.writeHNT("EIND", static_cast(-1)); + esm.writeHNT("EIND", effect.mEffectIndex); esm.writeHNT("LEFT", effect.mTimeLeft); esm.writeHNT("FLAG", effect.mFlags); } @@ -219,7 +207,8 @@ namespace ESM esm.getHNT(effect.mMaxMagnitude, "MAGN"); } esm.getHNT(effect.mDuration, "DURA"); - esm.getHNOT(effectIdx, "EIND"); + effect.mEffectIndex = -1; + esm.getHNOT(effect.mEffectIndex, "EIND"); if (format <= MaxOldTimeLeftFormatVersion) effect.mTimeLeft = effect.mDuration; else diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index 0275535593..b53e77a6e6 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -7,6 +7,7 @@ #include "timestamp.hpp" #include +#include #include namespace ESM @@ -30,13 +31,14 @@ namespace ESM Flag_Invalid = 1 << 5 }; - RefId mEffectId; + MagicEffectId mEffectId; float mMagnitude; float mMinMagnitude; float mMaxMagnitude; std::variant mArg; // skill, attribute, or summon float mDuration; float mTimeLeft; + int32_t mEffectIndex; int32_t mFlags; RefId getSkillOrAttribute() const; diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index ba61a8c895..c747a33a5f 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -2,6 +2,8 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include + #include namespace ESM @@ -117,9 +119,9 @@ namespace ESM esm.getHNOT(effectIndex, "EIND"); int32_t actorId; esm.getHNT(actorId, "ACID"); - mSummonedCreatureMap[SummonKey(magicEffect, source, effectIndex)] = actorId; - mSummonedCreatures.emplace( - magicEffect, RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }); + mSummonedCreatureMap[SummonKey(ESM::MagicEffect::indexToRefId(magicEffect), source, effectIndex)] = actorId; + mSummonedCreatures.emplace(ESM::MagicEffect::indexToRefId( + magicEffect), RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }); } } else @@ -133,7 +135,7 @@ namespace ESM esm.getHNT(actor.mIndex, "ACID"); else actor = esm.getFormId(true, "ACID"); - mSummonedCreatures.emplace(magicEffect, actor); + mSummonedCreatures.emplace(ESM::MagicEffect::indexToRefId(magicEffect), actor); } } diff --git a/components/esm3/creaturestats.hpp b/components/esm3/creaturestats.hpp index cba74e065d..058c402b30 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; diff --git a/components/esm3/effectlist.hpp b/components/esm3/effectlist.hpp index 7c57be074d..b02f14dfc8 100644 --- a/components/esm3/effectlist.hpp +++ b/components/esm3/effectlist.hpp @@ -11,13 +11,15 @@ 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 - RefId mEffectID; + ESM::MagicEffectId mEffectID; // Which skills/attributes are affected (for restore/drain spells // etc.) diff --git a/components/esm3/magiceffects.cpp b/components/esm3/magiceffects.cpp index a8a759949b..6b86dfb02b 100644 --- a/components/esm3/magiceffects.cpp +++ b/components/esm3/magiceffects.cpp @@ -3,6 +3,8 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include + namespace ESM { @@ -10,7 +12,7 @@ namespace ESM { for (const auto& [key, params] : mEffects) { - esm.writeHNT("EFID", key); + esm.writeHNT("EFID", ESM::MagicEffect::refIdToIndex(key)); esm.writeHNT("BASE", params.first); esm.writeHNT("MODI", params.second); } @@ -28,7 +30,7 @@ namespace ESM params.second = 0.f; else esm.getHNT(params.second, "MODI"); - mEffects.emplace(id, params); + mEffects.emplace(ESM::MagicEffect::indexToRefId(id), params); } } diff --git a/components/esm3/magiceffects.hpp b/components/esm3/magiceffects.hpp index 74a6e34743..82194d0039 100644 --- a/components/esm3/magiceffects.hpp +++ b/components/esm3/magiceffects.hpp @@ -12,11 +12,13 @@ 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; @@ -24,14 +26,14 @@ namespace ESM struct SummonKey { - SummonKey(int32_t effectId, const ESM::RefId& sourceId, int32_t index) + SummonKey(const ESM::MagicEffectId& effectId, const ESM::RefId& sourceId, int32_t index) : mEffectId(effectId) , mSourceId(sourceId) , mEffectIndex(index) { } - int32_t mEffectId; + ESM::MagicEffectId mEffectId; ESM::RefId mSourceId; int32_t mEffectIndex; }; From 3e586ed693f834cd1f4c3fde6ed4c0f1c2082c7d Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Mon, 8 Dec 2025 18:43:16 -0500 Subject: [PATCH 05/14] 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; }; From 5839910eea17de30657716f1cffcd04963f2aa7c Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Thu, 11 Dec 2025 16:56:49 -0500 Subject: [PATCH 06/14] Refactoring mwgui and mwmechanics in openmw-lib. Magic bindings done in a later commit. --- apps/openmw/mwgui/alchemywindow.cpp | 4 +- apps/openmw/mwgui/spellcreationdialog.cpp | 26 +- apps/openmw/mwgui/spellcreationdialog.hpp | 4 +- apps/openmw/mwgui/spellicons.cpp | 4 +- apps/openmw/mwgui/spellicons.hpp | 2 +- apps/openmw/mwgui/spellmodel.cpp | 6 +- apps/openmw/mwgui/tooltips.cpp | 4 +- apps/openmw/mwgui/tooltips.hpp | 2 +- apps/openmw/mwgui/widgets.hpp | 29 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 6 +- apps/openmw/mwmechanics/spelleffects.cpp | 1318 ++++++++++--------- apps/openmw/mwmechanics/spellpriority.cpp | 740 +++++------ apps/openmw/mwmechanics/spellresistance.cpp | 22 +- apps/openmw/mwmechanics/spellresistance.hpp | 7 +- apps/openmw/mwmechanics/spells.cpp | 4 +- apps/openmw/mwmechanics/spellutil.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 4 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 19 files changed, 1127 insertions(+), 1061 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index da7e1dff78..4afd2ed18a 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -459,9 +459,9 @@ namespace MWGui for (const MWMechanics::EffectKey& effectKey : effectIds) { Widgets::SpellEffectParams params; - params.mEffectID = static_cast(effectKey.mId); + params.mEffectID = effectKey.mId; const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().find(effectKey.mId); + = MWBase::Environment::get().getESMStore()->get().search(effectKey.mId); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) params.mSkill = effectKey.mArg; else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index c5ece8ebe0..01722d2f3f 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -33,20 +33,20 @@ namespace { - bool sortMagicEffects(short id1, short id2) + bool sortMagicEffects(const ESM::RefId& id1, const ESM::RefId& id2) { const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); - return gmst.find(ESM::MagicEffect::indexToGmstString(id1))->mValue.getString() - < gmst.find(ESM::MagicEffect::indexToGmstString(id2))->mValue.getString(); + return gmst.find(ESM::MagicEffect::refIdToGmstString(id1))->mValue.getString() + < gmst.find(ESM::MagicEffect::refIdToGmstString(id2))->mValue.getString(); } void init(ESM::ENAMstruct& effect) { effect.mArea = 0; effect.mDuration = 0; - effect.mEffectID = -1; + effect.mEffectID = ESM::RefId(); effect.mMagnMax = 0; effect.mMagnMin = 0; effect.mRange = 0; @@ -222,9 +222,9 @@ namespace MWGui mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); - mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); + mEffectName->setCaptionWithReplacing("#{" + std::string(ESM::MagicEffect::refIdToGmstString(effect->mId)) + "}"); - mEffect.mEffectID = static_cast(effect->mIndex); + mEffect.mEffectID = effect->mId; mMagicEffect = effect; @@ -761,7 +761,7 @@ namespace MWGui , mUsedEffectsView(nullptr) , mAddEffectDialog() , mSelectedEffect(0) - , mSelectedKnownEffectId(0) + , mSelectedKnownEffectId(ESM::RefId()) , mConstantEffect(false) , mType(type) { @@ -782,7 +782,7 @@ namespace MWGui MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - std::vector knownEffects; + std::vector knownEffects; for (const ESM::Spell* spell : spells) { @@ -792,7 +792,7 @@ namespace MWGui for (const ESM::IndexedENAMstruct& effectInfo : spell->mEffects.mList) { - int16_t effectId = effectInfo.mData.mEffectID; + ESM::RefId effectId = effectInfo.mData.mEffectID; const ESM::MagicEffect* effect = MWBase::Environment::get().getESMStore()->get().find(effectId); @@ -812,12 +812,12 @@ namespace MWGui mAvailableEffectsList->clear(); int i = 0; - for (const short effectId : knownEffects) + for (const ESM::RefId& effectId : knownEffects) { mAvailableEffectsList->addItem(MWBase::Environment::get() .getESMStore() ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) + .find(ESM::MagicEffect::refIdToGmstString(effectId)) ->mValue.getString()); mButtonMapping[i] = effectId; ++i; @@ -826,12 +826,12 @@ namespace MWGui mAvailableEffectsList->scrollToTop(); mAvailableButtons.clear(); - for (const short effectId : knownEffects) + for (const ESM::RefId& effectId : knownEffects) { const std::string& name = MWBase::Environment::get() .getESMStore() ->get() - .find(ESM::MagicEffect::indexToGmstString(effectId)) + .find(ESM::MagicEffect::refIdToGmstString(effectId)) ->mValue.getString(); MyGUI::Button* w = mAvailableEffectsList->getItemWidget(name); mAvailableButtons.emplace_back(w); diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index f3382817e7..a54be05312 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -113,7 +113,7 @@ namespace MWGui void setConstantEffect(bool constant); protected: - std::map mButtonMapping; // maps button ID to effect ID + std::map mButtonMapping; // maps button ID to effect ID Gui::MWList* mAvailableEffectsList; MyGUI::ScrollView* mUsedEffectsView; @@ -123,7 +123,7 @@ namespace MWGui std::unique_ptr mSelectSkillDialog; int mSelectedEffect; - short mSelectedKnownEffectId; + ESM::RefId mSelectedKnownEffectId; bool mConstantEffect; diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index fae68389fd..deb7df5be9 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -28,7 +28,7 @@ namespace MWGui MWWorld::Ptr player = MWMechanics::getPlayer(); const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - std::map> effects; + std::map> effects; for (const auto& params : stats.getActiveSpells()) { for (const auto& effect : params.getEffects()) @@ -154,7 +154,7 @@ namespace MWGui Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); - const std::string& name = ESM::MagicEffect::indexToGmstString(effectId); + const std::string& name = std::string(ESM::MagicEffect::refIdToGmstString(effectId)); ToolTipInfo tooltipInfo; tooltipInfo.caption = "#{" + name + "}"; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index e53fa98284..e1d1d169d5 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -44,7 +44,7 @@ namespace MWGui void updateWidgets(MyGUI::Widget* parent, bool adjustSize); private: - std::map mWidgetMap; + std::map mWidgetMap; }; } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 5a732d771c..711761e655 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -48,11 +48,11 @@ namespace MWGui for (const auto& effect : effects.mList) { - short effectId = effect.mData.mEffectID; + ESM::RefId effectId = effect.mData.mEffectID; - if (effectId != -1) + if (!effectId.empty()) { - const ESM::MagicEffect* magicEffect = store.get().find(effectId); + const ESM::MagicEffect* magicEffect = store.get().search(effectId); const ESM::Attribute* attribute = store.get().search(ESM::Attribute::indexToRefId(effect.mData.mAttribute)); const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(effect.mData.mSkill)); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 88eedae99c..607614bec6 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -954,11 +954,11 @@ namespace MWGui widget->setUserString("ToolTipLayout", "ClassToolTip"); } - void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) + void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, const ESM::RefId& id) { const auto& store = MWBase::Environment::get().getESMStore(); const ESM::MagicEffect* effect = store->get().find(id); - const std::string& name = ESM::MagicEffect::indexToGmstString(id); + const std::string& name = std::string(ESM::MagicEffect::refIdToGmstString(id)); std::string icon = effect->mIcon; icon.insert(icon.rfind('\\') + 1, "b_"); diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 80a555733c..3c144f13cf 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -98,7 +98,7 @@ namespace MWGui static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); static void createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass); - static void createMagicEffectToolTip(MyGUI::Widget* widget, short id); + static void createMagicEffectToolTip(MyGUI::Widget* widget, const ESM::RefId& id); bool checkOwned(); /// Returns True if taking mFocusObject would be crime diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d5dc2abc50..6c233ef6a8 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace MyGUI { @@ -40,7 +41,7 @@ namespace MWGui , mIsConstant(false) , mNoMagnitude(false) , mKnown(true) - , mEffectID(-1) + , mEffectID(ESM::RefId()) , mMagnMin(-1) , mMagnMax(-1) , mRange(-1) @@ -55,10 +56,8 @@ namespace MWGui bool mKnown; // is this effect known to the player? (If not, will display as a question mark instead) - // value of -1 here means the effect is unknown to the player - short mEffectID; - - ESM::RefId mSkill, mAttribute; + // value of EmptyRefId here means the effect is unknown to the player + ESM::RefId mEffectID, mSkill, mAttribute; // value of -1 here means the value is unavailable int mMagnMin, mMagnMax, mRange, mDuration; @@ -71,16 +70,16 @@ namespace MWGui if (mEffectID != other.mEffectID) return false; - bool involvesAttribute = (mEffectID == 74 // restore attribute - || mEffectID == 85 // absorb attribute - || mEffectID == 17 // drain attribute - || mEffectID == 79 // fortify attribute - || mEffectID == 22); // damage attribute - bool involvesSkill = (mEffectID == 78 // restore skill - || mEffectID == 89 // absorb skill - || mEffectID == 21 // drain skill - || mEffectID == 83 // fortify skill - || mEffectID == 26); // damage skill + bool involvesAttribute = (mEffectID == ESM::MagicEffect::RestoreAttribute + || mEffectID == ESM::MagicEffect::AbsorbAttribute + || mEffectID == ESM::MagicEffect::DrainAttribute + || mEffectID == ESM::MagicEffect::FortifyAttribute + || mEffectID == ESM::MagicEffect::DamageAttribute); + bool involvesSkill = (mEffectID == ESM::MagicEffect::RestoreSkill + || mEffectID == ESM::MagicEffect::AbsorbSkill + || mEffectID == ESM::MagicEffect::DrainSkill + || mEffectID == ESM::MagicEffect::FortifySkill + || mEffectID == ESM::MagicEffect::DamageSkill); return ((other.mSkill == mSkill) || !involvesSkill) && ((other.mAttribute == mAttribute) && !involvesAttribute) && (other.mArea == mArea); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0f1fe34701..a2695122a7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -796,7 +796,7 @@ namespace MWMechanics continue; for (const auto& effect : spell.getEffects()) { - static const std::array damageEffects{ + static const std::array damageEffects{ ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index e82ff0c8d2..2a9aeddb2e 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -350,12 +350,14 @@ namespace MWMechanics if (godmode) return; auto& prng = MWBase::Environment::get().getWorld()->getPrng(); + static const std::array elementalShieldEffects{ ESM::MagicEffect::FireShield, + ESM::MagicEffect::LightningShield, ESM::MagicEffect::FrostShield }; for (int i = 0; i < 3; ++i) { float magnitude = victim.getClass() .getCreatureStats(victim) .getMagicEffects() - .getOrDefault(ESM::MagicEffect::FireShield + i) + .getOrDefault(elementalShieldEffects[i]) .getMagnitude(); if (!magnitude) @@ -375,7 +377,7 @@ namespace MWMechanics float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99(prng)); - short element = ESM::MagicEffect::FireDamage; + ESM::MagicEffectId element = ESM::MagicEffect::FireDamage; if (i == 1) element = ESM::MagicEffect::ShockDamage; if (i == 2) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index 13a84ba4a0..0706e9961b 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -37,6 +37,13 @@ namespace { + enum Stats + { + Health = 0, + Magicka = 1, + Fatigue = 2 + }; + float roll(const ESM::ActiveEffect& effect) { if (effect.mMinMagnitude == effect.mMaxMagnitude) @@ -47,7 +54,7 @@ namespace } ESM::ActiveEffect::Flags modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, - ESM::MagicEffect::Effects creatureEffect, MWMechanics::AiSetting setting, float magnitude) + const ESM::RefId& creatureEffect, MWMechanics::AiSetting setting, float magnitude) { if (target == MWMechanics::getPlayer() || (effect.mEffectId == creatureEffect) == target.getClass().isNpc()) return ESM::ActiveEffect::Flag_Invalid; @@ -292,7 +299,7 @@ namespace const VFS::Path::Normalized absorbStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(absorbStatic->mModel)); animation->addEffect( - absorbStaticModel.value(), ESM::MagicEffect::indexToName(ESM::MagicEffect::SpellAbsorption), false); + absorbStaticModel.value(), ESM::MagicEffect::refIdToName(ESM::MagicEffect::SpellAbsorption), false); } int spellCost = 0; @@ -365,8 +372,7 @@ namespace { const ESM::Spell* spell = spellParams.hasFlag(ESM::ActiveSpells::Flag_Temporary) ? spellParams.getSpell() : nullptr; - float magnitudeMult = MWMechanics::getEffectMultiplier( - static_cast(effect.mEffectId), target, caster, spell, &magnitudes); + float magnitudeMult = MWMechanics::getEffectMultiplier(effect.mEffectId, target, caster, spell, &magnitudes); if (magnitudeMult == 0) { // Fully resisted, show message @@ -385,7 +391,7 @@ namespace return MWMechanics::MagicApplicationResult::Type::APPLIED; } - static const std::map sBoundItemsMap{ + static const std::map sBoundItemsMap{ { ESM::MagicEffect::BoundBattleAxe, "sMagicBoundBattleAxeID" }, { ESM::MagicEffect::BoundBoots, "sMagicBoundBootsID" }, { ESM::MagicEffect::BoundCuirass, "sMagicBoundCuirassID" }, @@ -426,318 +432,315 @@ namespace MWMechanics { const auto world = MWBase::Environment::get().getWorld(); const bool godmode = target == getPlayer() && world->getGodModeState(); - switch (effect.mEffectId) - { - case ESM::MagicEffect::CureCommonDisease: - purgePermanent(target, &Spells::purgeCommonDisease, ESM::Spell::ST_Disease); - break; - case ESM::MagicEffect::CureBlightDisease: - purgePermanent(target, &Spells::purgeBlightDisease, ESM::Spell::ST_Blight); - break; - case ESM::MagicEffect::RemoveCurse: - purgePermanent(target, &Spells::purgeCurses, ESM::Spell::ST_Curse); - break; - case ESM::MagicEffect::CureCorprusDisease: - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( - target, ESM::MagicEffect::Corprus); - break; - case ESM::MagicEffect::CurePoison: - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( - target, ESM::MagicEffect::Poison); - break; - case ESM::MagicEffect::CureParalyzation: - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( - target, ESM::MagicEffect::Paralyze); - break; - case ESM::MagicEffect::Dispel: - // Dispel removes entire spells at once - target.getClass().getCreatureStats(target).getActiveSpells().purge( - [magnitude = effect.mMagnitude](const ActiveSpells::ActiveSpellParams& params) { - if (params.hasFlag(ESM::ActiveSpells::Flag_Temporary)) + if (effect.mEffectId == ESM::MagicEffect::CureCommonDisease) + purgePermanent(target, &Spells::purgeCommonDisease, ESM::Spell::ST_Disease); + else if (effect.mEffectId == ESM::MagicEffect::CureBlightDisease) + purgePermanent(target, &Spells::purgeBlightDisease, ESM::Spell::ST_Blight); + else if (effect.mEffectId == ESM::MagicEffect::RemoveCurse) + purgePermanent(target, &Spells::purgeCurses, ESM::Spell::ST_Curse); + else if (effect.mEffectId == ESM::MagicEffect::CureCorprusDisease) + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( + target, ESM::MagicEffect::Corprus); + else if (effect.mEffectId == ESM::MagicEffect::CurePoison) + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( + target, ESM::MagicEffect::Poison); + else if (effect.mEffectId == ESM::MagicEffect::CureParalyzation) + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( + target, ESM::MagicEffect::Paralyze); + else if (effect.mEffectId == ESM::MagicEffect::Dispel) + // Dispel removes entire spells at once + target.getClass().getCreatureStats(target).getActiveSpells().purge( + [magnitude = effect.mMagnitude](const ActiveSpells::ActiveSpellParams& params) { + if (params.hasFlag(ESM::ActiveSpells::Flag_Temporary)) + { + const ESM::Spell* spell = params.getSpell(); + if (spell && spell->mData.mType == ESM::Spell::ST_Spell) { - const ESM::Spell* spell = params.getSpell(); - if (spell && spell->mData.mType == ESM::Spell::ST_Spell) - { - auto& prng = MWBase::Environment::get().getWorld()->getPrng(); - return Misc::Rng::roll0to99(prng) < magnitude; - } + auto& prng = MWBase::Environment::get().getWorld()->getPrng(); + return Misc::Rng::roll0to99(prng) < magnitude; } - return false; - }, - target); - break; - case ESM::MagicEffect::AlmsiviIntervention: - case ESM::MagicEffect::DivineIntervention: - if (target != getPlayer()) - return ESM::ActiveEffect::Flag_Invalid; - else if (world->isTeleportingEnabled()) + } + return false; + }, + target); + else if (effect.mEffectId == ESM::MagicEffect::AlmsiviIntervention + || effect.mEffectId == ESM::MagicEffect::DivineIntervention) + { + if (target != getPlayer()) + return ESM::ActiveEffect::Flag_Invalid; + else if (world->isTeleportingEnabled()) + { + std::string_view marker + = (effect.mEffectId == ESM::MagicEffect::DivineIntervention) ? "divinemarker" : "templemarker"; + world->teleportToClosestMarker(target, ESM::RefId::stringRefId(marker)); + if (!caster.isEmpty()) { - std::string_view marker - = (effect.mEffectId == ESM::MagicEffect::DivineIntervention) ? "divinemarker" : "templemarker"; - world->teleportToClosestMarker(target, ESM::RefId::stringRefId(marker)); + MWRender::Animation* anim = world->getAnimation(caster); + anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); + const ESM::Static* fx + = world->getStore().get().search(ESM::RefId::stringRefId("VFX_Summon_end")); + if (fx != nullptr) + { + const VFS::Path::Normalized fxModel + = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(fx->mModel)); + anim->addEffect(fxModel.value(), ""); + } + } + } + else if (caster == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + } + else if (effect.mEffectId == ESM::MagicEffect::Mark) + { + if (target != getPlayer()) + return ESM::ActiveEffect::Flag_Invalid; + else if (world->isTeleportingEnabled()) + world->getPlayer().markPosition(target.getCell(), target.getRefData().getPosition()); + else if (caster == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + } + else if (effect.mEffectId == ESM::MagicEffect::Recall) + { + if (target != getPlayer()) + return ESM::ActiveEffect::Flag_Invalid; + else if (world->isTeleportingEnabled()) + { + MWWorld::CellStore* markedCell = nullptr; + ESM::Position markedPosition; + + world->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell) + { + ESM::RefId dest = markedCell->getCell()->getId(); + MWWorld::ActionTeleport action(dest, markedPosition, false); + action.execute(target); if (!caster.isEmpty()) { MWRender::Animation* anim = world->getAnimation(caster); - anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId)); - const ESM::Static* fx - = world->getStore().get().search(ESM::RefId::stringRefId("VFX_Summon_end")); - if (fx != nullptr) - { - const VFS::Path::Normalized fxModel - = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(fx->mModel)); - anim->addEffect(fxModel.value(), ""); - } + anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); } } - else if (caster == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); - break; - case ESM::MagicEffect::Mark: - if (target != getPlayer()) - return ESM::ActiveEffect::Flag_Invalid; - else if (world->isTeleportingEnabled()) - world->getPlayer().markPosition(target.getCell(), target.getRefData().getPosition()); - else if (caster == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); - break; - case ESM::MagicEffect::Recall: - if (target != getPlayer()) - return ESM::ActiveEffect::Flag_Invalid; - else if (world->isTeleportingEnabled()) + } + else if (caster == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + } + else if (effect.mEffectId == ESM::MagicEffect::CommandCreature + || effect.mEffectId == ESM::MagicEffect::CommandHumanoid) + { + if (caster.isEmpty() || !caster.getClass().isActor() || target == getPlayer() + || (effect.mEffectId == ESM::MagicEffect::CommandCreature) == target.getClass().isNpc()) + return ESM::ActiveEffect::Flag_Invalid; + else if (effect.mMagnitude >= target.getClass().getCreatureStats(target).getLevel()) + { + MWMechanics::AiFollow package(caster, true); + target.getClass().getCreatureStats(target).getAiSequence().stack(package, target); + } + } + else if (effect.mEffectId == ESM::MagicEffect::ExtraSpell) + { + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + if (target != getPlayer()) + { + auto& store = target.getClass().getInventoryStore(target); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { - MWWorld::CellStore* markedCell = nullptr; - ESM::Position markedPosition; - - world->getPlayer().getMarkedPosition(markedCell, markedPosition); - if (markedCell) + // Unequip everything except weapons, torches, and pants + switch (slot) { - ESM::RefId dest = markedCell->getCell()->getId(); - MWWorld::ActionTeleport action(dest, markedPosition, false); - action.execute(target); - if (!caster.isEmpty()) + case MWWorld::InventoryStore::Slot_Ammunition: + case MWWorld::InventoryStore::Slot_CarriedRight: + case MWWorld::InventoryStore::Slot_Pants: + continue; + case MWWorld::InventoryStore::Slot_CarriedLeft: { - MWRender::Animation* anim = world->getAnimation(caster); - anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId)); - } - } - } - else if (caster == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); - break; - case ESM::MagicEffect::CommandCreature: - case ESM::MagicEffect::CommandHumanoid: - if (caster.isEmpty() || !caster.getClass().isActor() || target == getPlayer() - || (effect.mEffectId == ESM::MagicEffect::CommandCreature) == target.getClass().isNpc()) - return ESM::ActiveEffect::Flag_Invalid; - else if (effect.mMagnitude >= target.getClass().getCreatureStats(target).getLevel()) - { - MWMechanics::AiFollow package(caster, true); - target.getClass().getCreatureStats(target).getAiSequence().stack(package, target); - } - break; - case ESM::MagicEffect::ExtraSpell: - if (!target.getClass().hasInventoryStore(target)) - return ESM::ActiveEffect::Flag_Invalid; - if (target != getPlayer()) - { - auto& store = target.getClass().getInventoryStore(target); - for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - // Unequip everything except weapons, torches, and pants - switch (slot) - { - case MWWorld::InventoryStore::Slot_Ammunition: - case MWWorld::InventoryStore::Slot_CarriedRight: - case MWWorld::InventoryStore::Slot_Pants: + auto carried = store.getSlot(slot); + if (carried == store.end() || carried.getType() != MWWorld::ContainerStore::Type_Armor) continue; - case MWWorld::InventoryStore::Slot_CarriedLeft: - { - auto carried = store.getSlot(slot); - if (carried == store.end() || carried.getType() != MWWorld::ContainerStore::Type_Armor) - continue; - [[fallthrough]]; - } - default: - store.unequipSlot(slot); + [[fallthrough]]; } + default: + store.unequipSlot(slot); } } - break; - case ESM::MagicEffect::TurnUndead: - if (target.getClass().isNpc() - || target.get()->mBase->mData.mType != ESM::Creature::Undead) - return ESM::ActiveEffect::Flag_Invalid; + } + } + else if (effect.mEffectId == ESM::MagicEffect::TurnUndead) + { + if (target.getClass().isNpc() || target.get()->mBase->mData.mType != ESM::Creature::Undead) + return ESM::ActiveEffect::Flag_Invalid; + else + { + auto& creatureStats = target.getClass().getCreatureStats(target); + Stat stat = creatureStats.getAiSetting(AiSetting::Flee); + stat.setModifier(static_cast(stat.getModifier() + effect.mMagnitude)); + creatureStats.setAiSetting(AiSetting::Flee, stat); + } + } + else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature || effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) + return modifyAiSetting( + target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::CalmCreature || effect.mEffectId == ESM::MagicEffect::CalmHumanoid) + { + ESM::ActiveEffect::Flags applied = modifyAiSetting( + target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude); + if (applied != ESM::ActiveEffect::Flag_Applied) + return applied; + if (effect.mMagnitude > 0) + { + auto& creatureStats = target.getClass().getCreatureStats(target); + creatureStats.getAiSequence().stopCombat(); + } + } + else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature || effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) + return modifyAiSetting( + target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::RallyCreature || effect.mEffectId == ESM::MagicEffect::RallyHumanoid) + return modifyAiSetting( + target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, -effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::Charm) + { + if (!target.getClass().isNpc()) + return ESM::ActiveEffect::Flag_Invalid; + } + else if (effect.mEffectId == ESM::MagicEffect::Sound) + { + if (target == getPlayer()) + { + const auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); + float volume = std::clamp( + (magnitudes.getOrDefault(effect.mEffectId).getModifier() + effect.mMagnitude) / 100.f, 0.f, 1.f); + MWBase::Environment::get().getSoundManager()->playSound3D(target, + ESM::RefId::stringRefId("magic sound"), volume, 1.f, MWSound::Type::Sfx, + MWSound::PlayMode::LoopNoEnv); + } + } + else if (effect.mEffectId == ESM::MagicEffect::SummonScamp || + effect.mEffectId == ESM::MagicEffect::SummonClannfear || + effect.mEffectId == ESM::MagicEffect::SummonDaedroth || + effect.mEffectId == ESM::MagicEffect::SummonDremora || + effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost || + effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion || + effect.mEffectId == ESM::MagicEffect::SummonBonewalker || + effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker || + effect.mEffectId == ESM::MagicEffect::SummonBonelord || + effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight || + effect.mEffectId == ESM::MagicEffect::SummonHunger || + effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint || + effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach || + effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach || + effect.mEffectId == ESM::MagicEffect::SummonStormAtronach || + effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere || + effect.mEffectId == ESM::MagicEffect::SummonFabricant || + effect.mEffectId == ESM::MagicEffect::SummonWolf || + effect.mEffectId == ESM::MagicEffect::SummonBear || + effect.mEffectId == ESM::MagicEffect::SummonBonewolf || + effect.mEffectId == ESM::MagicEffect::SummonCreature04 || + effect.mEffectId == ESM::MagicEffect::SummonCreature05) + { + if (!target.isInCell()) + return ESM::ActiveEffect::Flag_Invalid; + effect.mArg = summonCreature(effect.mEffectId, target); + } + else if (effect.mEffectId == ESM::MagicEffect::BoundGloves) + { + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + addBoundItem( + ESM::RefId::stringRefId( + world->getStore().get().find("sMagicBoundRightGauntletID")->mValue.getString()), + target); + addBoundItem( + ESM::RefId::stringRefId( + world->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString()), + target); + } + else if (effect.mEffectId == ESM::MagicEffect::BoundDagger || + effect.mEffectId == ESM::MagicEffect::BoundLongsword || + effect.mEffectId == ESM::MagicEffect::BoundMace || + effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || + effect.mEffectId == ESM::MagicEffect::BoundSpear || + effect.mEffectId == ESM::MagicEffect::BoundLongbow || + effect.mEffectId == ESM::MagicEffect::BoundCuirass || + effect.mEffectId == ESM::MagicEffect::BoundHelm || + effect.mEffectId == ESM::MagicEffect::BoundBoots || + effect.mEffectId == ESM::MagicEffect::BoundShield) + { + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + const std::string& item = sBoundItemsMap.at(effect.mEffectId); + const MWWorld::Store& gmst = world->getStore().get(); + const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); + if (!addBoundItem(itemId, target)) + effect.mTimeLeft = 0.f; + } + else if (effect.mEffectId == ESM::MagicEffect::FireDamage || effect.mEffectId == ESM::MagicEffect::ShockDamage + || effect.mEffectId == ESM::MagicEffect::FrostDamage || effect.mEffectId == ESM::MagicEffect::DamageHealth + || effect.mEffectId == ESM::MagicEffect::Poison || effect.mEffectId == ESM::MagicEffect::DamageMagicka + || effect.mEffectId == ESM::MagicEffect::DamageFatigue) + { + if (!godmode) + { + int index = 0; + if (effect.mEffectId == ESM::MagicEffect::DamageMagicka) + index = 1; + else if (effect.mEffectId == ESM::MagicEffect::DamageFatigue) + index = 2; + // Damage "Dynamic" abilities reduce the base value + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, index, -effect.mMagnitude); else { - auto& creatureStats = target.getClass().getCreatureStats(target); - Stat stat = creatureStats.getAiSetting(AiSetting::Flee); - stat.setModifier(static_cast(stat.getModifier() + effect.mMagnitude)); - creatureStats.setAiSetting(AiSetting::Flee, stat); + adjustDynamicStat( + target, index, -effect.mMagnitude, index == 2 && Settings::game().mUncappedDamageFatigue); + if (index == 0) + receivedMagicDamage = affectedHealth = true; } - break; - case ESM::MagicEffect::FrenzyCreature: - case ESM::MagicEffect::FrenzyHumanoid: - return modifyAiSetting( - target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, effect.mMagnitude); - case ESM::MagicEffect::CalmCreature: - case ESM::MagicEffect::CalmHumanoid: - { - ESM::ActiveEffect::Flags applied = modifyAiSetting( - target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude); - if (applied != ESM::ActiveEffect::Flag_Applied) - return applied; - if (effect.mMagnitude > 0) - { - auto& creatureStats = target.getClass().getCreatureStats(target); - creatureStats.getAiSequence().stopCombat(); - } - break; } - case ESM::MagicEffect::DemoralizeCreature: - case ESM::MagicEffect::DemoralizeHumanoid: - return modifyAiSetting( - target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, effect.mMagnitude); - case ESM::MagicEffect::RallyCreature: - case ESM::MagicEffect::RallyHumanoid: - return modifyAiSetting( - target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, -effect.mMagnitude); - case ESM::MagicEffect::Charm: - if (!target.getClass().isNpc()) - return ESM::ActiveEffect::Flag_Invalid; - break; - case ESM::MagicEffect::Sound: - if (target == getPlayer()) - { - const auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); - float volume = std::clamp( - (magnitudes.getOrDefault(effect.mEffectId).getModifier() + effect.mMagnitude) / 100.f, 0.f, - 1.f); - MWBase::Environment::get().getSoundManager()->playSound3D(target, - ESM::RefId::stringRefId("magic sound"), volume, 1.f, MWSound::Type::Sfx, - MWSound::PlayMode::LoopNoEnv); - } - break; - case ESM::MagicEffect::SummonScamp: - case ESM::MagicEffect::SummonClannfear: - case ESM::MagicEffect::SummonDaedroth: - case ESM::MagicEffect::SummonDremora: - case ESM::MagicEffect::SummonAncestralGhost: - case ESM::MagicEffect::SummonSkeletalMinion: - case ESM::MagicEffect::SummonBonewalker: - case ESM::MagicEffect::SummonGreaterBonewalker: - case ESM::MagicEffect::SummonBonelord: - case ESM::MagicEffect::SummonWingedTwilight: - case ESM::MagicEffect::SummonHunger: - case ESM::MagicEffect::SummonGoldenSaint: - case ESM::MagicEffect::SummonFlameAtronach: - case ESM::MagicEffect::SummonFrostAtronach: - case ESM::MagicEffect::SummonStormAtronach: - case ESM::MagicEffect::SummonCenturionSphere: - case ESM::MagicEffect::SummonFabricant: - case ESM::MagicEffect::SummonWolf: - case ESM::MagicEffect::SummonBear: - case ESM::MagicEffect::SummonBonewolf: - case ESM::MagicEffect::SummonCreature04: - case ESM::MagicEffect::SummonCreature05: - if (!target.isInCell()) - return ESM::ActiveEffect::Flag_Invalid; - effect.mArg = summonCreature(effect.mEffectId, target); - break; - case ESM::MagicEffect::BoundGloves: - if (!target.getClass().hasInventoryStore(target)) - return ESM::ActiveEffect::Flag_Invalid; - addBoundItem(ESM::RefId::stringRefId(world->getStore() - .get() - .find("sMagicBoundRightGauntletID") - ->mValue.getString()), - target); - // left gauntlet added below - [[fallthrough]]; - case ESM::MagicEffect::BoundDagger: - case ESM::MagicEffect::BoundLongsword: - case ESM::MagicEffect::BoundMace: - case ESM::MagicEffect::BoundBattleAxe: - case ESM::MagicEffect::BoundSpear: - case ESM::MagicEffect::BoundLongbow: - case ESM::MagicEffect::BoundCuirass: - case ESM::MagicEffect::BoundHelm: - case ESM::MagicEffect::BoundBoots: - case ESM::MagicEffect::BoundShield: + } + else if (effect.mEffectId == ESM::MagicEffect::DamageAttribute) + { + if (!godmode) + damageAttribute(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::DamageSkill) + { + if (!godmode && target.getClass().isNpc()) { - if (!target.getClass().hasInventoryStore(target)) - return ESM::ActiveEffect::Flag_Invalid; - const std::string& item = sBoundItemsMap.at(effect.mEffectId); - const MWWorld::Store& gmst = world->getStore().get(); - const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); - if (!addBoundItem(itemId, target)) - effect.mTimeLeft = 0.f; - break; - } - case ESM::MagicEffect::FireDamage: - case ESM::MagicEffect::ShockDamage: - case ESM::MagicEffect::FrostDamage: - case ESM::MagicEffect::DamageHealth: - case ESM::MagicEffect::Poison: - case ESM::MagicEffect::DamageMagicka: - case ESM::MagicEffect::DamageFatigue: - if (!godmode) - { - int index = 0; - if (effect.mEffectId == ESM::MagicEffect::DamageMagicka) - index = 1; - else if (effect.mEffectId == ESM::MagicEffect::DamageFatigue) - index = 2; - // Damage "Dynamic" abilities reduce the base value - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - modDynamicStat(target, index, -effect.mMagnitude); - else - { - adjustDynamicStat( - target, index, -effect.mMagnitude, index == 2 && Settings::game().mUncappedDamageFatigue); - if (index == 0) - receivedMagicDamage = affectedHealth = true; - } - } - break; - case ESM::MagicEffect::DamageAttribute: - if (!godmode) - damageAttribute(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::DamageSkill: - if (!godmode && target.getClass().isNpc()) + // Damage Skill abilities reduce base skill :todd: + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) { + auto& npcStats = target.getClass().getNpcStats(target); + SkillValue& skill = npcStats.getSkill(effect.getSkillOrAttribute()); // Damage Skill abilities reduce base skill :todd: - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - { - auto& npcStats = target.getClass().getNpcStats(target); - SkillValue& skill = npcStats.getSkill(effect.getSkillOrAttribute()); - // Damage Skill abilities reduce base skill :todd: - skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f)); - } - else - damageSkill(target, effect, effect.mMagnitude); + skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f)); } - break; - case ESM::MagicEffect::RestoreAttribute: - restoreAttribute(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::RestoreSkill: - if (target.getClass().isNpc()) - restoreSkill(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::RestoreHealth: - affectedHealth = true; - [[fallthrough]]; - case ESM::MagicEffect::RestoreMagicka: - case ESM::MagicEffect::RestoreFatigue: - adjustDynamicStat(target, effect.mEffectId - ESM::MagicEffect::RestoreHealth, effect.mMagnitude); - break; - case ESM::MagicEffect::SunDamage: + else + damageSkill(target, effect, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::RestoreAttribute) + restoreAttribute(target, effect, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::RestoreSkill) + { + if (target.getClass().isNpc()) + restoreSkill(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::RestoreHealth) + { + affectedHealth = true; + adjustDynamicStat(target, Stats::Health, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::RestoreMagicka) + adjustDynamicStat(target, Stats::Magicka, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::RestoreFatigue) + adjustDynamicStat(target, Stats::Fatigue, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::SunDamage) + { + //// isInCell shouldn't be needed, but updateActor called during game start + if (!godmode && target.isInCell() && target.getCell()->isExterior() + && !(target.getCell()->isQuasiExterior())) { - // isInCell shouldn't be needed, but updateActor called during game start - if (!target.isInCell() || !(target.getCell()->isExterior() || target.getCell()->isQuasiExterior()) - || godmode) - break; const float sunRisen = world->getSunPercentage(); static float fMagicSunBlockedMult = world->getStore().get().find("fMagicSunBlockedMult")->mValue.getFloat(); @@ -748,112 +751,167 @@ namespace MWMechanics if (damage > 0.f) receivedMagicDamage = affectedHealth = true; } - break; - case ESM::MagicEffect::DrainHealth: - case ESM::MagicEffect::DrainMagicka: - case ESM::MagicEffect::DrainFatigue: - if (godmode) - return ESM::ActiveEffect::Flag_Remove; - else - { - int index = effect.mEffectId - ESM::MagicEffect::DrainHealth; - // Unlike Absorb and Damage effects Drain effects can bring stats below zero - adjustDynamicStat(target, index, -effect.mMagnitude, true); - if (index == 0) - receivedMagicDamage = affectedHealth = true; - } - break; - case ESM::MagicEffect::FortifyHealth: - case ESM::MagicEffect::FortifyMagicka: - case ESM::MagicEffect::FortifyFatigue: - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - modDynamicStat(target, effect.mEffectId - ESM::MagicEffect::FortifyHealth, effect.mMagnitude); - else - adjustDynamicStat( - target, effect.mEffectId - ESM::MagicEffect::FortifyHealth, effect.mMagnitude, false, true); - break; - case ESM::MagicEffect::DrainAttribute: - if (godmode) - return ESM::ActiveEffect::Flag_Remove; - damageAttribute(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::FortifyAttribute: - // Abilities affect base stats, but not for drain - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - { - auto& creatureStats = target.getClass().getCreatureStats(target); - auto attribute = effect.getSkillOrAttribute(); - AttributeValue attr = creatureStats.getAttribute(attribute); - attr.setBase(attr.getBase() + effect.mMagnitude); - creatureStats.setAttribute(attribute, attr); - } - else - fortifyAttribute(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::DrainSkill: - if (godmode || !target.getClass().isNpc()) - return ESM::ActiveEffect::Flag_Remove; - damageSkill(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::FortifySkill: - if (target.getClass().isNpc()) - { - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - { - // Abilities affect base stats, but not for drain - auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.getSkillOrAttribute()); - skill.setBase(skill.getBase() + effect.mMagnitude); - } - else - fortifySkill(target, effect, effect.mMagnitude); - } - break; - case ESM::MagicEffect::FortifyMaximumMagicka: - recalculateMagicka = true; - break; - case ESM::MagicEffect::AbsorbHealth: - case ESM::MagicEffect::AbsorbMagicka: - case ESM::MagicEffect::AbsorbFatigue: - if (godmode) - return ESM::ActiveEffect::Flag_Remove; - else - { - int index = effect.mEffectId - ESM::MagicEffect::AbsorbHealth; - adjustDynamicStat(target, index, -effect.mMagnitude); - if (!caster.isEmpty()) - adjustDynamicStat(caster, index, effect.mMagnitude); - if (index == 0) - receivedMagicDamage = affectedHealth = true; - } - break; - case ESM::MagicEffect::AbsorbAttribute: - if (godmode) - return ESM::ActiveEffect::Flag_Remove; - else - { - damageAttribute(target, effect, effect.mMagnitude); - if (!caster.isEmpty()) - fortifyAttribute(caster, effect, effect.mMagnitude); - } - break; - case ESM::MagicEffect::AbsorbSkill: - if (godmode) - return ESM::ActiveEffect::Flag_Remove; - else - { - if (target.getClass().isNpc()) - damageSkill(target, effect, effect.mMagnitude); - if (!caster.isEmpty() && caster.getClass().isNpc()) - fortifySkill(caster, effect, effect.mMagnitude); - } - break; - case ESM::MagicEffect::DisintegrateArmor: + } + else if (effect.mEffectId == ESM::MagicEffect::DrainHealth) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + // Unlike Absorb and Damage effects Drain effects can bring stats below zero + adjustDynamicStat(target, Stats::Health, -effect.mMagnitude, true); + receivedMagicDamage = affectedHealth = true; + } + } + else if (effect.mEffectId == ESM::MagicEffect::DrainMagicka) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + // Unlike Absorb and Damage effects Drain effects can bring stats below zero + adjustDynamicStat(target, Stats::Magicka, -effect.mMagnitude, true); + } + } + else if (effect.mEffectId == ESM::MagicEffect::DrainFatigue) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + // Unlike Absorb and Damage effects Drain effects can bring stats below zero + adjustDynamicStat(target, Stats::Fatigue, -effect.mMagnitude, true); + } + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyHealth) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Health, effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Health, effect.mMagnitude, false, true); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyMagicka) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Magicka, effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Magicka, effect.mMagnitude, false, true); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyFatigue) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Fatigue, effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Fatigue, effect.mMagnitude, false, true); + } + else if (effect.mEffectId == ESM::MagicEffect::DrainAttribute) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + damageAttribute(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyAttribute) + { + // Abilities affect base stats, but not for drain + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + { + auto& creatureStats = target.getClass().getCreatureStats(target); + auto attribute = effect.getSkillOrAttribute(); + AttributeValue attr = creatureStats.getAttribute(attribute); + attr.setBase(attr.getBase() + effect.mMagnitude); + creatureStats.setAttribute(attribute, attr); + } + else + fortifyAttribute(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::DrainSkill) + { + if (godmode || !target.getClass().isNpc()) + return ESM::ActiveEffect::Flag_Remove; + damageSkill(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifySkill) + { + if (target.getClass().isNpc()) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + { + // Abilities affect base stats, but not for drain + auto& npcStats = target.getClass().getNpcStats(target); + auto& skill = npcStats.getSkill(effect.getSkillOrAttribute()); + skill.setBase(skill.getBase() + effect.mMagnitude); + } + else + fortifySkill(target, effect, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyMaximumMagicka) + recalculateMagicka = true; + else if (effect.mEffectId == ESM::MagicEffect::AbsorbHealth) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + adjustDynamicStat(target, Stats::Health, -effect.mMagnitude); + if (!caster.isEmpty()) + { + adjustDynamicStat(caster, Stats::Health, effect.mMagnitude); + } + receivedMagicDamage = affectedHealth = true; + } + } + else if (effect.mEffectId == ESM::MagicEffect::AbsorbMagicka) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + adjustDynamicStat(target, Stats::Magicka, -effect.mMagnitude); + if (!caster.isEmpty()) + adjustDynamicStat(caster, Stats::Magicka, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::AbsorbFatigue) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + adjustDynamicStat(target, Stats::Fatigue, -effect.mMagnitude); + if (!caster.isEmpty()) + adjustDynamicStat(caster, Stats::Fatigue, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::AbsorbAttribute) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + damageAttribute(target, effect, effect.mMagnitude); + if (!caster.isEmpty()) + fortifyAttribute(caster, effect, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::AbsorbSkill) + { + if (godmode) + return ESM::ActiveEffect::Flag_Remove; + else + { + if (target.getClass().isNpc()) + damageSkill(target, effect, effect.mMagnitude); + if (!caster.isEmpty() && caster.getClass().isNpc()) + fortifySkill(caster, effect, effect.mMagnitude); + } + } + else if (effect.mEffectId == ESM::MagicEffect::DisintegrateArmor) + { + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + if (!godmode) { - if (!target.getClass().hasInventoryStore(target)) - return ESM::ActiveEffect::Flag_Invalid; - if (godmode) - break; static const std::array priorities{ MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_Cuirass, @@ -870,14 +928,14 @@ namespace MWMechanics if (disintegrateSlot(target, priority, effect.mMagnitude)) break; } - break; } - case ESM::MagicEffect::DisintegrateWeapon: - if (!target.getClass().hasInventoryStore(target)) - return ESM::ActiveEffect::Flag_Invalid; - if (!godmode) - disintegrateSlot(target, MWWorld::InventoryStore::Slot_CarriedRight, effect.mMagnitude); - break; + } + else if (effect.mEffectId == ESM::MagicEffect::DisintegrateWeapon) + { + if (!target.getClass().hasInventoryStore(target)) + return ESM::ActiveEffect::Flag_Invalid; + if (!godmode) + disintegrateSlot(target, MWWorld::InventoryStore::Slot_CarriedRight, effect.mMagnitude); } return ESM::ActiveEffect::Flag_Applied; } @@ -887,37 +945,32 @@ namespace MWMechanics if (effect.mFlags & ESM::ActiveEffect::Flag_Invalid) return true; const auto world = MWBase::Environment::get().getWorld(); - switch (effect.mEffectId) + if (effect.mEffectId == ESM::MagicEffect::Levitate) { - case ESM::MagicEffect::Levitate: + if (!world->isLevitationEnabled()) { - if (!world->isLevitationEnabled()) - { - if (target == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); - return true; - } - break; + if (target == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + return true; } - case ESM::MagicEffect::Recall: - case ESM::MagicEffect::DivineIntervention: - case ESM::MagicEffect::AlmsiviIntervention: + } + else if (effect.mEffectId == ESM::MagicEffect::DivineIntervention || + effect.mEffectId == ESM::MagicEffect::Recall || + effect.mEffectId == ESM::MagicEffect::AlmsiviIntervention) + { + return effect.mFlags & ESM::ActiveEffect::Flag_Applied; + } + else if (effect.mEffectId == ESM::MagicEffect::WaterWalking) + { + if (target.getClass().isPureWaterCreature(target) && world->isSwimming(target)) + return true; + if (effect.mFlags & ESM::ActiveEffect::Flag_Applied) + return false; + if (!world->isWaterWalkingCastableOnTarget(target)) { - return effect.mFlags & ESM::ActiveEffect::Flag_Applied; - } - case ESM::MagicEffect::WaterWalking: - { - if (target.getClass().isPureWaterCreature(target) && world->isSwimming(target)) - return true; - if (effect.mFlags & ESM::ActiveEffect::Flag_Applied) - break; - if (!world->isWaterWalkingCastableOnTarget(target)) - { - if (target == getPlayer()) - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidEffect}"); - return true; - } - break; + if (target == getPlayer()) + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidEffect}"); + return true; } } return false; @@ -1094,7 +1147,7 @@ namespace MWMechanics effect.mTimeLeft = 0; auto anim = world->getAnimation(target); if (anim) - anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId)); + anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); // Note that we can't return REMOVED here because the effect still needs to be detectable } effect.mFlags |= applied; @@ -1109,220 +1162,227 @@ namespace MWMechanics const auto world = MWBase::Environment::get().getWorld(); const auto worldModel = MWBase::Environment::get().getWorldModel(); auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); - switch (effect.mEffectId) + if (effect.mEffectId == ESM::MagicEffect::CommandCreature + || effect.mEffectId == ESM::MagicEffect::CommandHumanoid) { - case ESM::MagicEffect::CommandCreature: - case ESM::MagicEffect::CommandHumanoid: - if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f) + if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f) + { + auto& seq = target.getClass().getCreatureStats(target).getAiSequence(); + seq.erasePackageIf([&](const auto& package) { + return package->getTypeId() == MWMechanics::AiPackageTypeId::Follow + && static_cast(package.get())->isCommanded(); + }); + } + } + else if (effect.mEffectId == ESM::MagicEffect::ExtraSpell) + { + if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f && target != getPlayer()) + target.getClass().getInventoryStore(target).autoEquip(); + } + else if (effect.mEffectId == ESM::MagicEffect::TurnUndead) + { + auto& creatureStats = target.getClass().getCreatureStats(target); + Stat stat = creatureStats.getAiSetting(AiSetting::Flee); + stat.setModifier(static_cast(stat.getModifier() - effect.mMagnitude)); + creatureStats.setAiSetting(AiSetting::Flee, stat); + } + else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature || + effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) + modifyAiSetting(target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, -effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::CalmCreature || + effect.mEffectId == ESM::MagicEffect::CalmHumanoid) + modifyAiSetting(target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature || + effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) + modifyAiSetting( + target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::NightEye) + { + const MWMechanics::EffectParam nightEye = magnitudes.getOrDefault(effect.mEffectId); + if (nightEye.getMagnitude() < 0.f && nightEye.getBase() < 0) + { + // The PCVisionBonus functions are different from every other magic effect function in that they + // clamp the value to [0, 1]. Morrowind.exe applies the same clamping to the night-eye effect, which + // can create situations where an effect is still active (i.e. shown in the menu) but the screen is + // no longer bright. Modifying the base value here should prevent that while preserving their + // function. + float delta = std::clamp(-nightEye.getMagnitude(), 0.f, -static_cast(nightEye.getBase())); + magnitudes.modifyBase(effect.mEffectId, static_cast(delta)); + } + } + else if (effect.mEffectId == ESM::MagicEffect::RallyCreature || + effect.mEffectId == ESM::MagicEffect::RallyHumanoid) + modifyAiSetting(target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::Sound) + { + if (magnitudes.getOrDefault(effect.mEffectId).getModifier() <= 0.f && target == getPlayer()) + MWBase::Environment::get().getSoundManager()->stopSound3D( + target, ESM::RefId::stringRefId("magic sound")); + } + else if (effect.mEffectId == ESM::MagicEffect::SummonScamp || + effect.mEffectId == ESM::MagicEffect::SummonClannfear || + effect.mEffectId == ESM::MagicEffect::SummonDaedroth || + effect.mEffectId == ESM::MagicEffect::SummonDremora || + effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost || + effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion || + effect.mEffectId == ESM::MagicEffect::SummonBonewalker || + effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker || + effect.mEffectId == ESM::MagicEffect::SummonBonelord || + effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight || + effect.mEffectId == ESM::MagicEffect::SummonHunger || + effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint || + effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach || + effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach || + effect.mEffectId == ESM::MagicEffect::SummonStormAtronach || + effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere || + effect.mEffectId == ESM::MagicEffect::SummonFabricant || + effect.mEffectId == ESM::MagicEffect::SummonWolf || + effect.mEffectId == ESM::MagicEffect::SummonBear || + effect.mEffectId == ESM::MagicEffect::SummonBonewolf || + effect.mEffectId == ESM::MagicEffect::SummonCreature04 || + effect.mEffectId == ESM::MagicEffect::SummonCreature05) + { + 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 == actor) { - auto& seq = target.getClass().getCreatureStats(target).getAiSequence(); - seq.erasePackageIf([&](const auto& package) { - return package->getTypeId() == MWMechanics::AiPackageTypeId::Follow - && static_cast(package.get())->isCommanded(); - }); + summons.erase(it); + break; } - break; - case ESM::MagicEffect::ExtraSpell: - if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f && target != getPlayer()) - target.getClass().getInventoryStore(target).autoEquip(); - break; - case ESM::MagicEffect::TurnUndead: + } + } + else if (effect.mEffectId == ESM::MagicEffect::BoundGloves) + { + removeBoundItem( + ESM::RefId::stringRefId( + world->getStore().get().find("sMagicBoundRightGauntletID")->mValue.getString()), + target); + removeBoundItem( + ESM::RefId::stringRefId( + world->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString()), + target); + } + else if (effect.mEffectId == ESM::MagicEffect::BoundDagger || + effect.mEffectId == ESM::MagicEffect::BoundLongsword || + effect.mEffectId == ESM::MagicEffect::BoundMace || + effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || + effect.mEffectId == ESM::MagicEffect::BoundSpear || + effect.mEffectId == ESM::MagicEffect::BoundLongbow || + effect.mEffectId == ESM::MagicEffect::BoundCuirass || + effect.mEffectId == ESM::MagicEffect::BoundHelm || + effect.mEffectId == ESM::MagicEffect::BoundBoots || + effect.mEffectId == ESM::MagicEffect::BoundShield) + { + const std::string& item = sBoundItemsMap.at(effect.mEffectId); + removeBoundItem( + ESM::RefId::stringRefId(world->getStore().get().find(item)->mValue.getString()), + target); + } + else if (effect.mEffectId == ESM::MagicEffect::DrainHealth) + adjustDynamicStat(target, Stats::Health, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::DrainMagicka) + adjustDynamicStat(target, Stats::Magicka, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::DrainFatigue) + adjustDynamicStat(target, Stats::Fatigue, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::FortifyHealth) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Health, -effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Health, -effect.mMagnitude, true); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyMagicka) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Magicka, -effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Magicka, -effect.mMagnitude, true); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyFatigue) + { + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) + modDynamicStat(target, Stats::Fatigue, -effect.mMagnitude); + else + adjustDynamicStat(target, Stats::Fatigue, -effect.mMagnitude, true); + } + else if (effect.mEffectId == ESM::MagicEffect::DrainAttribute) + restoreAttribute(target, effect, effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::FortifyAttribute) + { + // Abilities affect base stats, but not for drain + if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) { auto& creatureStats = target.getClass().getCreatureStats(target); - Stat stat = creatureStats.getAiSetting(AiSetting::Flee); - stat.setModifier(static_cast(stat.getModifier() - effect.mMagnitude)); - creatureStats.setAiSetting(AiSetting::Flee, stat); + auto attribute = effect.getSkillOrAttribute(); + AttributeValue attr = creatureStats.getAttribute(attribute); + attr.setBase(attr.getBase() - effect.mMagnitude); + creatureStats.setAttribute(attribute, attr); } - break; - case ESM::MagicEffect::FrenzyCreature: - case ESM::MagicEffect::FrenzyHumanoid: - modifyAiSetting(target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, -effect.mMagnitude); - break; - case ESM::MagicEffect::CalmCreature: - case ESM::MagicEffect::CalmHumanoid: - modifyAiSetting(target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, effect.mMagnitude); - break; - case ESM::MagicEffect::DemoralizeCreature: - case ESM::MagicEffect::DemoralizeHumanoid: - modifyAiSetting( - target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude); - break; - case ESM::MagicEffect::NightEye: + else + fortifyAttribute(target, effect, -effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::DrainSkill) + { + if (target.getClass().isNpc()) + restoreSkill(target, effect, effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::FortifySkill) + { + if (target.getClass().isNpc()) { - const MWMechanics::EffectParam nightEye = magnitudes.getOrDefault(effect.mEffectId); - if (nightEye.getMagnitude() < 0.f && nightEye.getBase() < 0) - { - // The PCVisionBonus functions are different from every other magic effect function in that they - // clamp the value to [0, 1]. Morrowind.exe applies the same clamping to the night-eye effect, which - // can create situations where an effect is still active (i.e. shown in the menu) but the screen is - // no longer bright. Modifying the base value here should prevent that while preserving their - // function. - float delta = std::clamp(-nightEye.getMagnitude(), 0.f, -static_cast(nightEye.getBase())); - magnitudes.modifyBase(effect.mEffectId, static_cast(delta)); - } - } - break; - case ESM::MagicEffect::RallyCreature: - case ESM::MagicEffect::RallyHumanoid: - modifyAiSetting(target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude); - break; - case ESM::MagicEffect::Sound: - if (magnitudes.getOrDefault(effect.mEffectId).getModifier() <= 0.f && target == getPlayer()) - MWBase::Environment::get().getSoundManager()->stopSound3D( - target, ESM::RefId::stringRefId("magic sound")); - break; - case ESM::MagicEffect::SummonScamp: - case ESM::MagicEffect::SummonClannfear: - case ESM::MagicEffect::SummonDaedroth: - case ESM::MagicEffect::SummonDremora: - case ESM::MagicEffect::SummonAncestralGhost: - case ESM::MagicEffect::SummonSkeletalMinion: - case ESM::MagicEffect::SummonBonewalker: - case ESM::MagicEffect::SummonGreaterBonewalker: - case ESM::MagicEffect::SummonBonelord: - case ESM::MagicEffect::SummonWingedTwilight: - case ESM::MagicEffect::SummonHunger: - case ESM::MagicEffect::SummonGoldenSaint: - case ESM::MagicEffect::SummonFlameAtronach: - case ESM::MagicEffect::SummonFrostAtronach: - case ESM::MagicEffect::SummonStormAtronach: - case ESM::MagicEffect::SummonCenturionSphere: - case ESM::MagicEffect::SummonFabricant: - case ESM::MagicEffect::SummonWolf: - case ESM::MagicEffect::SummonBear: - case ESM::MagicEffect::SummonBonewolf: - case ESM::MagicEffect::SummonCreature04: - case ESM::MagicEffect::SummonCreature05: - { - 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 == actor) - { - summons.erase(it); - break; - } - } - } - break; - case ESM::MagicEffect::BoundGloves: - removeBoundItem(ESM::RefId::stringRefId(world->getStore() - .get() - .find("sMagicBoundRightGauntletID") - ->mValue.getString()), - target); - [[fallthrough]]; - case ESM::MagicEffect::BoundDagger: - case ESM::MagicEffect::BoundLongsword: - case ESM::MagicEffect::BoundMace: - case ESM::MagicEffect::BoundBattleAxe: - case ESM::MagicEffect::BoundSpear: - case ESM::MagicEffect::BoundLongbow: - case ESM::MagicEffect::BoundCuirass: - case ESM::MagicEffect::BoundHelm: - case ESM::MagicEffect::BoundBoots: - case ESM::MagicEffect::BoundShield: - { - const std::string& item = sBoundItemsMap.at(effect.mEffectId); - removeBoundItem( - ESM::RefId::stringRefId(world->getStore().get().find(item)->mValue.getString()), - target); - } - break; - case ESM::MagicEffect::DrainHealth: - case ESM::MagicEffect::DrainMagicka: - case ESM::MagicEffect::DrainFatigue: - adjustDynamicStat(target, effect.mEffectId - ESM::MagicEffect::DrainHealth, effect.mMagnitude); - break; - case ESM::MagicEffect::FortifyHealth: - case ESM::MagicEffect::FortifyMagicka: - case ESM::MagicEffect::FortifyFatigue: - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - modDynamicStat(target, effect.mEffectId - ESM::MagicEffect::FortifyHealth, -effect.mMagnitude); - else - adjustDynamicStat( - target, effect.mEffectId - ESM::MagicEffect::FortifyHealth, -effect.mMagnitude, true); - break; - case ESM::MagicEffect::DrainAttribute: - restoreAttribute(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::FortifyAttribute: // Abilities affect base stats, but not for drain if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) { - auto& creatureStats = target.getClass().getCreatureStats(target); - auto attribute = effect.getSkillOrAttribute(); - AttributeValue attr = creatureStats.getAttribute(attribute); - attr.setBase(attr.getBase() - effect.mMagnitude); - creatureStats.setAttribute(attribute, attr); + auto& npcStats = target.getClass().getNpcStats(target); + auto& skill = npcStats.getSkill(effect.getSkillOrAttribute()); + skill.setBase(skill.getBase() - effect.mMagnitude); } else - fortifyAttribute(target, effect, -effect.mMagnitude); - break; - case ESM::MagicEffect::DrainSkill: - if (target.getClass().isNpc()) - restoreSkill(target, effect, effect.mMagnitude); - break; - case ESM::MagicEffect::FortifySkill: - if (target.getClass().isNpc()) - { - // Abilities affect base stats, but not for drain - if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - { - auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.getSkillOrAttribute()); - skill.setBase(skill.getBase() - effect.mMagnitude); - } - else - fortifySkill(target, effect, -effect.mMagnitude); - } - break; - case ESM::MagicEffect::FortifyMaximumMagicka: - target.getClass().getCreatureStats(target).recalculateMagicka(); - break; - case ESM::MagicEffect::AbsorbAttribute: - { - const auto caster = worldModel->getPtr(spellParams.getCaster()); - restoreAttribute(target, effect, effect.mMagnitude); - if (!caster.isEmpty()) - fortifyAttribute(caster, effect, -effect.mMagnitude); + fortifySkill(target, effect, -effect.mMagnitude); } - break; - case ESM::MagicEffect::AbsorbSkill: + } + else if (effect.mEffectId == ESM::MagicEffect::FortifyMaximumMagicka) + target.getClass().getCreatureStats(target).recalculateMagicka(); + else if (effect.mEffectId == ESM::MagicEffect::AbsorbAttribute) + { + const auto caster = worldModel->getPtr(spellParams.getCaster()); + restoreAttribute(target, effect, effect.mMagnitude); + if (!caster.isEmpty()) + fortifyAttribute(caster, effect, -effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::AbsorbSkill) + { + if (target.getClass().isNpc()) + restoreSkill(target, effect, effect.mMagnitude); + const auto caster = worldModel->getPtr(spellParams.getCaster()); + if (!caster.isEmpty() && caster.getClass().isNpc()) + fortifySkill(caster, effect, -effect.mMagnitude); + } + else if (effect.mEffectId == ESM::MagicEffect::Corprus) + { + int worsenings = spellParams.getWorsenings(); + spellParams.resetWorsenings(); + if (worsenings > 0) { - if (target.getClass().isNpc()) - restoreSkill(target, effect, effect.mMagnitude); - const auto caster = worldModel->getPtr(spellParams.getCaster()); - if (!caster.isEmpty() && caster.getClass().isNpc()) - fortifySkill(caster, effect, -effect.mMagnitude); - } - break; - case ESM::MagicEffect::Corprus: - { - int worsenings = spellParams.getWorsenings(); - spellParams.resetWorsenings(); - if (worsenings > 0) + for (const auto& otherEffect : spellParams.getEffects()) { - for (const auto& otherEffect : spellParams.getEffects()) + if (isCorprusEffect(otherEffect, true)) { - if (isCorprusEffect(otherEffect, true)) - { - for (int i = 0; i < worsenings; i++) - removeMagicEffect(target, spellParams, otherEffect); - } + for (int i = 0; i < worsenings; i++) + removeMagicEffect(target, spellParams, otherEffect); } } - // Note that we remove the effects, but keep the params - target.getClass().getCreatureStats(target).getActiveSpells().purge( - [&spellParams]( - const ActiveSpells::ActiveSpellParams& params, const auto&) { return &spellParams == ¶ms; }, - target); } - break; + // Note that we remove the effects, but keep the params + target.getClass().getCreatureStats(target).getActiveSpells().purge( + [&spellParams]( + const ActiveSpells::ActiveSpellParams& params, const auto&) { return &spellParams == ¶ms; }, + target); } } @@ -1338,7 +1398,7 @@ namespace MWMechanics { auto anim = MWBase::Environment::get().getWorld()->getAnimation(target); if (anim) - anim->removeEffect(ESM::MagicEffect::indexToName(effect.mEffectId)); + anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); } } diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 1e811a0658..9c736c0dec 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -23,7 +23,14 @@ namespace { - int numEffectsToDispel(const MWWorld::Ptr& actor, int effectFilter = -1, bool negative = true) + enum Stats + { + Health = 0, + Magicka = 1, + Fatigue = 2 + }; + + int numEffectsToDispel(const MWWorld::Ptr& actor, const ESM::RefId& effectFilter = ESM::RefId(), bool negative = true) { int toCure = 0; const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells(); @@ -31,7 +38,7 @@ namespace { // if the effect filter is not specified, take in account only spells effects. Leave potions, enchanted // items etc. - if (effectFilter == -1) + if (effectFilter.empty()) { const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->get().search(it->getSourceSpellId()); @@ -42,8 +49,8 @@ namespace const MWMechanics::ActiveSpells::ActiveSpellParams& params = *it; for (const auto& effect : params.getEffects()) { - int effectId = effect.mEffectId; - if (effectFilter != -1 && effectId != effectFilter) + ESM::RefId effectId = effect.mEffectId; + if (!effectFilter.empty() && effectId != effectFilter) continue; const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getESMStore()->get().find(effectId); @@ -207,404 +214,381 @@ namespace MWMechanics // NOTE: enemy may be empty float rating = 1; - switch (effect.mEffectID) + if (effect.mEffectID == ESM::MagicEffect::Soultrap || + effect.mEffectID == ESM::MagicEffect::AlmsiviIntervention || + effect.mEffectID == ESM::MagicEffect::DivineIntervention || + effect.mEffectID == ESM::MagicEffect::CalmHumanoid || + effect.mEffectID == ESM::MagicEffect::CalmCreature || + effect.mEffectID == ESM::MagicEffect::FrenzyHumanoid || + effect.mEffectID == ESM::MagicEffect::FrenzyCreature || + effect.mEffectID == ESM::MagicEffect::DemoralizeHumanoid || + effect.mEffectID == ESM::MagicEffect::DemoralizeCreature || + effect.mEffectID == ESM::MagicEffect::RallyHumanoid || + effect.mEffectID == ESM::MagicEffect::RallyCreature || + effect.mEffectID == ESM::MagicEffect::Charm || + effect.mEffectID == ESM::MagicEffect::DetectAnimal || + effect.mEffectID == ESM::MagicEffect::DetectEnchantment || + effect.mEffectID == ESM::MagicEffect::DetectKey || + effect.mEffectID == ESM::MagicEffect::Telekinesis || + effect.mEffectID == ESM::MagicEffect::Mark || + effect.mEffectID == ESM::MagicEffect::Recall || + effect.mEffectID == ESM::MagicEffect::Jump || + effect.mEffectID == ESM::MagicEffect::WaterBreathing || + effect.mEffectID == ESM::MagicEffect::SwiftSwim || + effect.mEffectID == ESM::MagicEffect::WaterWalking || + effect.mEffectID == ESM::MagicEffect::SlowFall || + effect.mEffectID == ESM::MagicEffect::Light || + effect.mEffectID == ESM::MagicEffect::Lock || + effect.mEffectID == ESM::MagicEffect::Open || + effect.mEffectID == ESM::MagicEffect::TurnUndead || + effect.mEffectID == ESM::MagicEffect::WeaknessToCommonDisease || + effect.mEffectID == ESM::MagicEffect::WeaknessToBlightDisease || + effect.mEffectID == ESM::MagicEffect::WeaknessToCorprusDisease || + effect.mEffectID == ESM::MagicEffect::CureCommonDisease || + effect.mEffectID == ESM::MagicEffect::CureBlightDisease || + effect.mEffectID == ESM::MagicEffect::CureCorprusDisease || + effect.mEffectID == ESM::MagicEffect::ResistBlightDisease || + effect.mEffectID == ESM::MagicEffect::ResistCommonDisease || + effect.mEffectID == ESM::MagicEffect::ResistCorprusDisease || + effect.mEffectID == ESM::MagicEffect::Invisibility || + effect.mEffectID == ESM::MagicEffect::Chameleon || + effect.mEffectID == ESM::MagicEffect::NightEye || + effect.mEffectID == ESM::MagicEffect::Vampirism || + effect.mEffectID == ESM::MagicEffect::StuntedMagicka || + effect.mEffectID == ESM::MagicEffect::ExtraSpell || + effect.mEffectID == ESM::MagicEffect::RemoveCurse || + effect.mEffectID == ESM::MagicEffect::CommandCreature || + effect.mEffectID == ESM::MagicEffect::CommandHumanoid) + return 0.f; + else if (effect.mEffectID == ESM::MagicEffect::Blind) { - case ESM::MagicEffect::Soultrap: - case ESM::MagicEffect::AlmsiviIntervention: - case ESM::MagicEffect::DivineIntervention: - case ESM::MagicEffect::CalmHumanoid: - case ESM::MagicEffect::CalmCreature: - case ESM::MagicEffect::FrenzyHumanoid: - case ESM::MagicEffect::FrenzyCreature: - case ESM::MagicEffect::DemoralizeHumanoid: - case ESM::MagicEffect::DemoralizeCreature: - case ESM::MagicEffect::RallyHumanoid: - case ESM::MagicEffect::RallyCreature: - case ESM::MagicEffect::Charm: - case ESM::MagicEffect::DetectAnimal: - case ESM::MagicEffect::DetectEnchantment: - case ESM::MagicEffect::DetectKey: - case ESM::MagicEffect::Telekinesis: - case ESM::MagicEffect::Mark: - case ESM::MagicEffect::Recall: - case ESM::MagicEffect::Jump: - case ESM::MagicEffect::WaterBreathing: - case ESM::MagicEffect::SwiftSwim: - case ESM::MagicEffect::WaterWalking: - case ESM::MagicEffect::SlowFall: - case ESM::MagicEffect::Light: - case ESM::MagicEffect::Lock: - case ESM::MagicEffect::Open: - case ESM::MagicEffect::TurnUndead: - case ESM::MagicEffect::WeaknessToCommonDisease: - case ESM::MagicEffect::WeaknessToBlightDisease: - case ESM::MagicEffect::WeaknessToCorprusDisease: - case ESM::MagicEffect::CureCommonDisease: - case ESM::MagicEffect::CureBlightDisease: - case ESM::MagicEffect::CureCorprusDisease: - case ESM::MagicEffect::ResistBlightDisease: - case ESM::MagicEffect::ResistCommonDisease: - case ESM::MagicEffect::ResistCorprusDisease: - case ESM::MagicEffect::Invisibility: - case ESM::MagicEffect::Chameleon: - case ESM::MagicEffect::NightEye: - case ESM::MagicEffect::Vampirism: - case ESM::MagicEffect::StuntedMagicka: - case ESM::MagicEffect::ExtraSpell: - case ESM::MagicEffect::RemoveCurse: - case ESM::MagicEffect::CommandCreature: - case ESM::MagicEffect::CommandHumanoid: + if (enemy.isEmpty()) return 0.f; - case ESM::MagicEffect::Blind: - { - if (enemy.isEmpty()) - return 0.f; + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); - const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + // Enemy can't attack + if (stats.isParalyzed() || stats.getKnockedDown()) + return 0.f; - // Enemy can't attack - if (stats.isParalyzed() || stats.getKnockedDown()) - return 0.f; + // Enemy doesn't attack + if (stats.getDrawState() != MWMechanics::DrawState::Weapon) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::Sound) + { + if (enemy.isEmpty()) + return 0.f; - // Enemy doesn't attack - if (stats.getDrawState() != MWMechanics::DrawState::Weapon) - return 0.f; + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); - break; - } + // Enemy can't cast spells + if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude() > 0) + return 0.f; - case ESM::MagicEffect::Sound: - { - if (enemy.isEmpty()) - return 0.f; + if (stats.isParalyzed() || stats.getKnockedDown()) + return 0.f; - const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState::Spell) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::Silence) + { + if (enemy.isEmpty()) + return 0.f; - // Enemy can't cast spells - if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Silence).getMagnitude() > 0) - return 0.f; + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); - if (stats.isParalyzed() || stats.getKnockedDown()) - return 0.f; + // Enemy can't cast spells + if (stats.isParalyzed() || stats.getKnockedDown()) + return 0.f; - // Enemy doesn't cast spells - if (stats.getDrawState() != MWMechanics::DrawState::Spell) - return 0.f; - - break; - } - - case ESM::MagicEffect::Silence: - { - if (enemy.isEmpty()) - return 0.f; - - const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); - - // Enemy can't cast spells - if (stats.isParalyzed() || stats.getKnockedDown()) - return 0.f; - - // Enemy doesn't cast spells - if (stats.getDrawState() != MWMechanics::DrawState::Spell) - return 0.f; - break; - } - - case ESM::MagicEffect::RestoreAttribute: - return 0.f; // TODO: implement based on attribute damage - case ESM::MagicEffect::RestoreSkill: - return 0.f; // TODO: implement based on skill damage - - case ESM::MagicEffect::ResistFire: - case ESM::MagicEffect::ResistFrost: - case ESM::MagicEffect::ResistMagicka: - case ESM::MagicEffect::ResistNormalWeapons: - case ESM::MagicEffect::ResistParalysis: - case ESM::MagicEffect::ResistPoison: - case ESM::MagicEffect::ResistShock: - case ESM::MagicEffect::SpellAbsorption: - case ESM::MagicEffect::Reflect: + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState::Spell) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::RestoreAttribute) + return 0.f; // TODO: implement based on attribute damage + else if (effect.mEffectID == ESM::MagicEffect::RestoreSkill) + return 0.f; // TODO: implement based on skill damage + else if (effect.mEffectID == ESM::MagicEffect::ResistFire || + effect.mEffectID == ESM::MagicEffect::ResistFrost || + effect.mEffectID == ESM::MagicEffect::ResistMagicka || + effect.mEffectID == ESM::MagicEffect::ResistNormalWeapons || + effect.mEffectID == ESM::MagicEffect::ResistParalysis || + effect.mEffectID == ESM::MagicEffect::ResistPoison || + effect.mEffectID == ESM::MagicEffect::ResistShock || + effect.mEffectID == ESM::MagicEffect::SpellAbsorption || + effect.mEffectID == ESM::MagicEffect::Reflect) return 0.f; // probably useless since we don't know in advance what the enemy will cast // don't cast these for now as they would make the NPC cast the same effect over and over again, especially // when they have potions - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::FortifyHealth: - case ESM::MagicEffect::FortifyMagicka: - case ESM::MagicEffect::FortifyFatigue: - case ESM::MagicEffect::FortifySkill: - case ESM::MagicEffect::FortifyMaximumMagicka: - case ESM::MagicEffect::FortifyAttack: + else if (effect.mEffectID == ESM::MagicEffect::FortifyAttribute || + effect.mEffectID == ESM::MagicEffect::FortifyHealth || + effect.mEffectID == ESM::MagicEffect::FortifyMagicka || + effect.mEffectID == ESM::MagicEffect::FortifyFatigue || + effect.mEffectID == ESM::MagicEffect::FortifySkill || + effect.mEffectID == ESM::MagicEffect::FortifyMaximumMagicka || + effect.mEffectID == ESM::MagicEffect::FortifyAttack) + return 0.f; + else if (effect.mEffectID == ESM::MagicEffect::Burden) + { + if (enemy.isEmpty()) return 0.f; - case ESM::MagicEffect::Burden: + // Ignore enemy without inventory + if (!enemy.getClass().hasInventoryStore(enemy)) + return 0.f; + + // burden makes sense only to overburden an enemy + float burden = enemy.getClass().getEncumbrance(enemy) - enemy.getClass().getCapacity(enemy); + if (burden > 0) + return 0.f; + + if ((effect.mMagnMin + effect.mMagnMax) / 2.f > -burden) + rating *= 3; + else + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::Feather) + { + // Ignore actors without inventory + if (!actor.getClass().hasInventoryStore(actor)) + return 0.f; + + // feather makes sense only for overburden actors + float burden = actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor); + if (burden <= 0) + return 0.f; + + if ((effect.mMagnMin + effect.mMagnMax) / 2.f >= burden) + rating *= 3; + else + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::Levitate) + return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway + else if (effect.mEffectID == ESM::MagicEffect::BoundBoots || effect.mEffectID == ESM::MagicEffect::BoundHelm) + { + if (actor.getClass().isNpc()) + { + // Beast races can't wear helmets or boots + const ESM::RefId& raceid = actor.get()->mBase->mRace; + const ESM::Race* race = MWBase::Environment::get().getESMStore()->get().find(raceid); + if (race->mData.mFlags & ESM::Race::Beast) + return 0.f; + } + else + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::BoundShield) + { + if (!actor.getClass().hasInventoryStore(actor)) + return 0.f; + else if (!actor.getClass().isNpc()) + { + // If the actor is an NPC they can benefit from the armor rating, otherwise check if we've got a + // one-handed weapon to use with the shield + const auto& store = actor.getClass().getInventoryStore(actor); + auto oneHanded = std::find_if(store.cbegin(MWWorld::ContainerStore::Type_Weapon), store.cend(), + [](const MWWorld::ConstPtr& weapon) { + if (weapon.getClass().getItemHealth(weapon) <= 0.f) + return false; + short type = weapon.get()->mBase->mData.mType; + return !(MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::TwoHanded); + }); + if (oneHanded == store.cend()) + return 0.f; + } + } + // Creatures cannot wear armor + else if (effect.mEffectID == ESM::MagicEffect::BoundCuirass + || effect.mEffectID == ESM::MagicEffect::BoundGloves) + { + if (!actor.getClass().isNpc()) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::AbsorbMagicka) + { + if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getMagicka().getCurrent() <= 0.f) + { + rating = 0.5f; + rating *= getRestoreMagickaPriority(actor); + } + } + else if (effect.mEffectID == ESM::MagicEffect::RestoreHealth + || effect.mEffectID == ESM::MagicEffect::RestoreMagicka + || effect.mEffectID == ESM::MagicEffect::RestoreFatigue) + { + if (effect.mRange == ESM::RT_Self) + { + int index = -1; + if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) + index = Stats::Health; + else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka) + index = Stats::Magicka; + else if (effect.mEffectID == ESM::MagicEffect::RestoreFatigue) + index = Stats::Fatigue; + + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const DynamicStat& current + = stats.getDynamic(index); + // NB: this currently assumes the hardcoded magic effect flags are used + const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f; + const float toHeal = magnitude * std::max(1, effect.mDuration); + const float damage = std::max(current.getModified() - current.getCurrent(), 0.f); + float priority = 0.f; + if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) + priority = 4.f; + else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka) + priority = getRestoreMagickaPriority(actor); + else if (effect.mEffectID == ESM::MagicEffect::RestoreFatigue) + priority = 2.f; + float overheal = 0.f; + float heal = toHeal; + if (damage < toHeal && current.getCurrent() > current.getModified() * 0.5) + { + overheal = toHeal - damage; + heal = damage; + } + + priority = (priority - 1.f) / 2.f * std::pow((damage / current.getModified() + 0.6f), priority * 2) + + priority * (heal - 2.f * overheal) / current.getModified() - 0.5f; + rating = priority; + } + } + else if (effect.mEffectID == ESM::MagicEffect::Dispel) + { + int numPositive = 0; + int numNegative = 0; + int diff = 0; + + if (effect.mRange == ESM::RT_Self) + { + numPositive = numEffectsToDispel(actor, ESM::RefId(), false); + numNegative = numEffectsToDispel(actor); + + diff = numNegative - numPositive; + } + else { if (enemy.isEmpty()) return 0.f; - // Ignore enemy without inventory - if (!enemy.getClass().hasInventoryStore(enemy)) - return 0.f; + numPositive = numEffectsToDispel(enemy, ESM::RefId(), false); + numNegative = numEffectsToDispel(enemy); - // burden makes sense only to overburden an enemy - float burden = enemy.getClass().getEncumbrance(enemy) - enemy.getClass().getCapacity(enemy); - if (burden > 0) - return 0.f; + diff = numPositive - numNegative; - if ((effect.mMagnMin + effect.mMagnMax) / 2.f > -burden) - rating *= 3; - else - return 0.f; - - break; + // if rating < 0 here, the spell will be considered as negative later + rating *= -1; } - case ESM::MagicEffect::Feather: + if (diff <= 0) + return 0.f; + + rating *= (diff) / 5.f; + } + + // Prefer Cure effects over Dispel, because Dispel also removes positive effects + else if (effect.mEffectID == ESM::MagicEffect::CureParalyzation) + return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Paralyze); + else if (effect.mEffectID == ESM::MagicEffect::CurePoison) + return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Poison); + else if (effect.mEffectID == ESM::MagicEffect::DisintegrateArmor) + { + if (enemy.isEmpty()) + return 0.f; + + // Ignore enemy without inventory + if (!enemy.getClass().hasInventoryStore(enemy)) + return 0.f; + + MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy); + + // According to UESP + static const int armorSlots[] = { + MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, + MWWorld::InventoryStore::Slot_RightGauntlet, + MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots, + }; + + bool enemyHasArmor = false; + + // Ignore enemy without armor + for (unsigned int i = 0; i < sizeof(armorSlots) / sizeof(int); ++i) { - // Ignore actors without inventory - if (!actor.getClass().hasInventoryStore(actor)) - return 0.f; + MWWorld::ContainerStoreIterator item = inv.getSlot(armorSlots[i]); - // feather makes sense only for overburden actors - float burden = actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor); - if (burden <= 0) - return 0.f; - - if ((effect.mMagnMin + effect.mMagnMax) / 2.f >= burden) - rating *= 3; - else - return 0.f; - - break; + if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor)) + { + enemyHasArmor = true; + break; + } } - case ESM::MagicEffect::Levitate: - return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway - case ESM::MagicEffect::BoundBoots: - case ESM::MagicEffect::BoundHelm: - if (actor.getClass().isNpc()) - { - // Beast races can't wear helmets or boots - const ESM::RefId& raceid = actor.get()->mBase->mRace; - const ESM::Race* race = MWBase::Environment::get().getESMStore()->get().find(raceid); - if (race->mData.mFlags & ESM::Race::Beast) - return 0.f; - } - else - return 0.f; + if (!enemyHasArmor) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::DisintegrateWeapon) + { + if (enemy.isEmpty()) + return 0.f; - break; - case ESM::MagicEffect::BoundShield: - if (!actor.getClass().hasInventoryStore(actor)) - return 0.f; - else if (!actor.getClass().isNpc()) - { - // If the actor is an NPC they can benefit from the armor rating, otherwise check if we've got a - // one-handed weapon to use with the shield - const auto& store = actor.getClass().getInventoryStore(actor); - auto oneHanded = std::find_if(store.cbegin(MWWorld::ContainerStore::Type_Weapon), store.cend(), - [](const MWWorld::ConstPtr& weapon) { - if (weapon.getClass().getItemHealth(weapon) <= 0.f) - return false; - short type = weapon.get()->mBase->mData.mType; - return !(MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::TwoHanded); - }); - if (oneHanded == store.cend()) - return 0.f; - } - break; - // Creatures can not wear armor - case ESM::MagicEffect::BoundCuirass: - case ESM::MagicEffect::BoundGloves: - if (!actor.getClass().isNpc()) - return 0.f; - break; + // Ignore enemy without inventory + if (!enemy.getClass().hasInventoryStore(enemy)) + return 0.f; - case ESM::MagicEffect::AbsorbMagicka: - if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getMagicka().getCurrent() <= 0.f) - { - rating = 0.5f; - rating *= getRestoreMagickaPriority(actor); - } - break; - case ESM::MagicEffect::RestoreHealth: - case ESM::MagicEffect::RestoreMagicka: - case ESM::MagicEffect::RestoreFatigue: - if (effect.mRange == ESM::RT_Self) - { - const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - const DynamicStat& current - = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); - // NB: this currently assumes the hardcoded magic effect flags are used - const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f; - const float toHeal = magnitude * std::max(1, effect.mDuration); - const float damage = std::max(current.getModified() - current.getCurrent(), 0.f); - float priority = 0.f; - if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) - priority = 4.f; - else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka) - priority = getRestoreMagickaPriority(actor); - else if (effect.mEffectID == ESM::MagicEffect::RestoreFatigue) - priority = 2.f; - float overheal = 0.f; - float heal = toHeal; - if (damage < toHeal && current.getCurrent() > current.getModified() * 0.5) - { - overheal = toHeal - damage; - heal = damage; - } + MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy); + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - priority = (priority - 1.f) / 2.f * std::pow((damage / current.getModified() + 0.6f), priority * 2) - + priority * (heal - 2.f * overheal) / current.getModified() - 0.5f; - rating = priority; - } - break; - - case ESM::MagicEffect::Dispel: + // Ignore enemy without weapons + if (item == inv.end() || (item.getType() != MWWorld::ContainerStore::Type_Weapon)) + return 0.f; + } + else if (effect.mEffectID == ESM::MagicEffect::AbsorbAttribute + || effect.mEffectID == ESM::MagicEffect::DamageAttribute + || effect.mEffectID == ESM::MagicEffect::DrainAttribute) + { + if (!enemy.isEmpty() + && enemy.getClass() + .getCreatureStats(enemy) + .getAttribute(ESM::Attribute::indexToRefId(effect.mAttribute)) + .getModified() + <= 0) + return 0.f; { - int numPositive = 0; - int numNegative = 0; - int diff = 0; - - if (effect.mRange == ESM::RT_Self) + if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length) { - numPositive = numEffectsToDispel(actor, -1, false); - numNegative = numEffectsToDispel(actor); - - diff = numNegative - numPositive; + const float attributePriorities[ESM::Attribute::Length] = { + 1.0f, // Strength + 0.5f, // Intelligence + 0.6f, // Willpower + 0.7f, // Agility + 0.5f, // Speed + 0.8f, // Endurance + 0.7f, // Personality + 0.3f // Luck + }; + rating *= attributePriorities[effect.mAttribute]; } - else - { - if (enemy.isEmpty()) - return 0.f; - - numPositive = numEffectsToDispel(enemy, -1, false); - numNegative = numEffectsToDispel(enemy); - - diff = numPositive - numNegative; - - // if rating < 0 here, the spell will be considered as negative later - rating *= -1; - } - - if (diff <= 0) - return 0.f; - - rating *= (diff) / 5.f; - - break; } - - // Prefer Cure effects over Dispel, because Dispel also removes positive effects - case ESM::MagicEffect::CureParalyzation: - return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Paralyze); - - case ESM::MagicEffect::CurePoison: - return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Poison); - case ESM::MagicEffect::DisintegrateArmor: - { - if (enemy.isEmpty()) - return 0.f; - - // Ignore enemy without inventory - if (!enemy.getClass().hasInventoryStore(enemy)) - return 0.f; - - MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy); - - // According to UESP - static const int armorSlots[] = { - MWWorld::InventoryStore::Slot_CarriedLeft, - MWWorld::InventoryStore::Slot_Cuirass, - MWWorld::InventoryStore::Slot_LeftPauldron, - MWWorld::InventoryStore::Slot_RightPauldron, - MWWorld::InventoryStore::Slot_LeftGauntlet, - MWWorld::InventoryStore::Slot_RightGauntlet, - MWWorld::InventoryStore::Slot_Helmet, - MWWorld::InventoryStore::Slot_Greaves, - MWWorld::InventoryStore::Slot_Boots, - }; - - bool enemyHasArmor = false; - - // Ignore enemy without armor - for (unsigned int i = 0; i < sizeof(armorSlots) / sizeof(int); ++i) - { - MWWorld::ContainerStoreIterator item = inv.getSlot(armorSlots[i]); - - if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor)) - { - enemyHasArmor = true; - break; - } - } - - if (!enemyHasArmor) - return 0.f; - - break; - } - - case ESM::MagicEffect::DisintegrateWeapon: - { - if (enemy.isEmpty()) - return 0.f; - - // Ignore enemy without inventory - if (!enemy.getClass().hasInventoryStore(enemy)) - return 0.f; - - MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy); - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - - // Ignore enemy without weapons - if (item == inv.end() || (item.getType() != MWWorld::ContainerStore::Type_Weapon)) - return 0.f; - - break; - } - - case ESM::MagicEffect::AbsorbAttribute: - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::DrainAttribute: - if (!enemy.isEmpty() - && enemy.getClass() - .getCreatureStats(enemy) - .getAttribute(ESM::Attribute::indexToRefId(effect.mAttribute)) - .getModified() - <= 0) - return 0.f; - { - if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length) - { - const float attributePriorities[ESM::Attribute::Length] = { - 1.0f, // Strength - 0.5f, // Intelligence - 0.6f, // Willpower - 0.7f, // Agility - 0.5f, // Speed - 0.8f, // Endurance - 0.7f, // Personality - 0.3f // Luck - }; - rating *= attributePriorities[effect.mAttribute]; - } - } - break; - - case ESM::MagicEffect::AbsorbSkill: - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::DrainSkill: - if (enemy.isEmpty() || !enemy.getClass().isNpc()) - return 0.f; - if (enemy.getClass().getSkill(enemy, ESM::Skill::indexToRefId(effect.mSkill)) <= 0) - return 0.f; - break; - - default: - break; + } + else if (effect.mEffectID == ESM::MagicEffect::AbsorbSkill || effect.mEffectID == ESM::MagicEffect::DamageSkill + || effect.mEffectID == ESM::MagicEffect::DrainSkill) + { + if (enemy.isEmpty() || !enemy.getClass().isNpc()) + return 0.f; + if (enemy.getClass().getSkill(enemy, ESM::Skill::indexToRefId(effect.mSkill)) <= 0) + return 0.f; } // Allow only one summoned creature at time @@ -617,7 +601,27 @@ namespace MWMechanics // But rate summons higher than other effects rating = 3.f; } - if (effect.mEffectID >= ESM::MagicEffect::BoundDagger && effect.mEffectID <= ESM::MagicEffect::BoundGloves) + + static const std::array boundWeapons{ + ESM::MagicEffect::BoundDagger, + ESM::MagicEffect::BoundLongsword, + ESM::MagicEffect::BoundMace, + ESM::MagicEffect::BoundBattleAxe, + ESM::MagicEffect::BoundSpear, + ESM::MagicEffect::BoundLongbow, + }; + + static const std::array boundArmor{ + ESM::MagicEffect::ExtraSpell, + ESM::MagicEffect::BoundCuirass, + ESM::MagicEffect::BoundHelm, + ESM::MagicEffect::BoundBoots, + ESM::MagicEffect::BoundShield, + ESM::MagicEffect::BoundGloves, + }; + + if (std::ranges::find(boundWeapons, effect.mEffectID) != boundWeapons.end() || + std::ranges::find(boundArmor, effect.mEffectID) != boundArmor.end()) { // Prefer casting bound items over other spells rating = 2.f; @@ -627,9 +631,9 @@ namespace MWMechanics // summon another of a different kind unless what we have is a bow and the actor is out of ammo. // FIXME: This code assumes the summoned item is of the usual type (i.e. a mod hasn't changed Bound Bow to // summon an Axe instead) - if (effect.mEffectID <= ESM::MagicEffect::BoundLongbow) + if (std::ranges::find(boundWeapons, effect.mEffectID) != boundWeapons.end()) { - for (int e = ESM::MagicEffect::BoundDagger; e <= ESM::MagicEffect::BoundLongbow; ++e) + for (const auto& e : boundWeapons) if (actor.getClass().getCreatureStats(actor).getMagicEffects().getOrDefault(e).getMagnitude() > 0.f && (e != ESM::MagicEffect::BoundLongbow || effect.mEffectID == e || rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f)) diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index e55d851ec6..23314e9f6f 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -16,7 +16,7 @@ namespace MWMechanics { - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectMultiplier(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { if (!actor.getClass().isActor()) @@ -26,14 +26,14 @@ namespace MWMechanics return 1 - resistance / 100.f; } - float getEffectResistance(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectResistance(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { // Effects with no resistance attribute belonging to them can not be resisted - if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) + if (effectId.empty() || ESM::MagicEffect::getResistanceEffect(*effectId.getIf()).empty()) return 0.f; - const auto magicEffect = MWBase::Environment::get().getESMStore()->get().find(effectId); + const auto magicEffect = MWBase::Environment::get().getESMStore()->get().search(effectId); const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); @@ -72,16 +72,16 @@ namespace MWMechanics return x; } - float getEffectResistanceAttribute(short effectId, const MagicEffects* actorEffects) + float getEffectResistanceAttribute(const ESM::MagicEffectId& effectId, const MagicEffects* actorEffects) { - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + ESM::RefId resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + ESM::RefId weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); float resistance = 0; - if (resistanceEffect != -1) - resistance += actorEffects->getOrDefault(resistanceEffect).getMagnitude(); - if (weaknessEffect != -1) - resistance -= actorEffects->getOrDefault(weaknessEffect).getMagnitude(); + if (!resistanceEffect.empty()) + resistance += actorEffects->getOrDefault(*resistanceEffect.getIf()).getMagnitude(); + if (!weaknessEffect.empty()) + resistance -= actorEffects->getOrDefault(*weaknessEffect.getIf()).getMagnitude(); if (effectId == ESM::MagicEffect::FireDamage) resistance += actorEffects->getOrDefault(ESM::MagicEffect::FireShield).getMagnitude(); diff --git a/apps/openmw/mwmechanics/spellresistance.hpp b/apps/openmw/mwmechanics/spellresistance.hpp index 6966a456d1..442ce421d6 100644 --- a/apps/openmw/mwmechanics/spellresistance.hpp +++ b/apps/openmw/mwmechanics/spellresistance.hpp @@ -4,6 +4,7 @@ namespace ESM { struct Spell; + class RefId; } namespace MWWorld @@ -19,19 +20,19 @@ namespace MWMechanics /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectMultiplier(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). /// @return >=100 for fully resisted. can also return negative value for damage amplification. /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. - float getEffectResistance(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectResistance(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get the resistance attribute against an effect for a given actor. This will add together /// ResistX and Weakness to X effects relevant against the given effect. - float getEffectResistanceAttribute(short effectId, const MagicEffects* actorEffects); + float getEffectResistanceAttribute(const ESM::RefId& effectId, const MagicEffects* actorEffects); } #endif diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index f336688c1a..203575e7ab 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -257,7 +257,7 @@ namespace MWMechanics for (const ESM::SpellState::PermanentSpellEffectInfo& info : it->second) { // Applied corprus effects are already in loaded stats modifiers - if (info.mId == ESM::MagicEffect::FortifyAttribute) + if (info.mId == ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::FortifyAttribute)) { auto id = ESM::Attribute::indexToRefId(info.mArg); AttributeValue attr = creatureStats->getAttribute(id); @@ -265,7 +265,7 @@ namespace MWMechanics attr.damage(-info.mMagnitude); creatureStats->setAttribute(id, attr); } - else if (info.mId == ESM::MagicEffect::DrainAttribute) + else if (info.mId == ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::DrainAttribute)) { auto id = ESM::Attribute::indexToRefId(info.mArg); AttributeValue attr = creatureStats->getAttribute(id); diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 8dca5f3a00..5ff67107a3 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -166,7 +166,7 @@ namespace MWMechanics throw std::range_error("Index out of range"); ESM::ENAMstruct effect; - effect.mEffectID = static_cast(ingredient->mData.mEffectID[index]); + effect.mEffectID = ingredient->mData.mEffectID[index]; effect.mSkill = static_cast(ingredient->mData.mSkills[index]); effect.mAttribute = static_cast(ingredient->mData.mAttributes[index]); effect.mRange = ESM::RT_Self; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index cbc76e8743..ed030a28a9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -619,14 +619,14 @@ namespace MWScript k.has_value() && *k >= 0 && *k <= 32767) key = *k; else - key = ESM::MagicEffect::effectGmstIdToIndex(effectName); + key = ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::effectGmstIdToRefId(effectName)); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); for (const auto& spell : stats.getActiveSpells()) { for (const auto& effect : spell.getEffects()) { - if (effect.mFlags & ESM::ActiveEffect::Flag_Remove && effect.mEffectId == key) + if (effect.mFlags & ESM::ActiveEffect::Flag_Remove && ESM::MagicEffect::refIdToIndex(effect.mEffectId) == key) { runtime.push(1); return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 89ab4c97ee..7aaefa3d76 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3108,7 +3108,7 @@ namespace MWWorld const MWWorld::Class& cls = ptr.getClass(); if (cls.isActor()) { - std::set playing; + std::set playing; for (const auto& params : cls.getCreatureStats(ptr).getActiveSpells()) { for (const auto& effect : params.getEffects()) From 6ca64fdddd120ff1a30a3999ab9019f8a82cb9be Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Fri, 12 Dec 2025 19:43:28 -0500 Subject: [PATCH 07/14] Changed statsextensions internal representations of magic effects to RefId. Interfaces with mwscript remain index-based. --- apps/esmtool/record.cpp | 6 +- apps/opencs/model/world/collection.hpp | 5 +- apps/opencs/model/world/columnimp.cpp | 2 +- apps/openmw/mwscript/statsextensions.cpp | 86 ++++++++++++------------ 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 444d255647..48d18a235a 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -153,7 +153,7 @@ namespace int i = 0; for (const ESM::IndexedENAMstruct& effect : effects.mList) { - std::cout << " Effect[" << i << "]: " << magicEffectLabel(ESM::MagicEffect::refIdToIndex(effect.mData.mEffectID)) << " (" + std::cout << " Effect[" << i << "]: " << magicEffectLabel(effect.mData.mEffectID) << " (" << effect.mData.mEffectID << ")" << std::endl; if (effect.mData.mSkill != -1) std::cout << " Skill: " << skillLabel(effect.mData.mSkill) << " (" << (int)effect.mData.mSkill << ")" @@ -973,9 +973,7 @@ namespace EsmTool template <> void Record::print() { - std::cout << " Id: " << mData.mId << std::endl; - std::cout << " Index: " << magicEffectLabel(ESM::MagicEffect::refIdToIndex(mData.mId)) << " (" - << ESM::MagicEffect::refIdToIndex(mData.mId) << ")" << std::endl; + std::cout << " Index: " << magicEffectLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Flags: " << magicEffectFlags(mData.mData.mFlags) << std::endl; diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 1350666cfc..9d3658cdf0 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -80,9 +80,8 @@ namespace CSMWorld inline void setRecordId(const ESM::RefId& id, ESM::MagicEffect& record) { - if (const auto* magicEffectId = id.getIf()) - record.mId = *magicEffectId; - throw std::runtime_error("Invalid magic effect id: " + id.toDebugString()); + int index = ESM::MagicEffect::indexNameToIndex(id.getRefIdString()); + record.mId = ESM::RefId::index(ESM::REC_MGEF, static_cast(index)); } inline void setRecordId(const ESM::RefId& id, ESM::Skill& record) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 7080f2f7be..baf2e5d8e3 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -30,7 +30,7 @@ namespace CSMWorld switch (value.getRecordType()) { case ESM::REC_MGEF: - return std::string(ESM::MagicEffect::indexToName(value.getValue())); + return std::string(ESM::MagicEffect::sIndexNames[value.getValue()]); default: break; } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index cc5d593592..c7daa9072c 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -580,7 +580,7 @@ namespace MWScript runtime.pop(); if (ptr.getClass().isActor()) - ptr.getClass().getCreatureStats(ptr).getActiveSpells().purgeEffect(ptr, effectId); + ptr.getClass().getCreatureStats(ptr).getActiveSpells().purgeEffect(ptr, ESM::MagicEffect::indexToRefId(static_cast(effectId))); } }; @@ -1262,13 +1262,13 @@ namespace MWScript template class OpGetMagicEffect : public Interpreter::Opcode0 { - int mPositiveEffect; - int mNegativeEffect; + ESM::RefId mPositiveEffect; + ESM::RefId mNegativeEffect; public: OpGetMagicEffect(int positiveEffect, int negativeEffect) - : mPositiveEffect(positiveEffect) - , mNegativeEffect(negativeEffect) + : mPositiveEffect(ESM::MagicEffect::indexToRefId(positiveEffect)) + , mNegativeEffect(ESM::MagicEffect::indexToRefId(negativeEffect)) { } @@ -1284,7 +1284,7 @@ namespace MWScript const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.getOrDefault(mPositiveEffect).getMagnitude(); - if (mNegativeEffect != -1) + if (!mNegativeEffect.empty()) currentValue -= effects.getOrDefault(mNegativeEffect).getMagnitude(); // GetResist* should take in account elemental shields @@ -1303,13 +1303,13 @@ namespace MWScript template class OpSetMagicEffect : public Interpreter::Opcode0 { - int mPositiveEffect; - int mNegativeEffect; + ESM::RefId mPositiveEffect; + ESM::RefId mNegativeEffect; public: OpSetMagicEffect(int positiveEffect, int negativeEffect) - : mPositiveEffect(positiveEffect) - , mNegativeEffect(negativeEffect) + : mPositiveEffect(ESM::MagicEffect::indexToRefId(positiveEffect)) + , mNegativeEffect(ESM::MagicEffect::indexToRefId(negativeEffect)) { } @@ -1325,7 +1325,7 @@ namespace MWScript MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.getOrDefault(mPositiveEffect).getMagnitude(); - if (mNegativeEffect != -1) + if (!mNegativeEffect.empty()) currentValue -= effects.getOrDefault(mNegativeEffect).getMagnitude(); // SetResist* should take in account elemental shields @@ -1343,13 +1343,13 @@ namespace MWScript template class OpModMagicEffect : public Interpreter::Opcode0 { - int mPositiveEffect; - int mNegativeEffect; + ESM::RefId mPositiveEffect; + ESM::RefId mNegativeEffect; public: OpModMagicEffect(int positiveEffect, int negativeEffect) - : mPositiveEffect(positiveEffect) - , mNegativeEffect(negativeEffect) + : mPositiveEffect(ESM::MagicEffect::indexToRefId(positiveEffect)) + , mNegativeEffect(ESM::MagicEffect::indexToRefId(negativeEffect)) { } @@ -1415,8 +1415,8 @@ namespace MWScript struct MagicEffect { - int mPositiveEffect; - int mNegativeEffect; + ESM::RefId mPositiveEffect; + ESM::RefId mNegativeEffect; }; void installOpcodes(Interpreter::Interpreter& interpreter) @@ -1569,36 +1569,36 @@ namespace MWScript interpreter.installSegment5>(Compiler::Stats::opcodeGetStatExplicit); static const MagicEffect sMagicEffects[] = { - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistMagicka), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToMagicka) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistFire), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToFire) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistFrost), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToFrost) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistShock), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToShock) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistCommonDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToCommonDisease) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistBlightDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToBlightDisease) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistCorprusDisease), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToCorprusDisease) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistPoison), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToPoison) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistParalysis), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::ResistNormalWeapons), ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WeaknessToNormalWeapons) }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WaterBreathing), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Chameleon), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::WaterWalking), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::SwiftSwim), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Jump), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Levitate), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Shield), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Sound), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Silence), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Blind), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Paralyze), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Invisibility), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::FortifyAttack), -1 }, - { ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::Sanctuary), -1 }, + { ESM::MagicEffect::ResistMagicka, ESM::MagicEffect::WeaknessToMagicka }, + { ESM::MagicEffect::ResistFire, ESM::MagicEffect::WeaknessToFire }, + { ESM::MagicEffect::ResistFrost, ESM::MagicEffect::WeaknessToFrost }, + { ESM::MagicEffect::ResistShock, ESM::MagicEffect::WeaknessToShock }, + { ESM::MagicEffect::ResistCommonDisease, ESM::MagicEffect::WeaknessToCommonDisease }, + { ESM::MagicEffect::ResistBlightDisease, ESM::MagicEffect::WeaknessToBlightDisease }, + { ESM::MagicEffect::ResistCorprusDisease, ESM::MagicEffect::WeaknessToCorprusDisease }, + { ESM::MagicEffect::ResistPoison, ESM::MagicEffect::WeaknessToPoison }, + { ESM::MagicEffect::ResistParalysis, ESM::RefId() }, + { ESM::MagicEffect::ResistNormalWeapons, ESM::MagicEffect::WeaknessToNormalWeapons }, + { ESM::MagicEffect::WaterBreathing, ESM::RefId() }, + { ESM::MagicEffect::Chameleon, ESM::RefId() }, + { ESM::MagicEffect::WaterWalking, ESM::RefId() }, + { ESM::MagicEffect::SwiftSwim, ESM::RefId() }, + { ESM::MagicEffect::Jump, ESM::RefId() }, + { ESM::MagicEffect::Levitate, ESM::RefId() }, + { ESM::MagicEffect::Shield, ESM::RefId() }, + { ESM::MagicEffect::Sound, ESM::RefId() }, + { ESM::MagicEffect::Silence, ESM::RefId() }, + { ESM::MagicEffect::Blind, ESM::RefId() }, + { ESM::MagicEffect::Paralyze, ESM::RefId() }, + { ESM::MagicEffect::Invisibility, ESM::RefId() }, + { ESM::MagicEffect::FortifyAttack, ESM::RefId() }, + { ESM::MagicEffect::Sanctuary, ESM::RefId() }, }; for (int i = 0; i < 24; ++i) { - int positive = sMagicEffects[i].mPositiveEffect; - int negative = sMagicEffects[i].mNegativeEffect; + int positive = ESM::MagicEffect::refIdToIndex(sMagicEffects[i].mPositiveEffect); + int negative = ESM::MagicEffect::refIdToIndex(sMagicEffects[i].mNegativeEffect); interpreter.installSegment5>( Compiler::Stats::opcodeGetMagicEffect + i, positive, negative); From e389a3b3433d3f7b106c6919090b414902f76804 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Tue, 16 Dec 2025 10:11:13 -0500 Subject: [PATCH 08/14] - Changed OpenMW-CS model to use RefIds and QStrings for magic effects. - Changed esmtool records to used RefIds for magic effects. --- apps/esmtool/record.cpp | 17 ++-- apps/opencs/model/doc/document.cpp | 16 ++-- apps/opencs/model/tools/effectlistcheck.cpp | 8 +- apps/opencs/model/world/collection.hpp | 10 +-- apps/opencs/model/world/columnimp.cpp | 13 +-- .../model/world/nestedcoladapterimp.hpp | 79 +++++++++--------- apps/opencs/model/world/refidadapterimp.cpp | 80 +++++++++---------- 7 files changed, 105 insertions(+), 118 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 48d18a235a..042bab900d 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -153,8 +153,10 @@ namespace int i = 0; for (const ESM::IndexedENAMstruct& effect : effects.mList) { - std::cout << " Effect[" << i << "]: " << magicEffectLabel(effect.mData.mEffectID) << " (" - << effect.mData.mEffectID << ")" << std::endl; + int effectIdx = ESM::MagicEffect::refIdToIndex(effect.mData.mEffectID); + if (effectIdx != -1) + std::cout << " Effect[" << i << "]: " << magicEffectLabel(effectIdx) << " (" + << effectIdx << ")" << std::endl; if (effect.mData.mSkill != -1) std::cout << " Skill: " << skillLabel(effect.mData.mSkill) << " (" << (int)effect.mData.mSkill << ")" << std::endl; @@ -843,10 +845,12 @@ namespace EsmTool std::cout << " Value: " << mData.mData.mValue << std::endl; for (int i = 0; i != 4; i++) { - // A value of -1 means no effect - if (mData.mData.mEffectID[i] == -1) + // A value of EmptyRefId means no effect + if (mData.mData.mEffectID[i].empty()) continue; - std::cout << " Effect: " << magicEffectLabel(mData.mData.mEffectID[i]) << " (" << mData.mData.mEffectID[i] + + int effectIdx = ESM::MagicEffect::refIdToIndex(mData.mData.mEffectID[i]); + std::cout << " Effect: " << magicEffectLabel(effectIdx) << " (" << effectIdx << ")" << std::endl; std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) << " (" << mData.mData.mSkills[i] << ")" << std::endl; @@ -973,7 +977,8 @@ namespace EsmTool template <> void Record::print() { - std::cout << " Index: " << magicEffectLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; + int effectIdx = ESM::MagicEffect::refIdToIndex(mData.mId); + std::cout << " Index: " << magicEffectLabel(effectIdx) << " (" << effectIdx << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; std::cout << " Flags: " << magicEffectFlags(mData.mData.mFlags) << std::endl; diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 536a5c0d2d..7119268a27 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -128,11 +128,18 @@ void CSMDoc::Document::addOptionalGlobals() void CSMDoc::Document::addOptionalMagicEffects() { - for (int i = ESM::MagicEffect::SummonFabricant; i <= ESM::MagicEffect::SummonCreature05; ++i) + static const std::array optionalMagicEffects{ + ESM::MagicEffect::SummonFabricant, + ESM::MagicEffect::SummonWolf, + ESM::MagicEffect::SummonBear, + ESM::MagicEffect::SummonBonewolf, + ESM::MagicEffect::SummonCreature04, + ESM::MagicEffect::SummonCreature05, + }; + for (const auto& effectId : optionalMagicEffects) { ESM::MagicEffect effect; - effect.mIndex = i; - effect.mId = ESM::MagicEffect::indexToRefId(i); + effect.mId = effectId; effect.blank(); addOptionalMagicEffect(effect); @@ -283,8 +290,7 @@ void CSMDoc::Document::createBase() { ESM::MagicEffect record; - record.mIndex = i; - record.mId = ESM::MagicEffect::indexToRefId(i); + record.mId = *ESM::MagicEffect::indexToRefId(i).getIf(); record.blank(); diff --git a/apps/opencs/model/tools/effectlistcheck.cpp b/apps/opencs/model/tools/effectlistcheck.cpp index b8695bc419..461950e457 100644 --- a/apps/opencs/model/tools/effectlistcheck.cpp +++ b/apps/opencs/model/tools/effectlistcheck.cpp @@ -26,7 +26,8 @@ namespace CSMTools const std::string number = std::to_string(i); // At the time of writing this effects, attributes and skills are mostly hardcoded - if (effect.mData.mEffectID < 0 || effect.mData.mEffectID >= ESM::MagicEffect::Length) + int effectIndex = ESM::MagicEffect::refIdToIndex(effect.mData.mEffectID); + if (effectIndex < -1 || effectIndex >= ESM::MagicEffect::Length) messages.add(id, "Effect #" + number + ": invalid effect ID", "", CSMDoc::Message::Severity_Error); if (effect.mData.mSkill < -1 || effect.mData.mSkill >= ESM::Skill::Length) messages.add(id, "Effect #" + number + ": invalid skill", "", CSMDoc::Message::Severity_Error); @@ -68,13 +69,14 @@ namespace CSMTools for (size_t i = 0; i < 4; i++) { - if (ingredient.mData.mEffectID[i] == -1) + if (ingredient.mData.mEffectID[i].empty()) continue; hasEffects = true; + int effectIndex = ESM::MagicEffect::refIdToIndex(ingredient.mData.mEffectID[i]); const std::string number = std::to_string(i + 1); - if (ingredient.mData.mEffectID[i] < -1 || ingredient.mData.mEffectID[i] >= ESM::MagicEffect::Length) + if (effectIndex < -1 || effectIndex >= ESM::MagicEffect::Length) messages.add(id, "Effect #" + number + ": invalid effect ID", "", CSMDoc::Message::Severity_Error); if (ingredient.mData.mSkills[i] < -1 || ingredient.mData.mSkills[i] >= ESM::Skill::Length) messages.add(id, "Effect #" + number + ": invalid skill", "", CSMDoc::Message::Severity_Error); diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9d3658cdf0..8e4e7bde5c 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -73,15 +73,11 @@ namespace CSMWorld return ESM::RefId::stringRefId(Land::createUniqueRecordId(record.mX, record.mY)); } - inline ESM::RefId getRecordId(const ESM::MagicEffect& record) - { - return ESM::RefId::stringRefId(CSMWorld::getStringId(record.mId)); - } - inline void setRecordId(const ESM::RefId& id, ESM::MagicEffect& record) { - int index = ESM::MagicEffect::indexNameToIndex(id.getRefIdString()); - record.mId = ESM::RefId::index(ESM::REC_MGEF, static_cast(index)); + if (const auto* effectId = id.getIf()) + record.mId = *effectId; + throw std::runtime_error("Invalid magic effect id: " + id.toDebugString()); } inline void setRecordId(const ESM::RefId& id, ESM::Skill& record) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index baf2e5d8e3..f49b2bab08 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -25,18 +25,7 @@ namespace CSMWorld std::string operator()(ESM::FormId value) const { return value.toString("FormId:"); } - std::string operator()(ESM::IndexRefId value) const - { - switch (value.getRecordType()) - { - case ESM::REC_MGEF: - return std::string(ESM::MagicEffect::sIndexNames[value.getValue()]); - default: - break; - } - - return value.toDebugString(); - } + std::string operator()(ESM::IndexRefId value) const { return value.toDebugString(); } template std::string operator()(const T& value) const diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index f5c5501889..ef1001c2dc 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -260,7 +260,7 @@ namespace CSMWorld // blank row ESM::IndexedENAMstruct effect; effect.mIndex = position; - effect.mData.mEffectID = 0; + effect.mData.mEffectID = ESM::MagicEffect::WaterBreathing; effect.mData.mSkill = -1; effect.mData.mAttribute = -1; effect.mData.mRange = 0; @@ -319,34 +319,30 @@ namespace CSMWorld switch (subColIndex) { case 0: - return effect.mEffectID; + if (effect.mEffectID.empty()) + return QVariant(); + return QString::fromStdString(effect.mEffectID.getRefIdString()); case 1: { - switch (effect.mEffectID) - { - case ESM::MagicEffect::DrainSkill: - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::RestoreSkill: - case ESM::MagicEffect::FortifySkill: - case ESM::MagicEffect::AbsorbSkill: - return effect.mSkill; - default: - return QVariant(); - } + if (effect.mEffectID == ESM::MagicEffect::DrainSkill || + effect.mEffectID == ESM::MagicEffect::DamageSkill || + effect.mEffectID == ESM::MagicEffect::RestoreSkill || + effect.mEffectID == ESM::MagicEffect::FortifySkill || + effect.mEffectID == ESM::MagicEffect::AbsorbSkill) + return effect.mSkill; + else + return QVariant(); } case 2: { - switch (effect.mEffectID) - { - case ESM::MagicEffect::DrainAttribute: - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::RestoreAttribute: - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::AbsorbAttribute: - return effect.mAttribute; - default: - return QVariant(); - } + if (effect.mEffectID == ESM::MagicEffect::DrainAttribute || + effect.mEffectID == ESM::MagicEffect::DamageAttribute || + effect.mEffectID == ESM::MagicEffect::RestoreAttribute || + effect.mEffectID == ESM::MagicEffect::FortifyAttribute || + effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) + return effect.mAttribute; + else + return QVariant(); } case 3: return effect.mRange; @@ -377,26 +373,23 @@ namespace CSMWorld { case 0: { - effect.mEffectID = static_cast(value.toInt()); - switch (effect.mEffectID) + effect.mEffectID = ESM::MagicEffectId(value.toString().toStdString()); + if (effect.mEffectID == ESM::MagicEffect::DrainSkill || + effect.mEffectID == ESM::MagicEffect::DamageSkill || + effect.mEffectID == ESM::MagicEffect::RestoreSkill || + effect.mEffectID == ESM::MagicEffect::FortifySkill || + effect.mEffectID == ESM::MagicEffect::AbsorbSkill) + effect.mAttribute = -1; + else if (effect.mEffectID == ESM::MagicEffect::DrainAttribute || + effect.mEffectID == ESM::MagicEffect::DamageAttribute || + effect.mEffectID == ESM::MagicEffect::RestoreAttribute || + effect.mEffectID == ESM::MagicEffect::FortifyAttribute || + effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) + effect.mSkill = -1; + else { - case ESM::MagicEffect::DrainSkill: - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::RestoreSkill: - case ESM::MagicEffect::FortifySkill: - case ESM::MagicEffect::AbsorbSkill: - effect.mAttribute = -1; - break; - case ESM::MagicEffect::DrainAttribute: - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::RestoreAttribute: - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::AbsorbAttribute: - effect.mSkill = -1; - break; - default: - effect.mSkill = -1; - effect.mAttribute = -1; + effect.mSkill = -1; + effect.mAttribute = -1; } break; } diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index a6ea56a56d..d63f436838 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -144,37 +144,33 @@ QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData( if (subRowIndex < 0 || subRowIndex >= 4) throw std::runtime_error("index out of range"); + ESM::RefId effectId = record.get().mData.mEffectID[subRowIndex]; + switch (subColIndex) { case 0: - return record.get().mData.mEffectID[subRowIndex]; + return effectId.empty() ? QVariant() : QString::fromStdString(effectId.getRefIdString()); case 1: { - switch (record.get().mData.mEffectID[subRowIndex]) - { - case ESM::MagicEffect::DrainSkill: - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::RestoreSkill: - case ESM::MagicEffect::FortifySkill: - case ESM::MagicEffect::AbsorbSkill: - return record.get().mData.mSkills[subRowIndex]; - default: - return QVariant(); - } + if (effectId == ESM::MagicEffect::DrainSkill || + effectId == ESM::MagicEffect::DamageSkill || + effectId == ESM::MagicEffect::RestoreSkill || + effectId == ESM::MagicEffect::FortifySkill || + effectId == ESM::MagicEffect::AbsorbSkill) + return record.get().mData.mSkills[subRowIndex]; + else + return QVariant(); } case 2: { - switch (record.get().mData.mEffectID[subRowIndex]) - { - case ESM::MagicEffect::DrainAttribute: - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::RestoreAttribute: - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::AbsorbAttribute: - return record.get().mData.mAttributes[subRowIndex]; - default: - return QVariant(); - } + if (effectId == ESM::MagicEffect::DrainAttribute || + effectId == ESM::MagicEffect::DamageAttribute || + effectId == ESM::MagicEffect::RestoreAttribute || + effectId == ESM::MagicEffect::FortifyAttribute || + effectId == ESM::MagicEffect::AbsorbAttribute) + return record.get().mData.mAttributes[subRowIndex]; + else + return QVariant(); } default: throw std::runtime_error("Trying to access non-existing column in the nested table!"); @@ -191,29 +187,29 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedData( if (subRowIndex < 0 || subRowIndex >= 4) throw std::runtime_error("index out of range"); + ESM::RefId effectId + = value.toString().isEmpty() ? ESM::RefId() : ESM::MagicEffectId(value.toString().toStdString()); + switch (subColIndex) { case 0: - ingredient.mData.mEffectID[subRowIndex] = value.toInt(); - switch (ingredient.mData.mEffectID[subRowIndex]) + ingredient.mData.mEffectID[subRowIndex] = effectId; + if (effectId == ESM::MagicEffect::DrainSkill || + effectId == ESM::MagicEffect::DamageSkill || + effectId == ESM::MagicEffect::RestoreSkill || + effectId == ESM::MagicEffect::FortifySkill || + effectId == ESM::MagicEffect::AbsorbSkill) + ingredient.mData.mAttributes[subRowIndex] = -1; + else if (effectId == ESM::MagicEffect::DrainAttribute || + effectId == ESM::MagicEffect::DamageAttribute || + effectId == ESM::MagicEffect::RestoreAttribute || + effectId == ESM::MagicEffect::FortifyAttribute || + effectId == ESM::MagicEffect::AbsorbAttribute) + ingredient.mData.mSkills[subRowIndex] = -1; + else { - case ESM::MagicEffect::DrainSkill: - case ESM::MagicEffect::DamageSkill: - case ESM::MagicEffect::RestoreSkill: - case ESM::MagicEffect::FortifySkill: - case ESM::MagicEffect::AbsorbSkill: - ingredient.mData.mAttributes[subRowIndex] = -1; - break; - case ESM::MagicEffect::DrainAttribute: - case ESM::MagicEffect::DamageAttribute: - case ESM::MagicEffect::RestoreAttribute: - case ESM::MagicEffect::FortifyAttribute: - case ESM::MagicEffect::AbsorbAttribute: - ingredient.mData.mSkills[subRowIndex] = -1; - break; - default: - ingredient.mData.mSkills[subRowIndex] = -1; - ingredient.mData.mAttributes[subRowIndex] = -1; + ingredient.mData.mSkills[subRowIndex] = -1; + ingredient.mData.mAttributes[subRowIndex] = -1; } break; case 1: From 2c76043cf5f051241ddd9e2581d4329f5c9b54b8 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Tue, 16 Dec 2025 19:09:30 -0500 Subject: [PATCH 09/14] - Changed sol magic bindings to use RefIds instead of integer indexes for magic effects. - Fixed silly namespace oversight causing linker errors. - Fixed type mismatches occluded by linker errors. - Fixed unit tests. --- apps/components_tests/esm3/testsaveload.cpp | 3 +- apps/openmw/mwclass/ingredient.cpp | 6 +- apps/openmw/mwlua/magicbindings.cpp | 53 ++-- apps/openmw/mwlua/types/ingredient.cpp | 6 +- apps/openmw/mwmechanics/character.cpp | 13 +- apps/openmw/mwmechanics/spellresistance.cpp | 11 +- apps/openmw_tests/mwworld/teststore.cpp | 1 - components/esm3/loadmgef.cpp | 286 ++++++++++---------- 8 files changed, 181 insertions(+), 198 deletions(-) diff --git a/apps/components_tests/esm3/testsaveload.cpp b/apps/components_tests/esm3/testsaveload.cpp index 0aac55d0fd..9d9a549ef3 100644 --- a/apps/components_tests/esm3/testsaveload.cpp +++ b/apps/components_tests/esm3/testsaveload.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -574,7 +575,7 @@ namespace ESM { EffectList record; record.mList.emplace_back(IndexedENAMstruct{ { - .mEffectID = 1, + .mEffectID = ESM::MagicEffect::SwiftSwim, .mSkill = 2, .mAttribute = 3, .mRange = 4, diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 235d3b14b7..071dc05f54 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -71,7 +71,7 @@ namespace MWClass std::unique_ptr Ingredient::use(const MWWorld::Ptr& ptr, bool force) const { - if (ptr.get()->mBase->mData.mEffectID[0] < 0) + if (ptr.get()->mBase->mData.mEffectID[0].empty()) return std::make_unique(); std::unique_ptr action = std::make_unique(ptr); @@ -131,10 +131,10 @@ namespace MWClass MWGui::Widgets::SpellEffectList list; for (int i = 0; i < 4; ++i) { - if (ref->mBase->mData.mEffectID[i] < 0) + if (ref->mBase->mData.mEffectID[i].empty()) continue; MWGui::Widgets::SpellEffectParams params; - params.mEffectID = static_cast(ref->mBase->mData.mEffectID[i]); + params.mEffectID = ref->mBase->mData.mEffectID[i]; params.mAttribute = ESM::Attribute::indexToRefId(ref->mBase->mData.mAttributes[i]); params.mSkill = ESM::Skill::indexToRefId(ref->mBase->mData.mSkills[i]); params.mKnown = alchemySkill >= fWortChanceValue * (i + 1); diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index 97cf4dfb89..c31e4cdeb3 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -245,8 +245,9 @@ namespace MWLua sol::table effect(state, sol::create); magicApi["EFFECT_TYPE"] = LuaUtil::makeStrictReadOnly(effect); - for (const auto& name : ESM::MagicEffect::sIndexNames) + for (size_t i = 0; i < ESM::MagicEffect::Length; ++i) { + std::string_view name = ESM::MagicEffect::indexToName(static_cast(i)); effect[name] = Misc::StringUtils::lowerCase(name); } @@ -271,32 +272,12 @@ namespace MWLua return "ESM3_MagicEffectStore{" + std::to_string(store.getSize()) + " effects}"; }; magicEffectStoreT[sol::meta_function::index] = sol::overload( - [](const MagicEffectStore& store, int id) -> const ESM::MagicEffect* { return store.search(id); }, + [](const MagicEffectStore& store, const ESM::RefId& id) -> const ESM::MagicEffect* { return store.search(id); }, + [](const MagicEffectStore& store, int id) -> const ESM::MagicEffect* { return store.search(ESM::MagicEffect::indexToRefId(id)); }, [](const MagicEffectStore& store, std::string_view id) -> const ESM::MagicEffect* { int index = ESM::MagicEffect::indexNameToIndex(id); - return store.search(index); + return store.search(ESM::MagicEffect::indexToRefId(index)); }); - auto magicEffectsIter = [magicEffectStore](sol::this_state thisState, const sol::object& /*store*/, - sol::optional id) -> std::tuple { - MagicEffectStore::iterator iter; - if (id.has_value()) - { - iter = magicEffectStore->findIter(*id); - if (iter != magicEffectStore->end()) - iter++; - } - else - iter = magicEffectStore->begin(); - if (iter != magicEffectStore->end()) - return std::make_tuple( - sol::make_object(thisState, iter->first), sol::make_object(thisState, &iter->second)); - else - return std::make_tuple(sol::nil, sol::nil); - }; - magicEffectStoreT[sol::meta_function::pairs] - = [iter = sol::make_object(state, magicEffectsIter)] { return iter; }; - magicEffectStoreT[sol::meta_function::ipairs] - = [iter = sol::make_object(state, magicEffectsIter)] { return iter; }; magicEffects["records"] = magicEffectStore; @@ -344,15 +325,15 @@ namespace MWLua // Effect params auto effectParamsT = state.new_usertype("ESM3_EffectParams"); effectParamsT[sol::meta_function::to_string] = [magicEffectStore](const ESM::IndexedENAMstruct& params) { - const ESM::MagicEffect* const rec = magicEffectStore->find(params.mData.mEffectID); - return "ESM3_EffectParams[" + ESM::MagicEffect::indexToGmstString(rec->mIndex) + "]"; + const ESM::MagicEffect* const rec = magicEffectStore->search(params.mData.mEffectID); + return "ESM3_EffectParams[" + std::string(ESM::MagicEffect::refIdToGmstString(rec->mId)) + "]"; }; effectParamsT["effect"] = sol::readonly_property( [magicEffectStore](const ESM::IndexedENAMstruct& params) -> const ESM::MagicEffect* { return magicEffectStore->find(params.mData.mEffectID); }); effectParamsT["id"] = sol::readonly_property([](const ESM::IndexedENAMstruct& params) -> std::string { - auto name = ESM::MagicEffect::indexToName(params.mData.mEffectID); + auto name = ESM::MagicEffect::refIdToName(params.mData.mEffectID); return Misc::StringUtils::lowerCase(name); }); effectParamsT["affectedSkill"] @@ -386,10 +367,10 @@ namespace MWLua auto magicEffectT = state.new_usertype("ESM3_MagicEffect"); magicEffectT[sol::meta_function::to_string] = [](const ESM::MagicEffect& rec) { - return "ESM3_MagicEffect[" + ESM::MagicEffect::indexToGmstString(rec.mIndex) + "]"; + return "ESM3_MagicEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(rec.mId)) + "]"; }; magicEffectT["id"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { - auto name = ESM::MagicEffect::indexToName(rec.mIndex); + auto name = ESM::MagicEffect::refIdToName(rec.mId); return Misc::StringUtils::lowerCase(name); }); magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { @@ -422,7 +403,7 @@ namespace MWLua .getWorld() ->getStore() .get() - .find(ESM::MagicEffect::indexToGmstString(rec.mIndex)) + .find(ESM::MagicEffect::refIdToGmstString(rec.mId)) ->mValue.getString(); }); magicEffectT["school"] = sol::readonly_property( @@ -453,10 +434,10 @@ namespace MWLua auto activeSpellEffectT = state.new_usertype("ActiveSpellEffect"); activeSpellEffectT[sol::meta_function::to_string] = [](const ESM::ActiveEffect& self) { - return "ActiveSpellEffect[" + ESM::MagicEffect::indexToGmstString(self.mEffectId) + "]"; + return "ActiveSpellEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(self.mEffectId)) + "]"; }; activeSpellEffectT["id"] = sol::readonly_property([](const ESM::ActiveEffect& self) -> std::string { - auto name = ESM::MagicEffect::indexToName(self.mEffectId); + auto name = ESM::MagicEffect::refIdToName(self.mEffectId); return Misc::StringUtils::lowerCase(name); }); activeSpellEffectT["index"] @@ -588,10 +569,10 @@ namespace MWLua auto activeEffectT = state.new_usertype("ActiveEffect"); activeEffectT[sol::meta_function::to_string] = [](const ActiveEffect& self) { - return "ActiveEffect[" + ESM::MagicEffect::indexToGmstString(self.key.mId) + "]"; + return "ActiveEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(self.key.mId)) + "]"; }; activeEffectT["id"] = sol::readonly_property([](const ActiveEffect& self) -> std::string { - auto name = ESM::MagicEffect::indexToName(self.key.mId); + auto name = ESM::MagicEffect::refIdToName(self.key.mId); return Misc::StringUtils::lowerCase(name); }); activeEffectT["name"] @@ -1024,8 +1005,8 @@ namespace MWLua auto getEffectKey = [](std::string_view idStr, sol::optional argStr) -> MWMechanics::EffectKey { - auto id = ESM::MagicEffect::indexNameToIndex(idStr); - auto* rec = MWBase::Environment::get().getWorld()->getStore().get().find(id); + auto id = ESM::MagicEffectId(idStr); + auto* rec = MWBase::Environment::get().getWorld()->getStore().get().search(id); MWMechanics::EffectKey key = MWMechanics::EffectKey(id); diff --git a/apps/openmw/mwlua/types/ingredient.cpp b/apps/openmw/mwlua/types/ingredient.cpp index f1cb73bc5f..0089ce2457 100644 --- a/apps/openmw/mwlua/types/ingredient.cpp +++ b/apps/openmw/mwlua/types/ingredient.cpp @@ -46,10 +46,10 @@ namespace MWLua sol::table res(lua, sol::create); for (uint32_t i = 0; i < 4; ++i) { - if (rec.mData.mEffectID[i] < 0) + if (rec.mData.mEffectID[i].empty()) continue; ESM::IndexedENAMstruct effect; - effect.mData.mEffectID = static_cast(rec.mData.mEffectID[i]); + effect.mData.mEffectID = rec.mData.mEffectID[i]; effect.mData.mSkill = static_cast(rec.mData.mSkills[i]); effect.mData.mAttribute = static_cast(rec.mData.mAttributes[i]); effect.mData.mRange = ESM::RT_Self; @@ -63,4 +63,4 @@ namespace MWLua return res; }); } -} \ No newline at end of file +} diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c87ad8ef15..c30c865412 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2803,19 +2803,18 @@ namespace MWMechanics // Stop any effects that are no longer active std::vector effects = mAnimation->getLoopingEffects(); - for (std::string_view effectId : effects) + for (std::string_view effectStr : effects) { - auto index = ESM::MagicEffect::indexNameToIndex(effectId); + auto effectId = ESM::MagicEffectId(effectStr); - if (index >= 0 - && (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() + if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() || mPtr.getClass() .getCreatureStats(mPtr) .getMagicEffects() - .getOrDefault(MWMechanics::EffectKey(index)) + .getOrDefault(MWMechanics::EffectKey(effectId)) .getMagnitude() - <= 0)) - mAnimation->removeEffect(effectId); + <= 0) + mAnimation->removeEffect(effectStr); } } diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index 23314e9f6f..03354139b3 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -72,12 +72,15 @@ namespace MWMechanics return x; } - float getEffectResistanceAttribute(const ESM::MagicEffectId& effectId, const MagicEffects* actorEffects) + float getEffectResistanceAttribute(const ESM::RefId& effectId, const MagicEffects* actorEffects) { - ESM::RefId resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - ESM::RefId weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - float resistance = 0; + if (effectId.empty()) + return resistance; + + ESM::RefId resistanceEffect = ESM::MagicEffect::getResistanceEffect(*effectId.getIf()); + ESM::RefId weaknessEffect = ESM::MagicEffect::getWeaknessEffect(*effectId.getIf()); + if (!resistanceEffect.empty()) resistance += actorEffects->getOrDefault(*resistanceEffect.getIf()).getMagnitude(); if (!weaknessEffect.empty()) diff --git a/apps/openmw_tests/mwworld/teststore.cpp b/apps/openmw_tests/mwworld/teststore.cpp index 70f094379a..54ca941aaa 100644 --- a/apps/openmw_tests/mwworld/teststore.cpp +++ b/apps/openmw_tests/mwworld/teststore.cpp @@ -501,7 +501,6 @@ namespace } } - static_assert(ESM::hasIndex); static_assert(ESM::hasStringId); template > diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index b90d985088..9033b2a62f 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -26,153 +26,153 @@ namespace ESM 0x1048, 0x1048, 0x1048, 0x1048 }; } - const MagicEffectId WaterBreathing("WaterBreathing"); - const MagicEffectId SwiftSwim("SwiftSwim"); - const MagicEffectId WaterWalking("WaterWalking"); - const MagicEffectId Shield("Shield"); - const MagicEffectId FireShield("FireShield"); - const MagicEffectId LightningShield("LightningShield"); - const MagicEffectId FrostShield("FrostShield"); - const MagicEffectId Burden("Burden"); - const MagicEffectId Feather("Feather"); - const MagicEffectId Jump("Jump"); - const MagicEffectId Levitate("Levitate"); - const MagicEffectId SlowFall("SlowFall"); - const MagicEffectId Lock("Lock"); - const MagicEffectId Open("Open"); - const MagicEffectId FireDamage("FireDamage"); - const MagicEffectId ShockDamage("ShockDamage"); - const MagicEffectId FrostDamage("FrostDamage"); - const MagicEffectId DrainAttribute("DrainAttribute"); - const MagicEffectId DrainHealth("DrainHealth"); - const MagicEffectId DrainMagicka("DrainMagicka"); - const MagicEffectId DrainFatigue("DrainFatigue"); - const MagicEffectId DrainSkill("DrainSkill"); - const MagicEffectId DamageAttribute("DamageAttribute"); - const MagicEffectId DamageHealth("DamageHealth"); - const MagicEffectId DamageMagicka("DamageMagicka"); - const MagicEffectId DamageFatigue("DamageFatigue"); - const MagicEffectId DamageSkill("DamageSkill"); - const MagicEffectId Poison("Poison"); - const MagicEffectId WeaknessToFire("WeaknessToFire"); - const MagicEffectId WeaknessToFrost("WeaknessToFrost"); - const MagicEffectId WeaknessToShock("WeaknessToShock"); - const MagicEffectId WeaknessToMagicka("WeaknessToMagicka"); - const MagicEffectId WeaknessToCommonDisease("WeaknessToCommonDisease"); - const MagicEffectId WeaknessToBlightDisease("WeaknessToBlightDisease"); - const MagicEffectId WeaknessToCorprusDisease("WeaknessToCorprusDisease"); - const MagicEffectId WeaknessToPoison("WeaknessToPoison"); - const MagicEffectId WeaknessToNormalWeapons("WeaknessToNormalWeapons"); - const MagicEffectId DisintegrateWeapon("DisintegrateWeapon"); - const MagicEffectId DisintegrateArmor("DisintegrateArmor"); - const MagicEffectId Invisibility("Invisibility"); - const MagicEffectId Chameleon("Chameleon"); - const MagicEffectId Light("Light"); - const MagicEffectId Sanctuary("Sanctuary"); - const MagicEffectId NightEye("NightEye"); - const MagicEffectId Charm("Charm"); - const MagicEffectId Paralyze("Paralyze"); - const MagicEffectId Silence("Silence"); - const MagicEffectId Blind("Blind"); - const MagicEffectId Sound("Sound"); - const MagicEffectId CalmHumanoid("CalmHumanoid"); - const MagicEffectId CalmCreature("CalmCreature"); - const MagicEffectId FrenzyHumanoid("FrenzyHumanoid"); - const MagicEffectId FrenzyCreature("FrenzyCreature"); - const MagicEffectId DemoralizeHumanoid("DemoralizeHumanoid"); - const MagicEffectId DemoralizeCreature("DemoralizeCreature"); - const MagicEffectId RallyHumanoid("RallyHumanoid"); - const MagicEffectId RallyCreature("RallyCreature"); - const MagicEffectId Dispel("Dispel"); - const MagicEffectId Soultrap("Soultrap"); - const MagicEffectId Telekinesis("Telekinesis"); - const MagicEffectId Mark("Mark"); - const MagicEffectId Recall("Recall"); - const MagicEffectId DivineIntervention("DivineIntervention"); - const MagicEffectId AlmsiviIntervention("AlmsiviIntervention"); - const MagicEffectId DetectAnimal("DetectAnimal"); - const MagicEffectId DetectEnchantment("DetectEnchantment"); - const MagicEffectId DetectKey("DetectKey"); - const MagicEffectId SpellAbsorption("SpellAbsorption"); - const MagicEffectId Reflect("Reflect"); - const MagicEffectId CureCommonDisease("CureCommonDisease"); - const MagicEffectId CureBlightDisease("CureBlightDisease"); - const MagicEffectId CureCorprusDisease("CureCorprusDisease"); - const MagicEffectId CurePoison("CurePoison"); - const MagicEffectId CureParalyzation("CureParalyzation"); - const MagicEffectId RestoreAttribute("RestoreAttribute"); - const MagicEffectId RestoreHealth("RestoreHealth"); - const MagicEffectId RestoreMagicka("RestoreMagicka"); - const MagicEffectId RestoreFatigue("RestoreFatigue"); - const MagicEffectId RestoreSkill("RestoreSkill"); - const MagicEffectId FortifyAttribute("FortifyAttribute"); - const MagicEffectId FortifyHealth("FortifyHealth"); - const MagicEffectId FortifyMagicka("FortifyMagicka"); - const MagicEffectId FortifyFatigue("FortifyFatigue"); - const MagicEffectId FortifySkill("FortifySkill"); - const MagicEffectId FortifyMaximumMagicka("FortifyMaximumMagicka"); - const MagicEffectId AbsorbAttribute("AbsorbAttribute"); - const MagicEffectId AbsorbHealth("AbsorbHealth"); - const MagicEffectId AbsorbMagicka("AbsorbMagicka"); - const MagicEffectId AbsorbFatigue("AbsorbFatigue"); - const MagicEffectId AbsorbSkill("AbsorbSkill"); - const MagicEffectId ResistFire("ResistFire"); - const MagicEffectId ResistFrost("ResistFrost"); - const MagicEffectId ResistShock("ResistShock"); - const MagicEffectId ResistMagicka("ResistMagicka"); - const MagicEffectId ResistCommonDisease("ResistCommonDisease"); - const MagicEffectId ResistBlightDisease("ResistBlightDisease"); - const MagicEffectId ResistCorprusDisease("ResistCorprusDisease"); - const MagicEffectId ResistPoison("ResistPoison"); - const MagicEffectId ResistNormalWeapons("ResistNormalWeapons"); - const MagicEffectId ResistParalysis("ResistParalysis"); - const MagicEffectId RemoveCurse("RemoveCurse"); - const MagicEffectId TurnUndead("TurnUndead"); - const MagicEffectId SummonScamp("SummonScamp"); - const MagicEffectId SummonClannfear("SummonClannfear"); - const MagicEffectId SummonDaedroth("SummonDaedroth"); - const MagicEffectId SummonDremora("SummonDremora"); - const MagicEffectId SummonAncestralGhost("SummonAncestralGhost"); - const MagicEffectId SummonSkeletalMinion("SummonSkeletalMinion"); - const MagicEffectId SummonBonewalker("SummonBonewalker"); - const MagicEffectId SummonGreaterBonewalker("SummonGreaterBonewalker"); - const MagicEffectId SummonBonelord("SummonBonelord"); - const MagicEffectId SummonWingedTwilight("SummonWingedTwilight"); - const MagicEffectId SummonHunger("SummonHunger"); - const MagicEffectId SummonGoldenSaint("SummonGoldenSaint"); - const MagicEffectId SummonFlameAtronach("SummonFlameAtronach"); - const MagicEffectId SummonFrostAtronach("SummonFrostAtronach"); - const MagicEffectId SummonStormAtronach("SummonStormAtronach"); - const MagicEffectId FortifyAttack("FortifyAttack"); - const MagicEffectId CommandCreature("CommandCreature"); - const MagicEffectId CommandHumanoid("CommandHumanoid"); - const MagicEffectId BoundDagger("BoundDagger"); - const MagicEffectId BoundLongsword("BoundLongsword"); - const MagicEffectId BoundMace("BoundMace"); - const MagicEffectId BoundBattleAxe("BoundBattleAxe"); - const MagicEffectId BoundSpear("BoundSpear"); - const MagicEffectId BoundLongbow("BoundLongbow"); - const MagicEffectId ExtraSpell("ExtraSpell"); - const MagicEffectId BoundCuirass("BoundCuirass"); - const MagicEffectId BoundHelm("BoundHelm"); - const MagicEffectId BoundBoots("BoundBoots"); - const MagicEffectId BoundShield("BoundShield"); - const MagicEffectId BoundGloves("BoundGloves"); - const MagicEffectId Corprus("Corprus"); - const MagicEffectId Vampirism("Vampirism"); - const MagicEffectId SummonCenturionSphere("SummonCenturionSphere"); - const MagicEffectId SunDamage("SunDamage"); - const MagicEffectId StuntedMagicka("StuntedMagicka"); + const MagicEffectId MagicEffect::WaterBreathing("WaterBreathing"); + const MagicEffectId MagicEffect::SwiftSwim("SwiftSwim"); + const MagicEffectId MagicEffect::WaterWalking("WaterWalking"); + const MagicEffectId MagicEffect::Shield("Shield"); + const MagicEffectId MagicEffect::FireShield("FireShield"); + const MagicEffectId MagicEffect::LightningShield("LightningShield"); + const MagicEffectId MagicEffect::FrostShield("FrostShield"); + const MagicEffectId MagicEffect::Burden("Burden"); + const MagicEffectId MagicEffect::Feather("Feather"); + const MagicEffectId MagicEffect::Jump("Jump"); + const MagicEffectId MagicEffect::Levitate("Levitate"); + const MagicEffectId MagicEffect::SlowFall("SlowFall"); + const MagicEffectId MagicEffect::Lock("Lock"); + const MagicEffectId MagicEffect::Open("Open"); + const MagicEffectId MagicEffect::FireDamage("FireDamage"); + const MagicEffectId MagicEffect::ShockDamage("ShockDamage"); + const MagicEffectId MagicEffect::FrostDamage("FrostDamage"); + const MagicEffectId MagicEffect::DrainAttribute("DrainAttribute"); + const MagicEffectId MagicEffect::DrainHealth("DrainHealth"); + const MagicEffectId MagicEffect::DrainMagicka("DrainMagicka"); + const MagicEffectId MagicEffect::DrainFatigue("DrainFatigue"); + const MagicEffectId MagicEffect::DrainSkill("DrainSkill"); + const MagicEffectId MagicEffect::DamageAttribute("DamageAttribute"); + const MagicEffectId MagicEffect::DamageHealth("DamageHealth"); + const MagicEffectId MagicEffect::DamageMagicka("DamageMagicka"); + const MagicEffectId MagicEffect::DamageFatigue("DamageFatigue"); + const MagicEffectId MagicEffect::DamageSkill("DamageSkill"); + const MagicEffectId MagicEffect::Poison("Poison"); + const MagicEffectId MagicEffect::WeaknessToFire("WeaknessToFire"); + const MagicEffectId MagicEffect::WeaknessToFrost("WeaknessToFrost"); + const MagicEffectId MagicEffect::WeaknessToShock("WeaknessToShock"); + const MagicEffectId MagicEffect::WeaknessToMagicka("WeaknessToMagicka"); + const MagicEffectId MagicEffect::WeaknessToCommonDisease("WeaknessToCommonDisease"); + const MagicEffectId MagicEffect::WeaknessToBlightDisease("WeaknessToBlightDisease"); + const MagicEffectId MagicEffect::WeaknessToCorprusDisease("WeaknessToCorprusDisease"); + const MagicEffectId MagicEffect::WeaknessToPoison("WeaknessToPoison"); + const MagicEffectId MagicEffect::WeaknessToNormalWeapons("WeaknessToNormalWeapons"); + const MagicEffectId MagicEffect::DisintegrateWeapon("DisintegrateWeapon"); + const MagicEffectId MagicEffect::DisintegrateArmor("DisintegrateArmor"); + const MagicEffectId MagicEffect::Invisibility("Invisibility"); + const MagicEffectId MagicEffect::Chameleon("Chameleon"); + const MagicEffectId MagicEffect::Light("Light"); + const MagicEffectId MagicEffect::Sanctuary("Sanctuary"); + const MagicEffectId MagicEffect::NightEye("NightEye"); + const MagicEffectId MagicEffect::Charm("Charm"); + const MagicEffectId MagicEffect::Paralyze("Paralyze"); + const MagicEffectId MagicEffect::Silence("Silence"); + const MagicEffectId MagicEffect::Blind("Blind"); + const MagicEffectId MagicEffect::Sound("Sound"); + const MagicEffectId MagicEffect::CalmHumanoid("CalmHumanoid"); + const MagicEffectId MagicEffect::CalmCreature("CalmCreature"); + const MagicEffectId MagicEffect::FrenzyHumanoid("FrenzyHumanoid"); + const MagicEffectId MagicEffect::FrenzyCreature("FrenzyCreature"); + const MagicEffectId MagicEffect::DemoralizeHumanoid("DemoralizeHumanoid"); + const MagicEffectId MagicEffect::DemoralizeCreature("DemoralizeCreature"); + const MagicEffectId MagicEffect::RallyHumanoid("RallyHumanoid"); + const MagicEffectId MagicEffect::RallyCreature("RallyCreature"); + const MagicEffectId MagicEffect::Dispel("Dispel"); + const MagicEffectId MagicEffect::Soultrap("Soultrap"); + const MagicEffectId MagicEffect::Telekinesis("Telekinesis"); + const MagicEffectId MagicEffect::Mark("Mark"); + const MagicEffectId MagicEffect::Recall("Recall"); + const MagicEffectId MagicEffect::DivineIntervention("DivineIntervention"); + const MagicEffectId MagicEffect::AlmsiviIntervention("AlmsiviIntervention"); + const MagicEffectId MagicEffect::DetectAnimal("DetectAnimal"); + const MagicEffectId MagicEffect::DetectEnchantment("DetectEnchantment"); + const MagicEffectId MagicEffect::DetectKey("DetectKey"); + const MagicEffectId MagicEffect::SpellAbsorption("SpellAbsorption"); + const MagicEffectId MagicEffect::Reflect("Reflect"); + const MagicEffectId MagicEffect::CureCommonDisease("CureCommonDisease"); + const MagicEffectId MagicEffect::CureBlightDisease("CureBlightDisease"); + const MagicEffectId MagicEffect::CureCorprusDisease("CureCorprusDisease"); + const MagicEffectId MagicEffect::CurePoison("CurePoison"); + const MagicEffectId MagicEffect::CureParalyzation("CureParalyzation"); + const MagicEffectId MagicEffect::RestoreAttribute("RestoreAttribute"); + const MagicEffectId MagicEffect::RestoreHealth("RestoreHealth"); + const MagicEffectId MagicEffect::RestoreMagicka("RestoreMagicka"); + const MagicEffectId MagicEffect::RestoreFatigue("RestoreFatigue"); + const MagicEffectId MagicEffect::RestoreSkill("RestoreSkill"); + const MagicEffectId MagicEffect::FortifyAttribute("FortifyAttribute"); + const MagicEffectId MagicEffect::FortifyHealth("FortifyHealth"); + const MagicEffectId MagicEffect::FortifyMagicka("FortifyMagicka"); + const MagicEffectId MagicEffect::FortifyFatigue("FortifyFatigue"); + const MagicEffectId MagicEffect::FortifySkill("FortifySkill"); + const MagicEffectId MagicEffect::FortifyMaximumMagicka("FortifyMaximumMagicka"); + const MagicEffectId MagicEffect::AbsorbAttribute("AbsorbAttribute"); + const MagicEffectId MagicEffect::AbsorbHealth("AbsorbHealth"); + const MagicEffectId MagicEffect::AbsorbMagicka("AbsorbMagicka"); + const MagicEffectId MagicEffect::AbsorbFatigue("AbsorbFatigue"); + const MagicEffectId MagicEffect::AbsorbSkill("AbsorbSkill"); + const MagicEffectId MagicEffect::ResistFire("ResistFire"); + const MagicEffectId MagicEffect::ResistFrost("ResistFrost"); + const MagicEffectId MagicEffect::ResistShock("ResistShock"); + const MagicEffectId MagicEffect::ResistMagicka("ResistMagicka"); + const MagicEffectId MagicEffect::ResistCommonDisease("ResistCommonDisease"); + const MagicEffectId MagicEffect::ResistBlightDisease("ResistBlightDisease"); + const MagicEffectId MagicEffect::ResistCorprusDisease("ResistCorprusDisease"); + const MagicEffectId MagicEffect::ResistPoison("ResistPoison"); + const MagicEffectId MagicEffect::ResistNormalWeapons("ResistNormalWeapons"); + const MagicEffectId MagicEffect::ResistParalysis("ResistParalysis"); + const MagicEffectId MagicEffect::RemoveCurse("RemoveCurse"); + const MagicEffectId MagicEffect::TurnUndead("TurnUndead"); + const MagicEffectId MagicEffect::SummonScamp("SummonScamp"); + const MagicEffectId MagicEffect::SummonClannfear("SummonClannfear"); + const MagicEffectId MagicEffect::SummonDaedroth("SummonDaedroth"); + const MagicEffectId MagicEffect::SummonDremora("SummonDremora"); + const MagicEffectId MagicEffect::SummonAncestralGhost("SummonAncestralGhost"); + const MagicEffectId MagicEffect::SummonSkeletalMinion("SummonSkeletalMinion"); + const MagicEffectId MagicEffect::SummonBonewalker("SummonBonewalker"); + const MagicEffectId MagicEffect::SummonGreaterBonewalker("SummonGreaterBonewalker"); + const MagicEffectId MagicEffect::SummonBonelord("SummonBonelord"); + const MagicEffectId MagicEffect::SummonWingedTwilight("SummonWingedTwilight"); + const MagicEffectId MagicEffect::SummonHunger("SummonHunger"); + const MagicEffectId MagicEffect::SummonGoldenSaint("SummonGoldenSaint"); + const MagicEffectId MagicEffect::SummonFlameAtronach("SummonFlameAtronach"); + const MagicEffectId MagicEffect::SummonFrostAtronach("SummonFrostAtronach"); + const MagicEffectId MagicEffect::SummonStormAtronach("SummonStormAtronach"); + const MagicEffectId MagicEffect::FortifyAttack("FortifyAttack"); + const MagicEffectId MagicEffect::CommandCreature("CommandCreature"); + const MagicEffectId MagicEffect::CommandHumanoid("CommandHumanoid"); + const MagicEffectId MagicEffect::BoundDagger("BoundDagger"); + const MagicEffectId MagicEffect::BoundLongsword("BoundLongsword"); + const MagicEffectId MagicEffect::BoundMace("BoundMace"); + const MagicEffectId MagicEffect::BoundBattleAxe("BoundBattleAxe"); + const MagicEffectId MagicEffect::BoundSpear("BoundSpear"); + const MagicEffectId MagicEffect::BoundLongbow("BoundLongbow"); + const MagicEffectId MagicEffect::ExtraSpell("ExtraSpell"); + const MagicEffectId MagicEffect::BoundCuirass("BoundCuirass"); + const MagicEffectId MagicEffect::BoundHelm("BoundHelm"); + const MagicEffectId MagicEffect::BoundBoots("BoundBoots"); + const MagicEffectId MagicEffect::BoundShield("BoundShield"); + const MagicEffectId MagicEffect::BoundGloves("BoundGloves"); + const MagicEffectId MagicEffect::Corprus("Corprus"); + const MagicEffectId MagicEffect::Vampirism("Vampirism"); + const MagicEffectId MagicEffect::SummonCenturionSphere("SummonCenturionSphere"); + const MagicEffectId MagicEffect::SunDamage("SunDamage"); + const MagicEffectId MagicEffect::StuntedMagicka("StuntedMagicka"); // Tribunal only - const MagicEffectId SummonFabricant("SummonFabricant"); + const MagicEffectId MagicEffect::SummonFabricant("SummonFabricant"); // Bloodmoon only - const MagicEffectId SummonWolf("SummonWolf"); - const MagicEffectId SummonBear("SummonBear"); - const MagicEffectId SummonBonewolf("SummonBonewolf"); - const MagicEffectId SummonCreature04("SummonCreature04"); - const MagicEffectId SummonCreature05("SummonCreature05"); + const MagicEffectId MagicEffect::SummonWolf("SummonWolf"); + const MagicEffectId MagicEffect::SummonBear("SummonBear"); + const MagicEffectId MagicEffect::SummonBonewolf("SummonBonewolf"); + const MagicEffectId MagicEffect::SummonCreature04("SummonCreature04"); + const MagicEffectId MagicEffect::SummonCreature05("SummonCreature05"); void MagicEffect::load(ESMReader& esm, bool& isDeleted) { From 495207e2a57c59e1605751bc962780ec31e5efaf Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Wed, 17 Dec 2025 18:25:41 -0500 Subject: [PATCH 10/14] Triune Tests of Strength, Block, and Breathing Water. The ending of the words is ALMSIVI. --- apps/openmw/mwmechanics/spelleffects.cpp | 33 +++++++++++++----------- apps/openmw/mwmechanics/spellutil.cpp | 6 +++-- apps/openmw_tests/mwworld/teststore.cpp | 2 ++ components/esm3/creaturestats.cpp | 2 +- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index 0706e9961b..fa851e8511 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -391,19 +391,22 @@ namespace return MWMechanics::MagicApplicationResult::Type::APPLIED; } - static const std::map sBoundItemsMap{ - { ESM::MagicEffect::BoundBattleAxe, "sMagicBoundBattleAxeID" }, - { ESM::MagicEffect::BoundBoots, "sMagicBoundBootsID" }, - { ESM::MagicEffect::BoundCuirass, "sMagicBoundCuirassID" }, - { ESM::MagicEffect::BoundDagger, "sMagicBoundDaggerID" }, - { ESM::MagicEffect::BoundGloves, "sMagicBoundLeftGauntletID" }, - { ESM::MagicEffect::BoundHelm, "sMagicBoundHelmID" }, - { ESM::MagicEffect::BoundLongbow, "sMagicBoundLongbowID" }, - { ESM::MagicEffect::BoundLongsword, "sMagicBoundLongswordID" }, - { ESM::MagicEffect::BoundMace, "sMagicBoundMaceID" }, - { ESM::MagicEffect::BoundShield, "sMagicBoundShieldID" }, - { ESM::MagicEffect::BoundSpear, "sMagicBoundSpearID" }, - }; + const std::map& getBoundItemsMap() { + static const std::map sBoundItemsMap{ + { ESM::MagicEffect::BoundBattleAxe, "sMagicBoundBattleAxeID" }, + { ESM::MagicEffect::BoundBoots, "sMagicBoundBootsID" }, + { ESM::MagicEffect::BoundCuirass, "sMagicBoundCuirassID" }, + { ESM::MagicEffect::BoundDagger, "sMagicBoundDaggerID" }, + { ESM::MagicEffect::BoundGloves, "sMagicBoundLeftGauntletID" }, + { ESM::MagicEffect::BoundHelm, "sMagicBoundHelmID" }, + { ESM::MagicEffect::BoundLongbow, "sMagicBoundLongbowID" }, + { ESM::MagicEffect::BoundLongsword, "sMagicBoundLongswordID" }, + { ESM::MagicEffect::BoundMace, "sMagicBoundMaceID" }, + { ESM::MagicEffect::BoundShield, "sMagicBoundShieldID" }, + { ESM::MagicEffect::BoundSpear, "sMagicBoundSpearID" }, + }; + return sBoundItemsMap; + } using SpellsPurge = void (MWMechanics::Spells::*)(); void purgePermanent(const MWWorld::Ptr& target, SpellsPurge method, ESM::Spell::SpellType type) @@ -668,7 +671,7 @@ namespace MWMechanics { if (!target.getClass().hasInventoryStore(target)) return ESM::ActiveEffect::Flag_Invalid; - const std::string& item = sBoundItemsMap.at(effect.mEffectId); + const std::string& item = getBoundItemsMap().at(effect.mEffectId); const MWWorld::Store& gmst = world->getStore().get(); const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); if (!addBoundItem(itemId, target)) @@ -1278,7 +1281,7 @@ namespace MWMechanics effect.mEffectId == ESM::MagicEffect::BoundBoots || effect.mEffectId == ESM::MagicEffect::BoundShield) { - const std::string& item = sBoundItemsMap.at(effect.mEffectId); + const std::string& item = getBoundItemsMap().at(effect.mEffectId); removeBoundItem( ESM::RefId::stringRefId(world->getStore().get().find(item)->mValue.getString()), target); diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 5ff67107a3..81985a80b0 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -172,11 +172,13 @@ namespace MWMechanics effect.mRange = ESM::RT_Self; effect.mArea = 0; - if (effect.mEffectID < 0) + if (effect.mEffectID.empty()) return std::nullopt; const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - const auto magicEffect = store.get().find(effect.mEffectID); + const auto magicEffect = store.get().search(effect.mEffectID); + if (!magicEffect) + return std::nullopt; const MWMechanics::CreatureStats& creatureStats = caster.getClass().getCreatureStats(caster); float x = (caster.getClass().getSkill(caster, ESM::Skill::Alchemy) diff --git a/apps/openmw_tests/mwworld/teststore.cpp b/apps/openmw_tests/mwworld/teststore.cpp index 54ca941aaa..8c9d1060b3 100644 --- a/apps/openmw_tests/mwworld/teststore.cpp +++ b/apps/openmw_tests/mwworld/teststore.cpp @@ -447,6 +447,8 @@ namespace refId = ESM::Attribute::Strength; else if constexpr (std::is_same_v) refId = ESM::Skill::Block; + else if constexpr (std::is_same_v) + refId = ESM::MagicEffect::WaterBreathing; else refId = ESM::StringRefId(stringId); diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index c747a33a5f..7901efaf75 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -250,7 +250,7 @@ namespace ESM for (const auto& [effectId, actor] : mSummonedCreatures) { - esm.writeHNT("SUMM", effectId); + esm.writeHNT("SUMM", ESM::MagicEffect::refIdToIndex(effectId)); esm.writeFormId(actor, true, "ACID"); } From 74ebb29dd06e3cbac051f6ac79c145a3ce5563fd Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Sat, 20 Dec 2025 17:04:58 -0500 Subject: [PATCH 11/14] - Used addRecordFunctionBinding in magic effect binding. - Changed find to search. - Restored Hyrum's Law use of effect system by Visible Rings. --- apps/opencs/model/doc/document.cpp | 2 -- apps/openmw/mwlua/magicbindings.cpp | 40 +++++++++------------------ apps/openmw/mwmechanics/character.cpp | 6 ++++ 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 7119268a27..66f847b787 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -289,9 +289,7 @@ void CSMDoc::Document::createBase() for (int i = 0; i < ESM::MagicEffect::Length; ++i) { ESM::MagicEffect record; - record.mId = *ESM::MagicEffect::indexToRefId(i).getIf(); - record.blank(); getData().getMagicEffects().add(record); diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index c31e4cdeb3..15d21034d6 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -263,23 +263,9 @@ namespace MWLua // MagicEffect store sol::table magicEffects(state, sol::create); + addRecordFunctionBinding(magicEffects, context); magicApi["effects"] = LuaUtil::makeReadOnly(magicEffects); - using MagicEffectStore = MWWorld::Store; - const MagicEffectStore* magicEffectStore - = &MWBase::Environment::get().getWorld()->getStore().get(); - auto magicEffectStoreT = state.new_usertype("ESM3_MagicEffectStore"); - magicEffectStoreT[sol::meta_function::to_string] = [](const MagicEffectStore& store) { - return "ESM3_MagicEffectStore{" + std::to_string(store.getSize()) + " effects}"; - }; - magicEffectStoreT[sol::meta_function::index] = sol::overload( - [](const MagicEffectStore& store, const ESM::RefId& id) -> const ESM::MagicEffect* { return store.search(id); }, - [](const MagicEffectStore& store, int id) -> const ESM::MagicEffect* { return store.search(ESM::MagicEffect::indexToRefId(id)); }, - [](const MagicEffectStore& store, std::string_view id) -> const ESM::MagicEffect* { - int index = ESM::MagicEffect::indexNameToIndex(id); - return store.search(ESM::MagicEffect::indexToRefId(index)); - }); - - magicEffects["records"] = magicEffectStore; + const auto* magicEffectStore = &MWBase::Environment::get().getWorld()->getStore().get(); // Spell record auto spellT = state.new_usertype("ESM3_Spell"); @@ -330,7 +316,7 @@ namespace MWLua }; effectParamsT["effect"] = sol::readonly_property( [magicEffectStore](const ESM::IndexedENAMstruct& params) -> const ESM::MagicEffect* { - return magicEffectStore->find(params.mData.mEffectID); + return magicEffectStore->search(params.mData.mEffectID); }); effectParamsT["id"] = sol::readonly_property([](const ESM::IndexedENAMstruct& params) -> std::string { auto name = ESM::MagicEffect::refIdToName(params.mData.mEffectID); @@ -447,7 +433,7 @@ namespace MWLua }); activeSpellEffectT["affectedSkill"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) return self.getSkillOrAttribute().serializeText(); else @@ -455,7 +441,7 @@ namespace MWLua }); activeSpellEffectT["affectedAttribute"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) return self.getSkillOrAttribute().serializeText(); else @@ -463,21 +449,21 @@ namespace MWLua }); activeSpellEffectT["magnitudeThisFrame"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMagnitude; }); activeSpellEffectT["minMagnitude"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMinMagnitude; }); activeSpellEffectT["maxMagnitude"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMaxMagnitude; @@ -487,7 +473,7 @@ namespace MWLua // Permanent/constant effects, abilities, etc. will have a negative duration if (self.mDuration < 0) return sol::nullopt; - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoDuration) return sol::nullopt; return self.mTimeLeft; @@ -497,7 +483,7 @@ namespace MWLua // Permanent/constant effects, abilities, etc. will have a negative duration if (self.mDuration < 0) return sol::nullopt; - auto* rec = magicEffectStore->find(self.mEffectId); + auto* rec = magicEffectStore->search(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoDuration) return sol::nullopt; return self.mDuration; @@ -580,14 +566,14 @@ namespace MWLua activeEffectT["affectedSkill"] = sol::readonly_property([magicEffectStore](const ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.key.mId); + auto* rec = magicEffectStore->search(self.key.mId); if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) return self.key.mArg.serializeText(); return sol::nullopt; }); activeEffectT["affectedAttribute"] = sol::readonly_property([magicEffectStore](const ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->find(self.key.mId); + auto* rec = magicEffectStore->search(self.key.mId); if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) return self.key.mArg.serializeText(); return sol::nullopt; @@ -944,7 +930,7 @@ namespace MWLua for (const ESM::IndexedENAMstruct& enam : enams) { - const ESM::MagicEffect* mgef = esmStore.get().find(enam.mData.mEffectID); + const ESM::MagicEffect* mgef = esmStore.get().search(enam.mData.mEffectID); MWMechanics::ActiveSpells::ActiveEffect effect; effect.mEffectId = enam.mData.mEffectID; effect.mArg = MWMechanics::EffectKey(enam.mData).mArg; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c30c865412..90be77fc12 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2807,6 +2807,12 @@ namespace MWMechanics { auto effectId = ESM::MagicEffectId(effectStr); + // This check is to preserve backward compatibility with mods that overload the magic effect + // system (e.g. using it as a mesh attachment system). This will need to be removed when effect + // dehardcoding is implemented so custom magic effect animations are processed correctly. + if (ESM::MagicEffect::refIdToIndex(effectId) < 0) + continue; + if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() || mPtr.getClass() .getCreatureStats(mPtr) From 8820c1b08b1a07a6af17e2867d1839247a1bed57 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Mon, 22 Dec 2025 18:06:47 -0500 Subject: [PATCH 12/14] Cleaned up loadmgef and addressed potential memory corruption issues to string view lifetimes. --- components/esm3/loadmgef.cpp | 200 +++++++++++++++++++++++++++++++---- components/esm3/loadmgef.hpp | 3 - 2 files changed, 177 insertions(+), 26 deletions(-) diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index 9033b2a62f..f8fa5e3e93 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -4,7 +4,6 @@ #include "esmwriter.hpp" #include "loadskil.hpp" -#include #include namespace ESM @@ -433,9 +432,8 @@ namespace ESM return {}; } - // Map effect ID to GMST name - const std::array MagicEffect::sGmstEffectIds = { - + // Map effect index to GMST name + const std::array sGmstEffectIds = { "sEffectWaterBreathing", "sEffectSwiftSwim", "sEffectWaterWalking", @@ -585,6 +583,156 @@ namespace ESM "sEffectSummonCreature05", }; + static const std::array sMagicEffectNames{ + "WaterBreathing", + "SwiftSwim", + "WaterWalking", + "Shield", + "FireShield", + "LightningShield", + "FrostShield", + "Burden", + "Feather", + "Jump", + "Levitate", + "SlowFall", + "Lock", + "Open", + "FireDamage", + "ShockDamage", + "FrostDamage", + "DrainAttribute", + "DrainHealth", + "DrainMagicka", + "DrainFatigue", + "DrainSkill", + "DamageAttribute", + "DamageHealth", + "DamageMagicka", + "DamageFatigue", + "DamageSkill", + "Poison", + "WeaknessToFire", + "WeaknessToFrost", + "WeaknessToShock", + "WeaknessToMagicka", + "WeaknessToCommonDisease", + "WeaknessToBlightDisease", + "WeaknessToCorprusDisease", + "WeaknessToPoison", + "WeaknessToNormalWeapons", + "DisintegrateWeapon", + "DisintegrateArmor", + "Invisibility", + "Chameleon", + "Light", + "Sanctuary", + "NightEye", + "Charm", + "Paralyze", + "Silence", + "Blind", + "Sound", + "CalmHumanoid", + "CalmCreature", + "FrenzyHumanoid", + "FrenzyCreature", + "DemoralizeHumanoid", + "DemoralizeCreature", + "RallyHumanoid", + "RallyCreature", + "Dispel", + "Soultrap", + "Telekinesis", + "Mark", + "Recall", + "DivineIntervention", + "AlmsiviIntervention", + "DetectAnimal", + "DetectEnchantment", + "DetectKey", + "SpellAbsorption", + "Reflect", + "CureCommonDisease", + "CureBlightDisease", + "CureCorprusDisease", + "CurePoison", + "CureParalyzation", + "RestoreAttribute", + "RestoreHealth", + "RestoreMagicka", + "RestoreFatigue", + "RestoreSkill", + "FortifyAttribute", + "FortifyHealth", + "FortifyMagicka", + "FortifyFatigue", + "FortifySkill", + "FortifyMaximumMagicka", + "AbsorbAttribute", + "AbsorbHealth", + "AbsorbMagicka", + "AbsorbFatigue", + "AbsorbSkill", + "ResistFire", + "ResistFrost", + "ResistShock", + "ResistMagicka", + "ResistCommonDisease", + "ResistBlightDisease", + "ResistCorprusDisease", + "ResistPoison", + "ResistNormalWeapons", + "ResistParalysis", + "RemoveCurse", + "TurnUndead", + "SummonScamp", + "SummonClannfear", + "SummonDaedroth", + "SummonDremora", + "SummonAncestralGhost", + "SummonSkeletalMinion", + "SummonBonewalker", + "SummonGreaterBonewalker", + "SummonBonelord", + "SummonWingedTwilight", + "SummonHunger", + "SummonGoldenSaint", + "SummonFlameAtronach", + "SummonFrostAtronach", + "SummonStormAtronach", + "FortifyAttack", + "CommandCreature", + "CommandHumanoid", + "BoundDagger", + "BoundLongsword", + "BoundMace", + "BoundBattleAxe", + "BoundSpear", + "BoundLongbow", + "ExtraSpell", + "BoundCuirass", + "BoundHelm", + "BoundBoots", + "BoundShield", + "BoundGloves", + "Corprus", + "Vampirism", + "SummonCenturionSphere", + "SunDamage", + "StuntedMagicka", + + // Tribunal only + "SummonFabricant", + + // Bloodmoon only + "SummonWolf", + "SummonBear", + "SummonBonewolf", + "SummonCreature04", + "SummonCreature05", + }; + static const std::array sMagicEffectIds { MagicEffect::WaterBreathing, @@ -734,7 +882,7 @@ namespace ESM MagicEffect::SummonBonewolf, MagicEffect::SummonCreature04, MagicEffect::SummonCreature05, - }; + }; template static std::map initStringToIntMap(const Collection& strings) @@ -746,8 +894,11 @@ namespace ESM return map; } - const std::map MagicEffect::sGmstEffectIdToIndexMap - = initStringToIntMap(MagicEffect::sGmstEffectIds); + const std::map sGmstEffectIdToIndexMap + = initStringToIntMap(sGmstEffectIds); + + const std::map sMagicEffectNamesToIndexMap + = initStringToIntMap(sMagicEffectNames); MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { @@ -805,16 +956,16 @@ namespace ESM return {}; int index = refIdToIndex(effectId); if (index < 0 || index >= Length) - return effectId.getRefIdString(); + return {}; return sGmstEffectIds[index]; } RefId MagicEffect::effectGmstIdToRefId(std::string_view gmstId) { - auto name = sGmstEffectIdToIndexMap.find(gmstId); - if (name == sGmstEffectIdToIndexMap.end()) + auto it = sGmstEffectIdToIndexMap.find(gmstId); + if (it == sGmstEffectIdToIndexMap.end()) return {}; - return sMagicEffectIds[name->second]; + return sMagicEffectIds[it->second]; } RefId MagicEffect::indexToRefId(int index) @@ -836,31 +987,34 @@ namespace ESM RefId MagicEffect::nameToRefId(std::string_view name) { - for (const RefId& effect : sMagicEffectIds) - if (effect.getRefIdString() == name) - return effect; - return {}; + auto it = sMagicEffectNamesToIndexMap.find(name); + if (it == sMagicEffectNamesToIndexMap.end()) + return {}; + return sMagicEffectIds[it->second]; } std::string_view MagicEffect::refIdToName(const RefId& effectId) { - if (!effectId.empty() && effectId.getIf()) - return effectId.getRefIdString(); - return {}; + if (effectId.empty()) + return {}; + auto it = sMagicEffectNamesToIndexMap.find(effectId.getRefIdString()); + if (it == sMagicEffectNamesToIndexMap.end()) + return {}; + return sMagicEffectNames[it->second]; } std::string_view MagicEffect::indexToName(int index) { if (index < 0 || index >= Length) return {}; - return sMagicEffectIds[index].getValue(); + return sMagicEffectNames[index]; } int MagicEffect::indexNameToIndex(std::string_view name) { - for (size_t i = 0; i < sMagicEffectIds.size(); ++i) - if (sMagicEffectIds[i].getValue() == name) - return static_cast(i); - return -1; + auto it = sMagicEffectNamesToIndexMap.find(name); + if (it == sMagicEffectNamesToIndexMap.end()) + return -1; + return it->second; } } diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index e732d2872e..23e3c11491 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -258,9 +258,6 @@ namespace ESM static constexpr int Length = 143; - static const std::array sGmstEffectIds; - static const std::map sGmstEffectIdToIndexMap; - static std::string_view refIdToGmstString(const RefId& effectId); static RefId effectGmstIdToRefId(std::string_view gmstId); From 9159788c9bc286e70a309138c85abf423303daa5 Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Mon, 22 Dec 2025 22:01:06 -0500 Subject: [PATCH 13/14] Address reviewer feedback and ran clang-format on modified files: - Changed reverse lookup map constructor for gmsts, names, and ESM::MagicEffectIds to be more generic. - Changed refIdToIndex to use map instead of array traversal. - Removed redundant check from refIdToGmstString. - Removed sMagicEffectNames and associated map. Removed dead code from loadmgef.cpp/hpp. - Changed return types of magic effect bindings to RefId. - Reverted ESMStore search to find. - Changed std::map to std::unordered_map for faster lookups in resistance and weakness functions. - Changed loadmgef To methods to pass RefId by value. - Removed StringRefId alias MagicEffectId. - Changed all effectId arguments in PR to pass-by-value. - Removed ESM::MagicEffect::refIdToName function. - Changed effect key type from long to ESM::RefId in OpGetEffect of mwscript. - Removed superfluous MWWorld::Store argument to MagicEffect store's setUp method. - Removed transformation code from decompose for ENAM and IRDT structs. - Changed resistance and weakness maps to initialization to use initialization instead of assignment. - Changed sGmstEffectIds to constexpr array since all parameters are known at compile time. - Changed getBoundItemsMap to use an unordered_map and have string_view values. - MagicEffect store definition removed (uses standard template class TypedDynamicStore). - Fixed bug in calculating sun damage that would have made vampires daywalkers in Mournhold. - Removed polymorphic EffectKey constructor and added overload to getOrDefault. - Placed ESM spec IRDT and ENAM structs in anonymous namespace. - Added exception if attempting to serialize ENAM subrecords with out of bounds index. - Added include format statements to satisfy Ubuntu CI linker. --- apps/esmtool/record.cpp | 7 +- apps/opencs/model/doc/document.cpp | 6 +- apps/opencs/model/world/collection.hpp | 7 - .../model/world/nestedcoladapterimp.hpp | 42 +- apps/opencs/model/world/refidadapterimp.cpp | 38 +- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/spellcreationdialog.cpp | 10 +- apps/openmw/mwgui/spellicons.cpp | 5 +- apps/openmw/mwgui/spellmodel.cpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 8 +- apps/openmw/mwgui/tooltips.hpp | 2 +- apps/openmw/mwgui/widgets.hpp | 12 +- apps/openmw/mwlua/magicbindings.cpp | 67 +- apps/openmw/mwmechanics/activespells.cpp | 7 +- apps/openmw/mwmechanics/activespells.hpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/alchemy.cpp | 7 +- apps/openmw/mwmechanics/character.cpp | 13 +- apps/openmw/mwmechanics/combat.cpp | 12 +- apps/openmw/mwmechanics/magiceffects.cpp | 7 +- apps/openmw/mwmechanics/magiceffects.hpp | 9 +- apps/openmw/mwmechanics/spellcasting.cpp | 10 +- apps/openmw/mwmechanics/spelleffects.cpp | 207 +++-- apps/openmw/mwmechanics/spellpriority.cpp | 142 ++-- apps/openmw/mwmechanics/spellresistance.cpp | 21 +- apps/openmw/mwmechanics/spellresistance.hpp | 6 +- apps/openmw/mwmechanics/spellutil.cpp | 4 +- apps/openmw/mwmechanics/summoning.cpp | 13 +- apps/openmw/mwmechanics/summoning.hpp | 6 +- apps/openmw/mwscript/miscextensions.cpp | 8 +- apps/openmw/mwscript/statsextensions.cpp | 7 +- apps/openmw/mwworld/esmstore.cpp | 10 +- apps/openmw/mwworld/store.cpp | 12 +- apps/openmw/mwworld/store.hpp | 13 +- components/esm3/activespells.cpp | 6 +- components/esm3/creaturestats.cpp | 3 +- components/esm3/effectlist.cpp | 62 +- components/esm3/loadingr.cpp | 75 +- components/esm3/loadmgef.cpp | 710 +++++++----------- components/esm3/loadmgef.hpp | 302 ++++---- components/esm3/magiceffects.hpp | 2 +- 41 files changed, 839 insertions(+), 1049 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 042bab900d..5832339dd8 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -155,8 +155,8 @@ namespace { int effectIdx = ESM::MagicEffect::refIdToIndex(effect.mData.mEffectID); if (effectIdx != -1) - std::cout << " Effect[" << i << "]: " << magicEffectLabel(effectIdx) << " (" - << effectIdx << ")" << std::endl; + std::cout << " Effect[" << i << "]: " << magicEffectLabel(effectIdx) << " (" << effectIdx << ")" + << std::endl; if (effect.mData.mSkill != -1) std::cout << " Skill: " << skillLabel(effect.mData.mSkill) << " (" << (int)effect.mData.mSkill << ")" << std::endl; @@ -850,8 +850,7 @@ namespace EsmTool continue; int effectIdx = ESM::MagicEffect::refIdToIndex(mData.mData.mEffectID[i]); - std::cout << " Effect: " << magicEffectLabel(effectIdx) << " (" << effectIdx - << ")" << std::endl; + std::cout << " Effect: " << magicEffectLabel(effectIdx) << " (" << effectIdx << ")" << std::endl; std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) << " (" << mData.mData.mSkills[i] << ")" << std::endl; std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i]) << " (" diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 66f847b787..327ef4e144 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -128,7 +128,7 @@ void CSMDoc::Document::addOptionalGlobals() void CSMDoc::Document::addOptionalMagicEffects() { - static const std::array optionalMagicEffects{ + static const std::array optionalMagicEffects{ ESM::MagicEffect::SummonFabricant, ESM::MagicEffect::SummonWolf, ESM::MagicEffect::SummonBear, @@ -136,7 +136,7 @@ void CSMDoc::Document::addOptionalMagicEffects() ESM::MagicEffect::SummonCreature04, ESM::MagicEffect::SummonCreature05, }; - for (const auto& effectId : optionalMagicEffects) + for (const auto effectId : optionalMagicEffects) { ESM::MagicEffect effect; effect.mId = effectId; @@ -289,7 +289,7 @@ void CSMDoc::Document::createBase() for (int i = 0; i < ESM::MagicEffect::Length; ++i) { ESM::MagicEffect record; - record.mId = *ESM::MagicEffect::indexToRefId(i).getIf(); + record.mId = ESM::MagicEffect::indexToRefId(i); record.blank(); getData().getMagicEffects().add(record); diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 8e4e7bde5c..227c36803b 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -73,13 +73,6 @@ namespace CSMWorld return ESM::RefId::stringRefId(Land::createUniqueRecordId(record.mX, record.mY)); } - inline void setRecordId(const ESM::RefId& id, ESM::MagicEffect& record) - { - if (const auto* effectId = id.getIf()) - record.mId = *effectId; - throw std::runtime_error("Invalid magic effect id: " + id.toDebugString()); - } - inline void setRecordId(const ESM::RefId& id, ESM::Skill& record) { if (const auto* skillId = id.getIf()) diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index ef1001c2dc..596341e029 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -324,22 +324,22 @@ namespace CSMWorld return QString::fromStdString(effect.mEffectID.getRefIdString()); case 1: { - if (effect.mEffectID == ESM::MagicEffect::DrainSkill || - effect.mEffectID == ESM::MagicEffect::DamageSkill || - effect.mEffectID == ESM::MagicEffect::RestoreSkill || - effect.mEffectID == ESM::MagicEffect::FortifySkill || - effect.mEffectID == ESM::MagicEffect::AbsorbSkill) + if (effect.mEffectID == ESM::MagicEffect::DrainSkill + || effect.mEffectID == ESM::MagicEffect::DamageSkill + || effect.mEffectID == ESM::MagicEffect::RestoreSkill + || effect.mEffectID == ESM::MagicEffect::FortifySkill + || effect.mEffectID == ESM::MagicEffect::AbsorbSkill) return effect.mSkill; else return QVariant(); } case 2: { - if (effect.mEffectID == ESM::MagicEffect::DrainAttribute || - effect.mEffectID == ESM::MagicEffect::DamageAttribute || - effect.mEffectID == ESM::MagicEffect::RestoreAttribute || - effect.mEffectID == ESM::MagicEffect::FortifyAttribute || - effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) + if (effect.mEffectID == ESM::MagicEffect::DrainAttribute + || effect.mEffectID == ESM::MagicEffect::DamageAttribute + || effect.mEffectID == ESM::MagicEffect::RestoreAttribute + || effect.mEffectID == ESM::MagicEffect::FortifyAttribute + || effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) return effect.mAttribute; else return QVariant(); @@ -373,18 +373,18 @@ namespace CSMWorld { case 0: { - effect.mEffectID = ESM::MagicEffectId(value.toString().toStdString()); - if (effect.mEffectID == ESM::MagicEffect::DrainSkill || - effect.mEffectID == ESM::MagicEffect::DamageSkill || - effect.mEffectID == ESM::MagicEffect::RestoreSkill || - effect.mEffectID == ESM::MagicEffect::FortifySkill || - effect.mEffectID == ESM::MagicEffect::AbsorbSkill) + effect.mEffectID = ESM::RefId::deserializeText(value.toString().toStdString()); + if (effect.mEffectID == ESM::MagicEffect::DrainSkill + || effect.mEffectID == ESM::MagicEffect::DamageSkill + || effect.mEffectID == ESM::MagicEffect::RestoreSkill + || effect.mEffectID == ESM::MagicEffect::FortifySkill + || effect.mEffectID == ESM::MagicEffect::AbsorbSkill) effect.mAttribute = -1; - else if (effect.mEffectID == ESM::MagicEffect::DrainAttribute || - effect.mEffectID == ESM::MagicEffect::DamageAttribute || - effect.mEffectID == ESM::MagicEffect::RestoreAttribute || - effect.mEffectID == ESM::MagicEffect::FortifyAttribute || - effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) + else if (effect.mEffectID == ESM::MagicEffect::DrainAttribute + || effect.mEffectID == ESM::MagicEffect::DamageAttribute + || effect.mEffectID == ESM::MagicEffect::RestoreAttribute + || effect.mEffectID == ESM::MagicEffect::FortifyAttribute + || effect.mEffectID == ESM::MagicEffect::AbsorbAttribute) effect.mSkill = -1; else { diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index d63f436838..4d4ca1ab6a 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -152,22 +152,18 @@ QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData( return effectId.empty() ? QVariant() : QString::fromStdString(effectId.getRefIdString()); case 1: { - if (effectId == ESM::MagicEffect::DrainSkill || - effectId == ESM::MagicEffect::DamageSkill || - effectId == ESM::MagicEffect::RestoreSkill || - effectId == ESM::MagicEffect::FortifySkill || - effectId == ESM::MagicEffect::AbsorbSkill) + if (effectId == ESM::MagicEffect::DrainSkill || effectId == ESM::MagicEffect::DamageSkill + || effectId == ESM::MagicEffect::RestoreSkill || effectId == ESM::MagicEffect::FortifySkill + || effectId == ESM::MagicEffect::AbsorbSkill) return record.get().mData.mSkills[subRowIndex]; else return QVariant(); } case 2: { - if (effectId == ESM::MagicEffect::DrainAttribute || - effectId == ESM::MagicEffect::DamageAttribute || - effectId == ESM::MagicEffect::RestoreAttribute || - effectId == ESM::MagicEffect::FortifyAttribute || - effectId == ESM::MagicEffect::AbsorbAttribute) + if (effectId == ESM::MagicEffect::DrainAttribute || effectId == ESM::MagicEffect::DamageAttribute + || effectId == ESM::MagicEffect::RestoreAttribute || effectId == ESM::MagicEffect::FortifyAttribute + || effectId == ESM::MagicEffect::AbsorbAttribute) return record.get().mData.mAttributes[subRowIndex]; else return QVariant(); @@ -188,24 +184,20 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedData( throw std::runtime_error("index out of range"); ESM::RefId effectId - = value.toString().isEmpty() ? ESM::RefId() : ESM::MagicEffectId(value.toString().toStdString()); + = value.toString().isEmpty() ? ESM::RefId() : ESM::RefId::deserializeText(value.toString().toStdString()); switch (subColIndex) { case 0: ingredient.mData.mEffectID[subRowIndex] = effectId; - if (effectId == ESM::MagicEffect::DrainSkill || - effectId == ESM::MagicEffect::DamageSkill || - effectId == ESM::MagicEffect::RestoreSkill || - effectId == ESM::MagicEffect::FortifySkill || - effectId == ESM::MagicEffect::AbsorbSkill) - ingredient.mData.mAttributes[subRowIndex] = -1; - else if (effectId == ESM::MagicEffect::DrainAttribute || - effectId == ESM::MagicEffect::DamageAttribute || - effectId == ESM::MagicEffect::RestoreAttribute || - effectId == ESM::MagicEffect::FortifyAttribute || - effectId == ESM::MagicEffect::AbsorbAttribute) - ingredient.mData.mSkills[subRowIndex] = -1; + if (effectId == ESM::MagicEffect::DrainSkill || effectId == ESM::MagicEffect::DamageSkill + || effectId == ESM::MagicEffect::RestoreSkill || effectId == ESM::MagicEffect::FortifySkill + || effectId == ESM::MagicEffect::AbsorbSkill) + ingredient.mData.mAttributes[subRowIndex] = -1; + else if (effectId == ESM::MagicEffect::DrainAttribute || effectId == ESM::MagicEffect::DamageAttribute + || effectId == ESM::MagicEffect::RestoreAttribute || effectId == ESM::MagicEffect::FortifyAttribute + || effectId == ESM::MagicEffect::AbsorbAttribute) + ingredient.mData.mSkills[subRowIndex] = -1; else { ingredient.mData.mSkills[subRowIndex] = -1; diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 4afd2ed18a..b1a23f811e 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -461,7 +461,7 @@ namespace MWGui Widgets::SpellEffectParams params; params.mEffectID = effectKey.mId; const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().search(effectKey.mId); + = MWBase::Environment::get().getESMStore()->get().find(effectKey.mId); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) params.mSkill = effectKey.mArg; else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 01722d2f3f..69ed7132bd 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -1,5 +1,7 @@ #include "spellcreationdialog.hpp" +#include + #include #include #include @@ -33,7 +35,7 @@ namespace { - bool sortMagicEffects(const ESM::RefId& id1, const ESM::RefId& id2) + bool sortMagicEffects(ESM::RefId id1, ESM::RefId id2) { const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); @@ -222,7 +224,7 @@ namespace MWGui mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); - mEffectName->setCaptionWithReplacing("#{" + std::string(ESM::MagicEffect::refIdToGmstString(effect->mId)) + "}"); + mEffectName->setCaptionWithReplacing(std::format("#{{{}}}", ESM::MagicEffect::refIdToGmstString(effect->mId))); mEffect.mEffectID = effect->mId; @@ -812,7 +814,7 @@ namespace MWGui mAvailableEffectsList->clear(); int i = 0; - for (const ESM::RefId& effectId : knownEffects) + for (const auto effectId : knownEffects) { mAvailableEffectsList->addItem(MWBase::Environment::get() .getESMStore() @@ -826,7 +828,7 @@ namespace MWGui mAvailableEffectsList->scrollToTop(); mAvailableButtons.clear(); - for (const ESM::RefId& effectId : knownEffects) + for (const auto effectId : knownEffects) { const std::string& name = MWBase::Environment::get() .getESMStore() diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index deb7df5be9..468d147994 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -1,5 +1,6 @@ #include "spellicons.hpp" +#include #include #include @@ -154,10 +155,8 @@ namespace MWGui Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); - const std::string& name = std::string(ESM::MagicEffect::refIdToGmstString(effectId)); - ToolTipInfo tooltipInfo; - tooltipInfo.caption = "#{" + name + "}"; + tooltipInfo.caption = std::format("#{{{}}}", ESM::MagicEffect::refIdToGmstString(effectId)); tooltipInfo.icon = effect->mIcon; tooltipInfo.imageSize = 16; tooltipInfo.wordWrap = false; diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 711761e655..f390e7ff50 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -52,7 +52,7 @@ namespace MWGui if (!effectId.empty()) { - const ESM::MagicEffect* magicEffect = store.get().search(effectId); + const ESM::MagicEffect* magicEffect = store.get().find(effectId); const ESM::Attribute* attribute = store.get().search(ESM::Attribute::indexToRefId(effect.mData.mAttribute)); const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(effect.mData.mSkill)); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 607614bec6..4922aefe4d 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -954,11 +954,10 @@ namespace MWGui widget->setUserString("ToolTipLayout", "ClassToolTip"); } - void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, const ESM::RefId& id) + void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, ESM::RefId effectId) { const auto& store = MWBase::Environment::get().getESMStore(); - const ESM::MagicEffect* effect = store->get().find(id); - const std::string& name = std::string(ESM::MagicEffect::refIdToGmstString(id)); + const ESM::MagicEffect* effect = store->get().find(effectId); std::string icon = effect->mIcon; icon.insert(icon.rfind('\\') + 1, "b_"); @@ -967,7 +966,8 @@ namespace MWGui widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); - widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); + widget->setUserString( + "Caption_MagicEffectName", std::format("#{{{}}}", ESM::MagicEffect::refIdToGmstString(effectId))); widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 3c144f13cf..3665e6a38c 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -98,7 +98,7 @@ namespace MWGui static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); static void createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass); - static void createMagicEffectToolTip(MyGUI::Widget* widget, const ESM::RefId& id); + static void createMagicEffectToolTip(MyGUI::Widget* widget, ESM::RefId effectId); bool checkOwned(); /// Returns True if taking mFocusObject would be crime diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 6c233ef6a8..b45eb531f1 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include namespace MyGUI { @@ -41,7 +41,6 @@ namespace MWGui , mIsConstant(false) , mNoMagnitude(false) , mKnown(true) - , mEffectID(ESM::RefId()) , mMagnMin(-1) , mMagnMax(-1) , mRange(-1) @@ -71,15 +70,12 @@ namespace MWGui return false; bool involvesAttribute = (mEffectID == ESM::MagicEffect::RestoreAttribute - || mEffectID == ESM::MagicEffect::AbsorbAttribute - || mEffectID == ESM::MagicEffect::DrainAttribute + || mEffectID == ESM::MagicEffect::AbsorbAttribute || mEffectID == ESM::MagicEffect::DrainAttribute || mEffectID == ESM::MagicEffect::FortifyAttribute || mEffectID == ESM::MagicEffect::DamageAttribute); bool involvesSkill = (mEffectID == ESM::MagicEffect::RestoreSkill - || mEffectID == ESM::MagicEffect::AbsorbSkill - || mEffectID == ESM::MagicEffect::DrainSkill - || mEffectID == ESM::MagicEffect::FortifySkill - || mEffectID == ESM::MagicEffect::DamageSkill); + || mEffectID == ESM::MagicEffect::AbsorbSkill || mEffectID == ESM::MagicEffect::DrainSkill + || mEffectID == ESM::MagicEffect::FortifySkill || mEffectID == ESM::MagicEffect::DamageSkill); return ((other.mSkill == mSkill) || !involvesSkill) && ((other.mAttribute == mAttribute) && !involvesAttribute) && (other.mArea == mArea); } diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index 15d21034d6..add12b37ad 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -1,5 +1,7 @@ #include "magicbindings.hpp" +#include + #include #include #include @@ -245,10 +247,10 @@ namespace MWLua sol::table effect(state, sol::create); magicApi["EFFECT_TYPE"] = LuaUtil::makeStrictReadOnly(effect); - for (size_t i = 0; i < ESM::MagicEffect::Length; ++i) + for (int i = 0; i < ESM::MagicEffect::Length; ++i) { - std::string_view name = ESM::MagicEffect::indexToName(static_cast(i)); - effect[name] = Misc::StringUtils::lowerCase(name); + std::string_view name = ESM::MagicEffect::indexToName(i); + effect[name] = ESM::MagicEffect::indexToRefId(i).serializeText(); } // Spell store @@ -311,17 +313,15 @@ namespace MWLua // Effect params auto effectParamsT = state.new_usertype("ESM3_EffectParams"); effectParamsT[sol::meta_function::to_string] = [magicEffectStore](const ESM::IndexedENAMstruct& params) { - const ESM::MagicEffect* const rec = magicEffectStore->search(params.mData.mEffectID); - return "ESM3_EffectParams[" + std::string(ESM::MagicEffect::refIdToGmstString(rec->mId)) + "]"; + const ESM::MagicEffect* const rec = magicEffectStore->find(params.mData.mEffectID); + return std::format("ESM3_EffectParams[{}]", ESM::MagicEffect::refIdToGmstString(rec->mId)); }; effectParamsT["effect"] = sol::readonly_property( [magicEffectStore](const ESM::IndexedENAMstruct& params) -> const ESM::MagicEffect* { - return magicEffectStore->search(params.mData.mEffectID); + return magicEffectStore->find(params.mData.mEffectID); }); - effectParamsT["id"] = sol::readonly_property([](const ESM::IndexedENAMstruct& params) -> std::string { - auto name = ESM::MagicEffect::refIdToName(params.mData.mEffectID); - return Misc::StringUtils::lowerCase(name); - }); + effectParamsT["id"] = sol::readonly_property( + [](const ESM::IndexedENAMstruct& params) -> ESM::RefId { return params.mData.mEffectID; }); effectParamsT["affectedSkill"] = sol::readonly_property([](const ESM::IndexedENAMstruct& params) -> sol::optional { ESM::RefId id = ESM::Skill::indexToRefId(params.mData.mSkill); @@ -353,12 +353,9 @@ namespace MWLua auto magicEffectT = state.new_usertype("ESM3_MagicEffect"); magicEffectT[sol::meta_function::to_string] = [](const ESM::MagicEffect& rec) { - return "ESM3_MagicEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(rec.mId)) + "]"; + return std::format("ESM3_MagicEffect[{}]", ESM::MagicEffect::refIdToGmstString(rec.mId)); }; - magicEffectT["id"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { - auto name = ESM::MagicEffect::refIdToName(rec.mId); - return Misc::StringUtils::lowerCase(name); - }); + magicEffectT["id"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> ESM::RefId { return rec.mId; }); magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); @@ -420,12 +417,10 @@ namespace MWLua auto activeSpellEffectT = state.new_usertype("ActiveSpellEffect"); activeSpellEffectT[sol::meta_function::to_string] = [](const ESM::ActiveEffect& self) { - return "ActiveSpellEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(self.mEffectId)) + "]"; + return std::format("ActiveSpellEffect[{}]", ESM::MagicEffect::refIdToGmstString(self.mEffectId)); }; - activeSpellEffectT["id"] = sol::readonly_property([](const ESM::ActiveEffect& self) -> std::string { - auto name = ESM::MagicEffect::refIdToName(self.mEffectId); - return Misc::StringUtils::lowerCase(name); - }); + activeSpellEffectT["id"] + = sol::readonly_property([](const ESM::ActiveEffect& self) -> ESM::RefId { return self.mEffectId; }); activeSpellEffectT["index"] = sol::readonly_property([](const ESM::ActiveEffect& self) -> int { return self.mEffectIndex; }); activeSpellEffectT["name"] = sol::readonly_property([](const ESM::ActiveEffect& self) -> std::string { @@ -433,7 +428,7 @@ namespace MWLua }); activeSpellEffectT["affectedSkill"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) return self.getSkillOrAttribute().serializeText(); else @@ -441,7 +436,7 @@ namespace MWLua }); activeSpellEffectT["affectedAttribute"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) return self.getSkillOrAttribute().serializeText(); else @@ -449,21 +444,21 @@ namespace MWLua }); activeSpellEffectT["magnitudeThisFrame"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMagnitude; }); activeSpellEffectT["minMagnitude"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMinMagnitude; }); activeSpellEffectT["maxMagnitude"] = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoMagnitude) return sol::nullopt; return self.mMaxMagnitude; @@ -473,7 +468,7 @@ namespace MWLua // Permanent/constant effects, abilities, etc. will have a negative duration if (self.mDuration < 0) return sol::nullopt; - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoDuration) return sol::nullopt; return self.mTimeLeft; @@ -483,7 +478,7 @@ namespace MWLua // Permanent/constant effects, abilities, etc. will have a negative duration if (self.mDuration < 0) return sol::nullopt; - auto* rec = magicEffectStore->search(self.mEffectId); + auto* rec = magicEffectStore->find(self.mEffectId); if (rec->mData.mFlags & ESM::MagicEffect::Flags::NoDuration) return sol::nullopt; return self.mDuration; @@ -555,25 +550,23 @@ namespace MWLua auto activeEffectT = state.new_usertype("ActiveEffect"); activeEffectT[sol::meta_function::to_string] = [](const ActiveEffect& self) { - return "ActiveEffect[" + std::string(ESM::MagicEffect::refIdToGmstString(self.key.mId)) + "]"; + return std::format("ActiveEffect[{}]", ESM::MagicEffect::refIdToGmstString(self.key.mId)); }; - activeEffectT["id"] = sol::readonly_property([](const ActiveEffect& self) -> std::string { - auto name = ESM::MagicEffect::refIdToName(self.key.mId); - return Misc::StringUtils::lowerCase(name); - }); + activeEffectT["id"] + = sol::readonly_property([](const ActiveEffect& self) -> ESM::RefId { return self.key.mId; }); activeEffectT["name"] = sol::readonly_property([](const ActiveEffect& self) -> std::string { return self.key.toString(); }); activeEffectT["affectedSkill"] = sol::readonly_property([magicEffectStore](const ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.key.mId); + auto* rec = magicEffectStore->find(self.key.mId); if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) return self.key.mArg.serializeText(); return sol::nullopt; }); activeEffectT["affectedAttribute"] = sol::readonly_property([magicEffectStore](const ActiveEffect& self) -> sol::optional { - auto* rec = magicEffectStore->search(self.key.mId); + auto* rec = magicEffectStore->find(self.key.mId); if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) return self.key.mArg.serializeText(); return sol::nullopt; @@ -930,7 +923,7 @@ namespace MWLua for (const ESM::IndexedENAMstruct& enam : enams) { - const ESM::MagicEffect* mgef = esmStore.get().search(enam.mData.mEffectID); + const ESM::MagicEffect* mgef = esmStore.get().find(enam.mData.mEffectID); MWMechanics::ActiveSpells::ActiveEffect effect; effect.mEffectId = enam.mData.mEffectID; effect.mArg = MWMechanics::EffectKey(enam.mData).mArg; @@ -991,8 +984,8 @@ namespace MWLua auto getEffectKey = [](std::string_view idStr, sol::optional argStr) -> MWMechanics::EffectKey { - auto id = ESM::MagicEffectId(idStr); - auto* rec = MWBase::Environment::get().getWorld()->getStore().get().search(id); + auto id = ESM::RefId::deserializeText(idStr); + auto* rec = MWBase::Environment::get().getWorld()->getStore().get().find(id); MWMechanics::EffectKey key = MWMechanics::EffectKey(id); diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 81f1a9ae1d..ff52465180 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -358,7 +358,7 @@ namespace MWMechanics if (Settings::game().mClassicCalmSpellsBehavior) { - ESM::MagicEffectId effect + ESM::RefId effect = ptr.getClass().isNpc() ? ESM::MagicEffect::CalmHumanoid : ESM::MagicEffect::CalmCreature; if (creatureStats.getMagicEffects().getOrDefault(effect).getMagnitude() > 0.f) creatureStats.getAiSequence().stopCombat(); @@ -426,8 +426,7 @@ namespace MWMechanics { const VFS::Path::Normalized reflectStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(reflectStatic->mModel)); - animation->addEffect( - reflectStaticModel, ESM::MagicEffect::refIdToName(ESM::MagicEffect::Reflect), false); + animation->addEffect(reflectStaticModel, ESM::MagicEffect::Reflect.getValue(), false); } caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell(*reflected); } @@ -646,7 +645,7 @@ namespace MWMechanics purge([=](const ActiveSpellParams& params) { return params.mActiveSpellId == id; }, ptr); } - void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, const ESM::RefId& effectId, ESM::RefId effectArg) + void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, 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 6a82643e66..dcc37521f0 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::RefId& effectId, ESM::RefId effectArg = {}); + void purgeEffect(const MWWorld::Ptr& ptr, 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/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a2695122a7..796a46fda8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -796,7 +796,7 @@ namespace MWMechanics continue; for (const auto& effect : spell.getEffects()) { - static const std::array damageEffects{ + static const std::array damageEffects{ ESM::MagicEffect::FireDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::FrostDamage, @@ -1817,7 +1817,7 @@ namespace MWMechanics // Make sure spell effects are removed purgeSpellEffects(actor.getPtr().getCellRef().getRefNum()); - stats.getMagicEffects().add(ESM::MagicEffect::Vampirism, vampirism); + stats.getMagicEffects().add(EffectKey(ESM::MagicEffect::Vampirism), vampirism); if (isPlayer) { diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index a6534d9c93..f748988e42 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -35,7 +36,7 @@ namespace 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].getIf(), arg); + return MWMechanics::EffectKey(ingredient.mData.mEffectID[i], arg); } bool containsEffect(const ESM::Ingredient& ingredient, const MWMechanics::EffectKey& effect) @@ -172,11 +173,11 @@ void MWMechanics::Alchemy::updateEffects() for (const auto& effectKey : effects) { const ESM::MagicEffect* magicEffect - = MWBase::Environment::get().getESMStore()->get().search(effectKey.mId); + = MWBase::Environment::get().getESMStore()->get().find(effectKey.mId); if (magicEffect->mData.mBaseCost <= 0) { - const std::string os = "invalid base cost for magic effect " + std::string(ESM::MagicEffect::refIdToName(effectKey.mId)); + const std::string os = std::format("invalid base cost for magic effect {}", effectKey.mId.getRefIdString()); throw std::runtime_error(os); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 90be77fc12..e141c917e5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2805,21 +2805,16 @@ namespace MWMechanics for (std::string_view effectStr : effects) { - auto effectId = ESM::MagicEffectId(effectStr); + auto effectId = ESM::RefId::deserializeText(effectStr); - // This check is to preserve backward compatibility with mods that overload the magic effect - // system (e.g. using it as a mesh attachment system). This will need to be removed when effect - // dehardcoding is implemented so custom magic effect animations are processed correctly. - if (ESM::MagicEffect::refIdToIndex(effectId) < 0) - continue; - - if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() + if (MWBase::Environment::get().getESMStore()->get().search(effectId) + && (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished() || mPtr.getClass() .getCreatureStats(mPtr) .getMagicEffects() .getOrDefault(MWMechanics::EffectKey(effectId)) .getMagnitude() - <= 0) + <= 0)) mAnimation->removeEffect(effectStr); } } diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 2a9aeddb2e..dbb31f2a34 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -350,14 +350,14 @@ namespace MWMechanics if (godmode) return; auto& prng = MWBase::Environment::get().getWorld()->getPrng(); - static const std::array elementalShieldEffects{ ESM::MagicEffect::FireShield, + static const std::array elementalShieldEffects{ ESM::MagicEffect::FireShield, ESM::MagicEffect::LightningShield, ESM::MagicEffect::FrostShield }; - for (int i = 0; i < 3; ++i) + for (const auto elementalShieldEffect : elementalShieldEffects) { float magnitude = victim.getClass() .getCreatureStats(victim) .getMagicEffects() - .getOrDefault(elementalShieldEffects[i]) + .getOrDefault(elementalShieldEffect) .getMagnitude(); if (!magnitude) @@ -377,10 +377,10 @@ namespace MWMechanics float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99(prng)); - ESM::MagicEffectId element = ESM::MagicEffect::FireDamage; - if (i == 1) + ESM::RefId element = ESM::MagicEffect::FireDamage; + if (elementalShieldEffect == ESM::MagicEffect::LightningShield) element = ESM::MagicEffect::ShockDamage; - if (i == 2) + if (elementalShieldEffect == ESM::MagicEffect::FrostShield) element = ESM::MagicEffect::FrostDamage; float elementResistance diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index 046e77ab42..f9a84cde17 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -48,7 +48,7 @@ namespace MWMechanics std::string EffectKey::toString() const { const auto& store = MWBase::Environment::get().getESMStore(); - const ESM::MagicEffect* magicEffect = store->get().search(mId); + const ESM::MagicEffect* magicEffect = store->get().find(mId); return getMagicEffectString( *magicEffect, store->get().search(mArg), store->get().search(mArg)); } @@ -139,6 +139,11 @@ namespace MWMechanics return get(key).value_or(EffectParam()); } + EffectParam MagicEffects::getOrDefault(ESM::RefId effectId) const + { + return getOrDefault(EffectKey(effectId)); + } + std::optional MagicEffects::get(const EffectKey& key) const { Collection::const_iterator iter = mCollection.find(key); diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 491f011807..652bb13905 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -15,8 +15,6 @@ namespace ESM struct MagicEffect; struct MagicEffects; struct Skill; - - using MagicEffectId = StringRefId; } namespace MWMechanics @@ -34,12 +32,6 @@ namespace MWMechanics { } - EffectKey(ESM::MagicEffectId id, ESM::RefId arg = {}) - : mId(id) - , mArg(arg) - { - } - EffectKey(const ESM::ENAMstruct& effect); std::string toString() const; @@ -115,6 +107,7 @@ namespace MWMechanics void modifyBase(const EffectKey& key, int diff); EffectParam getOrDefault(const EffectKey& key) const; + EffectParam getOrDefault(ESM::RefId effectId) const; std::optional get(const EffectKey& key) const; ///< This function can safely be used for keys that are not present. }; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 398ad09d2b..c7571c35ac 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -321,7 +321,7 @@ namespace MWMechanics if (!enchantment->mEffects.mList.empty()) { ESM::RefId effectId = enchantment->mEffects.mList.front().mData.mEffectID; - const ESM::MagicEffect* magicEffect = store->get().search(effectId); + const ESM::MagicEffect* magicEffect = store->get().find(effectId); school = magicEffect->mData.mSchool; } @@ -509,8 +509,8 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); if (animation) { - animation->addEffect(castStaticModel.value(), ESM::MagicEffect::refIdToName(effect->mId), false, {}, - effect->mParticle); + animation->addEffect( + castStaticModel.value(), effect->mId.getRefIdString(), false, {}, effect->mParticle); } else { @@ -585,8 +585,8 @@ namespace MWMechanics { const VFS::Path::Normalized castStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(castStatic->mModel)); - anim->addEffect(castStaticModel.value(), ESM::MagicEffect::refIdToName(magicEffect.mId), loop, {}, - magicEffect.mParticle); + anim->addEffect( + castStaticModel.value(), magicEffect.mId.getRefIdString(), loop, {}, magicEffect.mParticle); } } } diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index fa851e8511..f62609d6e5 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -54,7 +54,7 @@ namespace } ESM::ActiveEffect::Flags modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, - const ESM::RefId& creatureEffect, MWMechanics::AiSetting setting, float magnitude) + ESM::RefId creatureEffect, MWMechanics::AiSetting setting, float magnitude) { if (target == MWMechanics::getPlayer() || (effect.mEffectId == creatureEffect) == target.getClass().isNpc()) return ESM::ActiveEffect::Flag_Invalid; @@ -298,8 +298,7 @@ namespace { const VFS::Path::Normalized absorbStaticModel = Misc::ResourceHelpers::correctMeshPath(VFS::Path::Normalized(absorbStatic->mModel)); - animation->addEffect( - absorbStaticModel.value(), ESM::MagicEffect::refIdToName(ESM::MagicEffect::SpellAbsorption), false); + animation->addEffect(absorbStaticModel.value(), ESM::MagicEffect::SpellAbsorption.getValue(), false); } int spellCost = 0; @@ -372,7 +371,8 @@ namespace { const ESM::Spell* spell = spellParams.hasFlag(ESM::ActiveSpells::Flag_Temporary) ? spellParams.getSpell() : nullptr; - float magnitudeMult = MWMechanics::getEffectMultiplier(effect.mEffectId, target, caster, spell, &magnitudes); + float magnitudeMult + = MWMechanics::getEffectMultiplier(effect.mEffectId, target, caster, spell, &magnitudes); if (magnitudeMult == 0) { // Fully resisted, show message @@ -391,13 +391,13 @@ namespace return MWMechanics::MagicApplicationResult::Type::APPLIED; } - const std::map& getBoundItemsMap() { - static const std::map sBoundItemsMap{ + const std::unordered_map& getBoundItemsMap() + { + static const std::unordered_map sBoundItemsMap{ { ESM::MagicEffect::BoundBattleAxe, "sMagicBoundBattleAxeID" }, { ESM::MagicEffect::BoundBoots, "sMagicBoundBootsID" }, { ESM::MagicEffect::BoundCuirass, "sMagicBoundCuirassID" }, { ESM::MagicEffect::BoundDagger, "sMagicBoundDaggerID" }, - { ESM::MagicEffect::BoundGloves, "sMagicBoundLeftGauntletID" }, { ESM::MagicEffect::BoundHelm, "sMagicBoundHelmID" }, { ESM::MagicEffect::BoundLongbow, "sMagicBoundLongbowID" }, { ESM::MagicEffect::BoundLongsword, "sMagicBoundLongswordID" }, @@ -442,11 +442,9 @@ namespace MWMechanics else if (effect.mEffectId == ESM::MagicEffect::RemoveCurse) purgePermanent(target, &Spells::purgeCurses, ESM::Spell::ST_Curse); else if (effect.mEffectId == ESM::MagicEffect::CureCorprusDisease) - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( - target, ESM::MagicEffect::Corprus); + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(target, ESM::MagicEffect::Corprus); else if (effect.mEffectId == ESM::MagicEffect::CurePoison) - target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( - target, ESM::MagicEffect::Poison); + target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(target, ESM::MagicEffect::Poison); else if (effect.mEffectId == ESM::MagicEffect::CureParalyzation) target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect( target, ESM::MagicEffect::Paralyze); @@ -479,7 +477,7 @@ namespace MWMechanics if (!caster.isEmpty()) { MWRender::Animation* anim = world->getAnimation(caster); - anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); + anim->removeEffect(effect.mEffectId.getRefIdString()); const ESM::Static* fx = world->getStore().get().search(ESM::RefId::stringRefId("VFX_Summon_end")); if (fx != nullptr) @@ -520,7 +518,7 @@ namespace MWMechanics if (!caster.isEmpty()) { MWRender::Animation* anim = world->getAnimation(caster); - anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); + anim->removeEffect(effect.mEffectId.getRefIdString()); } } } @@ -580,13 +578,15 @@ namespace MWMechanics creatureStats.setAiSetting(AiSetting::Flee, stat); } } - else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature || effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature + || effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) return modifyAiSetting( target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, effect.mMagnitude); - else if (effect.mEffectId == ESM::MagicEffect::CalmCreature || effect.mEffectId == ESM::MagicEffect::CalmHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::CalmCreature + || effect.mEffectId == ESM::MagicEffect::CalmHumanoid) { - ESM::ActiveEffect::Flags applied = modifyAiSetting( - target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude); + ESM::ActiveEffect::Flags applied + = modifyAiSetting(target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, -effect.mMagnitude); if (applied != ESM::ActiveEffect::Flag_Applied) return applied; if (effect.mMagnitude > 0) @@ -595,10 +595,12 @@ namespace MWMechanics creatureStats.getAiSequence().stopCombat(); } } - else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature || effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature + || effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) return modifyAiSetting( target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, effect.mMagnitude); - else if (effect.mEffectId == ESM::MagicEffect::RallyCreature || effect.mEffectId == ESM::MagicEffect::RallyHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::RallyCreature + || effect.mEffectId == ESM::MagicEffect::RallyHumanoid) return modifyAiSetting( target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, -effect.mMagnitude); else if (effect.mEffectId == ESM::MagicEffect::Charm) @@ -618,28 +620,26 @@ namespace MWMechanics MWSound::PlayMode::LoopNoEnv); } } - else if (effect.mEffectId == ESM::MagicEffect::SummonScamp || - effect.mEffectId == ESM::MagicEffect::SummonClannfear || - effect.mEffectId == ESM::MagicEffect::SummonDaedroth || - effect.mEffectId == ESM::MagicEffect::SummonDremora || - effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost || - effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion || - effect.mEffectId == ESM::MagicEffect::SummonBonewalker || - effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker || - effect.mEffectId == ESM::MagicEffect::SummonBonelord || - effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight || - effect.mEffectId == ESM::MagicEffect::SummonHunger || - effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint || - effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach || - effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach || - effect.mEffectId == ESM::MagicEffect::SummonStormAtronach || - effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere || - effect.mEffectId == ESM::MagicEffect::SummonFabricant || - effect.mEffectId == ESM::MagicEffect::SummonWolf || - effect.mEffectId == ESM::MagicEffect::SummonBear || - effect.mEffectId == ESM::MagicEffect::SummonBonewolf || - effect.mEffectId == ESM::MagicEffect::SummonCreature04 || - effect.mEffectId == ESM::MagicEffect::SummonCreature05) + else if (effect.mEffectId == ESM::MagicEffect::SummonScamp + || effect.mEffectId == ESM::MagicEffect::SummonClannfear + || effect.mEffectId == ESM::MagicEffect::SummonDaedroth + || effect.mEffectId == ESM::MagicEffect::SummonDremora + || effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost + || effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion + || effect.mEffectId == ESM::MagicEffect::SummonBonewalker + || effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker + || effect.mEffectId == ESM::MagicEffect::SummonBonelord + || effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight + || effect.mEffectId == ESM::MagicEffect::SummonHunger + || effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint + || effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach + || effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach + || effect.mEffectId == ESM::MagicEffect::SummonStormAtronach + || effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere + || effect.mEffectId == ESM::MagicEffect::SummonFabricant || effect.mEffectId == ESM::MagicEffect::SummonWolf + || effect.mEffectId == ESM::MagicEffect::SummonBear || effect.mEffectId == ESM::MagicEffect::SummonBonewolf + || effect.mEffectId == ESM::MagicEffect::SummonCreature04 + || effect.mEffectId == ESM::MagicEffect::SummonCreature05) { if (!target.isInCell()) return ESM::ActiveEffect::Flag_Invalid; @@ -658,20 +658,16 @@ namespace MWMechanics world->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString()), target); } - else if (effect.mEffectId == ESM::MagicEffect::BoundDagger || - effect.mEffectId == ESM::MagicEffect::BoundLongsword || - effect.mEffectId == ESM::MagicEffect::BoundMace || - effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || - effect.mEffectId == ESM::MagicEffect::BoundSpear || - effect.mEffectId == ESM::MagicEffect::BoundLongbow || - effect.mEffectId == ESM::MagicEffect::BoundCuirass || - effect.mEffectId == ESM::MagicEffect::BoundHelm || - effect.mEffectId == ESM::MagicEffect::BoundBoots || - effect.mEffectId == ESM::MagicEffect::BoundShield) + else if (effect.mEffectId == ESM::MagicEffect::BoundDagger + || effect.mEffectId == ESM::MagicEffect::BoundLongsword || effect.mEffectId == ESM::MagicEffect::BoundMace + || effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || effect.mEffectId == ESM::MagicEffect::BoundSpear + || effect.mEffectId == ESM::MagicEffect::BoundLongbow || effect.mEffectId == ESM::MagicEffect::BoundCuirass + || effect.mEffectId == ESM::MagicEffect::BoundHelm || effect.mEffectId == ESM::MagicEffect::BoundBoots + || effect.mEffectId == ESM::MagicEffect::BoundShield) { if (!target.getClass().hasInventoryStore(target)) return ESM::ActiveEffect::Flag_Invalid; - const std::string& item = getBoundItemsMap().at(effect.mEffectId); + std::string_view item = getBoundItemsMap().at(effect.mEffectId); const MWWorld::Store& gmst = world->getStore().get(); const ESM::RefId itemId = ESM::RefId::stringRefId(gmst.find(item)->mValue.getString()); if (!addBoundItem(itemId, target)) @@ -684,19 +680,19 @@ namespace MWMechanics { if (!godmode) { - int index = 0; + auto targetStat = Stats::Health; if (effect.mEffectId == ESM::MagicEffect::DamageMagicka) - index = 1; + targetStat = Stats::Magicka; else if (effect.mEffectId == ESM::MagicEffect::DamageFatigue) - index = 2; + targetStat = Stats::Fatigue; // Damage "Dynamic" abilities reduce the base value if (spellParams.hasFlag(ESM::ActiveSpells::Flag_AffectsBaseValues)) - modDynamicStat(target, index, -effect.mMagnitude); + modDynamicStat(target, targetStat, -effect.mMagnitude); else { - adjustDynamicStat( - target, index, -effect.mMagnitude, index == 2 && Settings::game().mUncappedDamageFatigue); - if (index == 0) + adjustDynamicStat(target, targetStat, -effect.mMagnitude, + targetStat == Stats::Fatigue && Settings::game().mUncappedDamageFatigue); + if (targetStat == Stats::Health) receivedMagicDamage = affectedHealth = true; } } @@ -741,8 +737,8 @@ namespace MWMechanics else if (effect.mEffectId == ESM::MagicEffect::SunDamage) { //// isInCell shouldn't be needed, but updateActor called during game start - if (!godmode && target.isInCell() && target.getCell()->isExterior() - && !(target.getCell()->isQuasiExterior())) + if (!godmode && target.isInCell() + && (target.getCell()->isExterior() || target.getCell()->isQuasiExterior())) { const float sunRisen = world->getSunPercentage(); static float fMagicSunBlockedMult @@ -957,9 +953,9 @@ namespace MWMechanics return true; } } - else if (effect.mEffectId == ESM::MagicEffect::DivineIntervention || - effect.mEffectId == ESM::MagicEffect::Recall || - effect.mEffectId == ESM::MagicEffect::AlmsiviIntervention) + else if (effect.mEffectId == ESM::MagicEffect::DivineIntervention + || effect.mEffectId == ESM::MagicEffect::Recall + || effect.mEffectId == ESM::MagicEffect::AlmsiviIntervention) { return effect.mFlags & ESM::ActiveEffect::Flag_Applied; } @@ -1150,7 +1146,7 @@ namespace MWMechanics effect.mTimeLeft = 0; auto anim = world->getAnimation(target); if (anim) - anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); + anim->removeEffect(effect.mEffectId.getRefIdString()); // Note that we can't return REMOVED here because the effect still needs to be detectable } effect.mFlags |= applied; @@ -1189,16 +1185,15 @@ namespace MWMechanics stat.setModifier(static_cast(stat.getModifier() - effect.mMagnitude)); creatureStats.setAiSetting(AiSetting::Flee, stat); } - else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature || - effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::FrenzyCreature + || effect.mEffectId == ESM::MagicEffect::FrenzyHumanoid) modifyAiSetting(target, effect, ESM::MagicEffect::FrenzyCreature, AiSetting::Fight, -effect.mMagnitude); - else if (effect.mEffectId == ESM::MagicEffect::CalmCreature || - effect.mEffectId == ESM::MagicEffect::CalmHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::CalmCreature + || effect.mEffectId == ESM::MagicEffect::CalmHumanoid) modifyAiSetting(target, effect, ESM::MagicEffect::CalmCreature, AiSetting::Fight, effect.mMagnitude); - else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature || - effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) - modifyAiSetting( - target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude); + else if (effect.mEffectId == ESM::MagicEffect::DemoralizeCreature + || effect.mEffectId == ESM::MagicEffect::DemoralizeHumanoid) + modifyAiSetting(target, effect, ESM::MagicEffect::DemoralizeCreature, AiSetting::Flee, -effect.mMagnitude); else if (effect.mEffectId == ESM::MagicEffect::NightEye) { const MWMechanics::EffectParam nightEye = magnitudes.getOrDefault(effect.mEffectId); @@ -1213,8 +1208,8 @@ namespace MWMechanics magnitudes.modifyBase(effect.mEffectId, static_cast(delta)); } } - else if (effect.mEffectId == ESM::MagicEffect::RallyCreature || - effect.mEffectId == ESM::MagicEffect::RallyHumanoid) + else if (effect.mEffectId == ESM::MagicEffect::RallyCreature + || effect.mEffectId == ESM::MagicEffect::RallyHumanoid) modifyAiSetting(target, effect, ESM::MagicEffect::RallyCreature, AiSetting::Flee, effect.mMagnitude); else if (effect.mEffectId == ESM::MagicEffect::Sound) { @@ -1222,28 +1217,26 @@ namespace MWMechanics MWBase::Environment::get().getSoundManager()->stopSound3D( target, ESM::RefId::stringRefId("magic sound")); } - else if (effect.mEffectId == ESM::MagicEffect::SummonScamp || - effect.mEffectId == ESM::MagicEffect::SummonClannfear || - effect.mEffectId == ESM::MagicEffect::SummonDaedroth || - effect.mEffectId == ESM::MagicEffect::SummonDremora || - effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost || - effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion || - effect.mEffectId == ESM::MagicEffect::SummonBonewalker || - effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker || - effect.mEffectId == ESM::MagicEffect::SummonBonelord || - effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight || - effect.mEffectId == ESM::MagicEffect::SummonHunger || - effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint || - effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach || - effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach || - effect.mEffectId == ESM::MagicEffect::SummonStormAtronach || - effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere || - effect.mEffectId == ESM::MagicEffect::SummonFabricant || - effect.mEffectId == ESM::MagicEffect::SummonWolf || - effect.mEffectId == ESM::MagicEffect::SummonBear || - effect.mEffectId == ESM::MagicEffect::SummonBonewolf || - effect.mEffectId == ESM::MagicEffect::SummonCreature04 || - effect.mEffectId == ESM::MagicEffect::SummonCreature05) + else if (effect.mEffectId == ESM::MagicEffect::SummonScamp + || effect.mEffectId == ESM::MagicEffect::SummonClannfear + || effect.mEffectId == ESM::MagicEffect::SummonDaedroth + || effect.mEffectId == ESM::MagicEffect::SummonDremora + || effect.mEffectId == ESM::MagicEffect::SummonAncestralGhost + || effect.mEffectId == ESM::MagicEffect::SummonSkeletalMinion + || effect.mEffectId == ESM::MagicEffect::SummonBonewalker + || effect.mEffectId == ESM::MagicEffect::SummonGreaterBonewalker + || effect.mEffectId == ESM::MagicEffect::SummonBonelord + || effect.mEffectId == ESM::MagicEffect::SummonWingedTwilight + || effect.mEffectId == ESM::MagicEffect::SummonHunger + || effect.mEffectId == ESM::MagicEffect::SummonGoldenSaint + || effect.mEffectId == ESM::MagicEffect::SummonFlameAtronach + || effect.mEffectId == ESM::MagicEffect::SummonFrostAtronach + || effect.mEffectId == ESM::MagicEffect::SummonStormAtronach + || effect.mEffectId == ESM::MagicEffect::SummonCenturionSphere + || effect.mEffectId == ESM::MagicEffect::SummonFabricant || effect.mEffectId == ESM::MagicEffect::SummonWolf + || effect.mEffectId == ESM::MagicEffect::SummonBear || effect.mEffectId == ESM::MagicEffect::SummonBonewolf + || effect.mEffectId == ESM::MagicEffect::SummonCreature04 + || effect.mEffectId == ESM::MagicEffect::SummonCreature05) { ESM::RefNum actor = effect.getActor(); if (actor.isSet()) @@ -1270,18 +1263,14 @@ namespace MWMechanics world->getStore().get().find("sMagicBoundLeftGauntletID")->mValue.getString()), target); } - else if (effect.mEffectId == ESM::MagicEffect::BoundDagger || - effect.mEffectId == ESM::MagicEffect::BoundLongsword || - effect.mEffectId == ESM::MagicEffect::BoundMace || - effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || - effect.mEffectId == ESM::MagicEffect::BoundSpear || - effect.mEffectId == ESM::MagicEffect::BoundLongbow || - effect.mEffectId == ESM::MagicEffect::BoundCuirass || - effect.mEffectId == ESM::MagicEffect::BoundHelm || - effect.mEffectId == ESM::MagicEffect::BoundBoots || - effect.mEffectId == ESM::MagicEffect::BoundShield) + else if (effect.mEffectId == ESM::MagicEffect::BoundDagger + || effect.mEffectId == ESM::MagicEffect::BoundLongsword || effect.mEffectId == ESM::MagicEffect::BoundMace + || effect.mEffectId == ESM::MagicEffect::BoundBattleAxe || effect.mEffectId == ESM::MagicEffect::BoundSpear + || effect.mEffectId == ESM::MagicEffect::BoundLongbow || effect.mEffectId == ESM::MagicEffect::BoundCuirass + || effect.mEffectId == ESM::MagicEffect::BoundHelm || effect.mEffectId == ESM::MagicEffect::BoundBoots + || effect.mEffectId == ESM::MagicEffect::BoundShield) { - const std::string& item = getBoundItemsMap().at(effect.mEffectId); + std::string_view item = getBoundItemsMap().at(effect.mEffectId); removeBoundItem( ESM::RefId::stringRefId(world->getStore().get().find(item)->mValue.getString()), target); @@ -1401,7 +1390,7 @@ namespace MWMechanics { auto anim = MWBase::Environment::get().getWorld()->getAnimation(target); if (anim) - anim->removeEffect(ESM::MagicEffect::refIdToName(effect.mEffectId)); + anim->removeEffect(effect.mEffectId.getRefIdString()); } } diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 9c736c0dec..e93fb6f217 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -30,7 +30,7 @@ namespace Fatigue = 2 }; - int numEffectsToDispel(const MWWorld::Ptr& actor, const ESM::RefId& effectFilter = ESM::RefId(), bool negative = true) + int numEffectsToDispel(const MWWorld::Ptr& actor, ESM::RefId effectFilter = ESM::RefId(), bool negative = true) { int toCure = 0; const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells(); @@ -214,52 +214,40 @@ namespace MWMechanics // NOTE: enemy may be empty float rating = 1; - if (effect.mEffectID == ESM::MagicEffect::Soultrap || - effect.mEffectID == ESM::MagicEffect::AlmsiviIntervention || - effect.mEffectID == ESM::MagicEffect::DivineIntervention || - effect.mEffectID == ESM::MagicEffect::CalmHumanoid || - effect.mEffectID == ESM::MagicEffect::CalmCreature || - effect.mEffectID == ESM::MagicEffect::FrenzyHumanoid || - effect.mEffectID == ESM::MagicEffect::FrenzyCreature || - effect.mEffectID == ESM::MagicEffect::DemoralizeHumanoid || - effect.mEffectID == ESM::MagicEffect::DemoralizeCreature || - effect.mEffectID == ESM::MagicEffect::RallyHumanoid || - effect.mEffectID == ESM::MagicEffect::RallyCreature || - effect.mEffectID == ESM::MagicEffect::Charm || - effect.mEffectID == ESM::MagicEffect::DetectAnimal || - effect.mEffectID == ESM::MagicEffect::DetectEnchantment || - effect.mEffectID == ESM::MagicEffect::DetectKey || - effect.mEffectID == ESM::MagicEffect::Telekinesis || - effect.mEffectID == ESM::MagicEffect::Mark || - effect.mEffectID == ESM::MagicEffect::Recall || - effect.mEffectID == ESM::MagicEffect::Jump || - effect.mEffectID == ESM::MagicEffect::WaterBreathing || - effect.mEffectID == ESM::MagicEffect::SwiftSwim || - effect.mEffectID == ESM::MagicEffect::WaterWalking || - effect.mEffectID == ESM::MagicEffect::SlowFall || - effect.mEffectID == ESM::MagicEffect::Light || - effect.mEffectID == ESM::MagicEffect::Lock || - effect.mEffectID == ESM::MagicEffect::Open || - effect.mEffectID == ESM::MagicEffect::TurnUndead || - effect.mEffectID == ESM::MagicEffect::WeaknessToCommonDisease || - effect.mEffectID == ESM::MagicEffect::WeaknessToBlightDisease || - effect.mEffectID == ESM::MagicEffect::WeaknessToCorprusDisease || - effect.mEffectID == ESM::MagicEffect::CureCommonDisease || - effect.mEffectID == ESM::MagicEffect::CureBlightDisease || - effect.mEffectID == ESM::MagicEffect::CureCorprusDisease || - effect.mEffectID == ESM::MagicEffect::ResistBlightDisease || - effect.mEffectID == ESM::MagicEffect::ResistCommonDisease || - effect.mEffectID == ESM::MagicEffect::ResistCorprusDisease || - effect.mEffectID == ESM::MagicEffect::Invisibility || - effect.mEffectID == ESM::MagicEffect::Chameleon || - effect.mEffectID == ESM::MagicEffect::NightEye || - effect.mEffectID == ESM::MagicEffect::Vampirism || - effect.mEffectID == ESM::MagicEffect::StuntedMagicka || - effect.mEffectID == ESM::MagicEffect::ExtraSpell || - effect.mEffectID == ESM::MagicEffect::RemoveCurse || - effect.mEffectID == ESM::MagicEffect::CommandCreature || - effect.mEffectID == ESM::MagicEffect::CommandHumanoid) - return 0.f; + if (effect.mEffectID == ESM::MagicEffect::Soultrap || effect.mEffectID == ESM::MagicEffect::AlmsiviIntervention + || effect.mEffectID == ESM::MagicEffect::DivineIntervention + || effect.mEffectID == ESM::MagicEffect::CalmHumanoid || effect.mEffectID == ESM::MagicEffect::CalmCreature + || effect.mEffectID == ESM::MagicEffect::FrenzyHumanoid + || effect.mEffectID == ESM::MagicEffect::FrenzyCreature + || effect.mEffectID == ESM::MagicEffect::DemoralizeHumanoid + || effect.mEffectID == ESM::MagicEffect::DemoralizeCreature + || effect.mEffectID == ESM::MagicEffect::RallyHumanoid + || effect.mEffectID == ESM::MagicEffect::RallyCreature || effect.mEffectID == ESM::MagicEffect::Charm + || effect.mEffectID == ESM::MagicEffect::DetectAnimal + || effect.mEffectID == ESM::MagicEffect::DetectEnchantment + || effect.mEffectID == ESM::MagicEffect::DetectKey || effect.mEffectID == ESM::MagicEffect::Telekinesis + || effect.mEffectID == ESM::MagicEffect::Mark || effect.mEffectID == ESM::MagicEffect::Recall + || effect.mEffectID == ESM::MagicEffect::Jump || effect.mEffectID == ESM::MagicEffect::WaterBreathing + || effect.mEffectID == ESM::MagicEffect::SwiftSwim || effect.mEffectID == ESM::MagicEffect::WaterWalking + || effect.mEffectID == ESM::MagicEffect::SlowFall || effect.mEffectID == ESM::MagicEffect::Light + || effect.mEffectID == ESM::MagicEffect::Lock || effect.mEffectID == ESM::MagicEffect::Open + || effect.mEffectID == ESM::MagicEffect::TurnUndead + || effect.mEffectID == ESM::MagicEffect::WeaknessToCommonDisease + || effect.mEffectID == ESM::MagicEffect::WeaknessToBlightDisease + || effect.mEffectID == ESM::MagicEffect::WeaknessToCorprusDisease + || effect.mEffectID == ESM::MagicEffect::CureCommonDisease + || effect.mEffectID == ESM::MagicEffect::CureBlightDisease + || effect.mEffectID == ESM::MagicEffect::CureCorprusDisease + || effect.mEffectID == ESM::MagicEffect::ResistBlightDisease + || effect.mEffectID == ESM::MagicEffect::ResistCommonDisease + || effect.mEffectID == ESM::MagicEffect::ResistCorprusDisease + || effect.mEffectID == ESM::MagicEffect::Invisibility || effect.mEffectID == ESM::MagicEffect::Chameleon + || effect.mEffectID == ESM::MagicEffect::NightEye || effect.mEffectID == ESM::MagicEffect::Vampirism + || effect.mEffectID == ESM::MagicEffect::StuntedMagicka || effect.mEffectID == ESM::MagicEffect::ExtraSpell + || effect.mEffectID == ESM::MagicEffect::RemoveCurse + || effect.mEffectID == ESM::MagicEffect::CommandCreature + || effect.mEffectID == ESM::MagicEffect::CommandHumanoid) + return 0.f; else if (effect.mEffectID == ESM::MagicEffect::Blind) { if (enemy.isEmpty()) @@ -312,27 +300,24 @@ namespace MWMechanics return 0.f; // TODO: implement based on attribute damage else if (effect.mEffectID == ESM::MagicEffect::RestoreSkill) return 0.f; // TODO: implement based on skill damage - else if (effect.mEffectID == ESM::MagicEffect::ResistFire || - effect.mEffectID == ESM::MagicEffect::ResistFrost || - effect.mEffectID == ESM::MagicEffect::ResistMagicka || - effect.mEffectID == ESM::MagicEffect::ResistNormalWeapons || - effect.mEffectID == ESM::MagicEffect::ResistParalysis || - effect.mEffectID == ESM::MagicEffect::ResistPoison || - effect.mEffectID == ESM::MagicEffect::ResistShock || - effect.mEffectID == ESM::MagicEffect::SpellAbsorption || - effect.mEffectID == ESM::MagicEffect::Reflect) - return 0.f; // probably useless since we don't know in advance what the enemy will cast + else if (effect.mEffectID == ESM::MagicEffect::ResistFire || effect.mEffectID == ESM::MagicEffect::ResistFrost + || effect.mEffectID == ESM::MagicEffect::ResistMagicka + || effect.mEffectID == ESM::MagicEffect::ResistNormalWeapons + || effect.mEffectID == ESM::MagicEffect::ResistParalysis + || effect.mEffectID == ESM::MagicEffect::ResistPoison || effect.mEffectID == ESM::MagicEffect::ResistShock + || effect.mEffectID == ESM::MagicEffect::SpellAbsorption || effect.mEffectID == ESM::MagicEffect::Reflect) + return 0.f; // probably useless since we don't know in advance what the enemy will cast - // don't cast these for now as they would make the NPC cast the same effect over and over again, especially - // when they have potions - else if (effect.mEffectID == ESM::MagicEffect::FortifyAttribute || - effect.mEffectID == ESM::MagicEffect::FortifyHealth || - effect.mEffectID == ESM::MagicEffect::FortifyMagicka || - effect.mEffectID == ESM::MagicEffect::FortifyFatigue || - effect.mEffectID == ESM::MagicEffect::FortifySkill || - effect.mEffectID == ESM::MagicEffect::FortifyMaximumMagicka || - effect.mEffectID == ESM::MagicEffect::FortifyAttack) - return 0.f; + // don't cast these for now as they would make the NPC cast the same effect over and over again, especially + // when they have potions + else if (effect.mEffectID == ESM::MagicEffect::FortifyAttribute + || effect.mEffectID == ESM::MagicEffect::FortifyHealth + || effect.mEffectID == ESM::MagicEffect::FortifyMagicka + || effect.mEffectID == ESM::MagicEffect::FortifyFatigue + || effect.mEffectID == ESM::MagicEffect::FortifySkill + || effect.mEffectID == ESM::MagicEffect::FortifyMaximumMagicka + || effect.mEffectID == ESM::MagicEffect::FortifyAttack) + return 0.f; else if (effect.mEffectID == ESM::MagicEffect::Burden) { if (enemy.isEmpty()) @@ -424,17 +409,14 @@ namespace MWMechanics { if (effect.mRange == ESM::RT_Self) { - int index = -1; - if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) - index = Stats::Health; - else if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka) - index = Stats::Magicka; + auto targetStat = Stats::Health; + if (effect.mEffectID == ESM::MagicEffect::RestoreMagicka) + targetStat = Stats::Magicka; else if (effect.mEffectID == ESM::MagicEffect::RestoreFatigue) - index = Stats::Fatigue; + targetStat = Stats::Fatigue; const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - const DynamicStat& current - = stats.getDynamic(index); + const DynamicStat& current = stats.getDynamic(targetStat); // NB: this currently assumes the hardcoded magic effect flags are used const float magnitude = (effect.mMagnMin + effect.mMagnMax) / 2.f; const float toHeal = magnitude * std::max(1, effect.mDuration); @@ -602,7 +584,7 @@ namespace MWMechanics rating = 3.f; } - static const std::array boundWeapons{ + static const std::array boundWeapons{ ESM::MagicEffect::BoundDagger, ESM::MagicEffect::BoundLongsword, ESM::MagicEffect::BoundMace, @@ -611,7 +593,7 @@ namespace MWMechanics ESM::MagicEffect::BoundLongbow, }; - static const std::array boundArmor{ + static const std::array boundArmor{ ESM::MagicEffect::ExtraSpell, ESM::MagicEffect::BoundCuirass, ESM::MagicEffect::BoundHelm, @@ -620,8 +602,8 @@ namespace MWMechanics ESM::MagicEffect::BoundGloves, }; - if (std::ranges::find(boundWeapons, effect.mEffectID) != boundWeapons.end() || - std::ranges::find(boundArmor, effect.mEffectID) != boundArmor.end()) + if (std::ranges::find(boundWeapons, effect.mEffectID) != boundWeapons.end() + || std::ranges::find(boundArmor, effect.mEffectID) != boundArmor.end()) { // Prefer casting bound items over other spells rating = 2.f; @@ -633,7 +615,7 @@ namespace MWMechanics // summon an Axe instead) if (std::ranges::find(boundWeapons, effect.mEffectID) != boundWeapons.end()) { - for (const auto& e : boundWeapons) + for (const auto e : boundWeapons) if (actor.getClass().getCreatureStats(actor).getMagicEffects().getOrDefault(e).getMagnitude() > 0.f && (e != ESM::MagicEffect::BoundLongbow || effect.mEffectID == e || rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f)) diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index 03354139b3..abb3977340 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -16,7 +16,7 @@ namespace MWMechanics { - float getEffectMultiplier(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectMultiplier(ESM::RefId effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { if (!actor.getClass().isActor()) @@ -26,14 +26,14 @@ namespace MWMechanics return 1 - resistance / 100.f; } - float getEffectResistance(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectResistance(ESM::RefId effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { // Effects with no resistance attribute belonging to them can not be resisted - if (effectId.empty() || ESM::MagicEffect::getResistanceEffect(*effectId.getIf()).empty()) + if (ESM::MagicEffect::getResistanceEffect(effectId).empty()) return 0.f; - const auto magicEffect = MWBase::Environment::get().getESMStore()->get().search(effectId); + const auto magicEffect = MWBase::Environment::get().getESMStore()->get().find(effectId); const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); @@ -72,19 +72,17 @@ namespace MWMechanics return x; } - float getEffectResistanceAttribute(const ESM::RefId& effectId, const MagicEffects* actorEffects) + float getEffectResistanceAttribute(ESM::RefId effectId, const MagicEffects* actorEffects) { float resistance = 0; - if (effectId.empty()) - return resistance; - ESM::RefId resistanceEffect = ESM::MagicEffect::getResistanceEffect(*effectId.getIf()); - ESM::RefId weaknessEffect = ESM::MagicEffect::getWeaknessEffect(*effectId.getIf()); + ESM::RefId resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + ESM::RefId weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); if (!resistanceEffect.empty()) - resistance += actorEffects->getOrDefault(*resistanceEffect.getIf()).getMagnitude(); + resistance += actorEffects->getOrDefault(resistanceEffect).getMagnitude(); if (!weaknessEffect.empty()) - resistance -= actorEffects->getOrDefault(*weaknessEffect.getIf()).getMagnitude(); + resistance -= actorEffects->getOrDefault(weaknessEffect).getMagnitude(); if (effectId == ESM::MagicEffect::FireDamage) resistance += actorEffects->getOrDefault(ESM::MagicEffect::FireShield).getMagnitude(); @@ -95,5 +93,4 @@ namespace MWMechanics return resistance; } - } diff --git a/apps/openmw/mwmechanics/spellresistance.hpp b/apps/openmw/mwmechanics/spellresistance.hpp index 442ce421d6..0d2ad45523 100644 --- a/apps/openmw/mwmechanics/spellresistance.hpp +++ b/apps/openmw/mwmechanics/spellresistance.hpp @@ -20,19 +20,19 @@ namespace MWMechanics /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. - float getEffectMultiplier(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectMultiplier(ESM::RefId effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). /// @return >=100 for fully resisted. can also return negative value for damage amplification. /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. - float getEffectResistance(const ESM::RefId& effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + float getEffectResistance(ESM::RefId effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get the resistance attribute against an effect for a given actor. This will add together /// ResistX and Weakness to X effects relevant against the given effect. - float getEffectResistanceAttribute(const ESM::RefId& effectId, const MagicEffects* actorEffects); + float getEffectResistanceAttribute(ESM::RefId effectId, const MagicEffects* actorEffects); } #endif diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 81985a80b0..67e9084b29 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -176,9 +176,7 @@ namespace MWMechanics return std::nullopt; const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - const auto magicEffect = store.get().search(effect.mEffectID); - if (!magicEffect) - return std::nullopt; + const auto magicEffect = store.get().find(effect.mEffectID); const MWMechanics::CreatureStats& creatureStats = caster.getClass().getCreatureStats(caster); float x = (caster.getClass().getSkill(caster, ESM::Skill::Alchemy) diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index e6c9a85ff2..047e1ce810 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -23,11 +23,11 @@ namespace MWMechanics { - bool isSummoningEffect(const ESM::RefId& effectId) + bool isSummoningEffect(ESM::RefId effectId) { if (effectId.empty()) return false; - static const std::array summonEffects{ + static const std::array summonEffects{ ESM::MagicEffect::SummonAncestralGhost, ESM::MagicEffect::SummonBonelord, ESM::MagicEffect::SummonBonewalker, @@ -51,8 +51,7 @@ namespace MWMechanics ESM::MagicEffect::SummonCreature04, ESM::MagicEffect::SummonCreature05, }; - return (std::find(summonEffects.begin(), summonEffects.end(), *effectId.getIf()) - != summonEffects.end()); + return (std::find(summonEffects.begin(), summonEffects.end(), effectId) != summonEffects.end()); } static const std::map& getSummonMap() @@ -62,7 +61,7 @@ namespace MWMechanics if (summonMap.size() > 0) return summonMap; - const std::map summonMapToGameSetting{ + const std::map summonMapToGameSetting{ { ESM::MagicEffect::SummonAncestralGhost, "sMagicAncestralGhostID" }, { ESM::MagicEffect::SummonBonelord, "sMagicBonelordID" }, { ESM::MagicEffect::SummonBonewalker, "sMagicLeastBonewalkerID" }, @@ -95,7 +94,7 @@ namespace MWMechanics return summonMap; } - ESM::RefId getSummonedCreature(const ESM::RefId& effectId) + ESM::RefId getSummonedCreature(ESM::RefId effectId) { const auto& summonMap = getSummonMap(); auto it = summonMap.find(effectId); @@ -106,7 +105,7 @@ namespace MWMechanics return ESM::RefId(); } - ESM::RefNum summonCreature(const ESM::RefId& effectId, const MWWorld::Ptr& summoner) + ESM::RefNum summonCreature(ESM::RefId effectId, const MWWorld::Ptr& summoner) { const ESM::RefId& creatureID = getSummonedCreature(effectId); ESM::RefNum creature; diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 09dac36d39..7818230fc4 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -18,13 +18,13 @@ namespace MWWorld namespace MWMechanics { - bool isSummoningEffect(const ESM::RefId& effectId); + bool isSummoningEffect(ESM::RefId effectId); - ESM::RefId getSummonedCreature(const ESM::RefId& effectId); + ESM::RefId getSummonedCreature(ESM::RefId effectId); void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); - ESM::RefNum summonCreature(const ESM::RefId& effectId, const MWWorld::Ptr& summoner); + ESM::RefNum summonCreature(ESM::RefId effectId, const MWWorld::Ptr& summoner); void updateSummons(const MWWorld::Ptr& summoner, bool cleanup); } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ed030a28a9..a810baa3e9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -613,20 +613,20 @@ namespace MWScript return; } - long key; + ESM::RefId key; if (const auto k = ::Misc::StringUtils::toNumeric(effectName); k.has_value() && *k >= 0 && *k <= 32767) - key = *k; + key = ESM::MagicEffect::indexToRefId(*k); else - key = ESM::MagicEffect::refIdToIndex(ESM::MagicEffect::effectGmstIdToRefId(effectName)); + key = ESM::MagicEffect::effectGmstIdToRefId(effectName); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); for (const auto& spell : stats.getActiveSpells()) { for (const auto& effect : spell.getEffects()) { - if (effect.mFlags & ESM::ActiveEffect::Flag_Remove && ESM::MagicEffect::refIdToIndex(effect.mEffectId) == key) + if (effect.mFlags & ESM::ActiveEffect::Flag_Remove && effect.mEffectId == key) { runtime.push(1); return; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c7daa9072c..edb43be22d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -580,7 +580,8 @@ namespace MWScript runtime.pop(); if (ptr.getClass().isActor()) - ptr.getClass().getCreatureStats(ptr).getActiveSpells().purgeEffect(ptr, ESM::MagicEffect::indexToRefId(static_cast(effectId))); + ptr.getClass().getCreatureStats(ptr).getActiveSpells().purgeEffect( + ptr, ESM::MagicEffect::indexToRefId(static_cast(effectId))); } }; @@ -1392,7 +1393,7 @@ namespace MWScript auto& effects = player.getClass().getCreatureStats(player).getMagicEffects(); float delta = std::clamp(arg * 100.f, 0.f, 100.f) - effects.getOrDefault(ESM::MagicEffect::NightEye).getMagnitude(); - effects.modifyBase(ESM::MagicEffect::NightEye, static_cast(delta)); + effects.modifyBase(MWMechanics::EffectKey(ESM::MagicEffect::NightEye), static_cast(delta)); } }; @@ -1409,7 +1410,7 @@ namespace MWScript float newBase = std::clamp(nightEye.getMagnitude() + arg * 100.f, 0.f, 100.f); newBase -= nightEye.getModifier(); float delta = std::clamp(newBase, 0.f, 100.f) - nightEye.getMagnitude(); - effects.modifyBase(ESM::MagicEffect::NightEye, static_cast(delta)); + effects.modifyBase(MWMechanics::EffectKey(ESM::MagicEffect::NightEye), static_cast(delta)); } }; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ff3bf9fd5b..275e36686d 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -185,8 +185,8 @@ namespace { iter->mData.mAttribute = -1; Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId - << ": dropping unexpected attribute argument of " - << ESM::MagicEffect::refIdToName(iter->mData.mEffectID) << " effect"; + << ": dropping unexpected attribute argument of " << iter->mData.mEffectID + << " effect"; changed = true; } @@ -194,8 +194,8 @@ namespace { iter->mData.mSkill = -1; Log(Debug::Verbose) << RecordType::getRecordType() << " " << spell.mId - << ": dropping unexpected skill argument of " - << ESM::MagicEffect::refIdToName(iter->mData.mEffectID) << " effect"; + << ": dropping unexpected skill argument of " << iter->mData.mEffectID + << " effect"; changed = true; } @@ -525,7 +525,7 @@ namespace MWWorld store->setUp(); getWritable().setUp(get()); - getWritable().setUp(get()); + getWritable().setUp(); getWritable().setUp(get()); getWritable().updateLandPositions(get()); getWritable().preprocessReferences(get()); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 6158918193..a55c0c8350 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -377,9 +377,6 @@ namespace MWWorld } } - // Need to instantiate these before they're used - template class TypedDynamicStore; - // LandTexture //========================================================================= Store::Store() = default; @@ -976,13 +973,6 @@ namespace MWWorld TypedDynamicStore::setUp(); } - // Magic effect - //========================================================================= - void Store::setUp(const MWWorld::Store& settings) - { - // MGEF record is complete. No further instantiation of fields is required. - } - // Attribute //========================================================================= @@ -1263,7 +1253,7 @@ template class MWWorld::TypedDynamicStore; // template class MWWorld::Store; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; -// template class MWWorld::Store; +template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; // template class MWWorld::Store; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 28e0dab777..dd23495bba 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -17,9 +17,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -452,17 +452,6 @@ namespace MWWorld void setUp(const MWWorld::Store& settings); }; - template <> - class Store : public TypedDynamicStore - { - using TypedDynamicStore::setUp; - - public: - Store() = default; - - void setUp(const MWWorld::Store& settings); - }; - template <> class Store : public TypedDynamicStore { diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index 826ac00d70..2ddbc4265c 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -13,7 +13,7 @@ namespace ESM { namespace { - bool isSummon(const ESM::RefId& effectId) + bool isSummon(ESM::RefId effectId) { static const std::array summonEffects{ MagicEffect::SummonScamp, @@ -41,7 +41,7 @@ namespace ESM }; return std::find(summonEffects.begin(), summonEffects.end(), effectId) != summonEffects.end(); } - bool affectsAttribute(const ESM::RefId& effectId) + bool affectsAttribute(ESM::RefId effectId) { static const std::array affectsAttributeEffects{ MagicEffect::DrainAttribute, @@ -53,7 +53,7 @@ namespace ESM return std::find(affectsAttributeEffects.begin(), affectsAttributeEffects.end(), effectId) != affectsAttributeEffects.end(); } - bool affectsSkill(const ESM::RefId& effectId) + bool affectsSkill(ESM::RefId effectId) { static const std::array affectsSkillEffects{ MagicEffect::DrainSkill, diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index 7901efaf75..5d30fbd07a 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -119,7 +119,8 @@ namespace ESM esm.getHNOT(effectIndex, "EIND"); int32_t actorId; esm.getHNT(actorId, "ACID"); - mSummonedCreatureMap[SummonKey(ESM::MagicEffect::indexToRefId(magicEffect), source, effectIndex)] = actorId; + mSummonedCreatureMap[SummonKey(ESM::MagicEffect::indexToRefId(magicEffect), source, effectIndex)] + = actorId; mSummonedCreatures.emplace(ESM::MagicEffect::indexToRefId( magicEffect), RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }); } diff --git a/components/esm3/effectlist.cpp b/components/esm3/effectlist.cpp index 221abb67f9..f82f956c90 100644 --- a/components/esm3/effectlist.cpp +++ b/components/esm3/effectlist.cpp @@ -3,23 +3,58 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include + #include #include namespace ESM { - template - constexpr bool loading = !std::is_const_v>; + namespace + { + // ENAM format defined by Morrowind.esm + struct EsmENAMstruct + { + int16_t mEffectID; + signed char mSkill, mAttribute; + int32_t mRange, mArea, mDuration, mMagnMin, mMagnMax; + }; - template T> + void toBinary(const ENAMstruct& src, EsmENAMstruct& dst) + { + int16_t index = static_cast(ESM::MagicEffect::refIdToIndex(src.mEffectID)); + if (index < 0 || index >= ESM::MagicEffect::Length) + throw std::runtime_error(std::format("Cannot serialize effect {}", src.mEffectID.toDebugString())); + dst.mEffectID = index; + dst.mSkill = src.mSkill; + dst.mAttribute = src.mAttribute; + dst.mRange = src.mRange; + dst.mArea = src.mArea; + dst.mDuration = src.mDuration; + dst.mMagnMin = src.mMagnMin; + dst.mMagnMax = src.mMagnMax; + } + + void fromBinary(const EsmENAMstruct& src, ENAMstruct& dst) + { + int16_t index = src.mEffectID; + if (index < 0 || index >= ESM::MagicEffect::Length) + throw std::runtime_error(std::format("Cannot deserialize effect with index {}", index)); + dst.mEffectID = ESM::MagicEffect::indexToRefId(index); + dst.mSkill = src.mSkill; + dst.mAttribute = src.mAttribute; + dst.mRange = src.mRange; + dst.mArea = src.mArea; + dst.mDuration = src.mDuration; + dst.mMagnMin = src.mMagnMin; + dst.mMagnMax = src.mMagnMax; + } + } + + template T> void decompose(T&& v, const auto& f) { - int16_t ioEffectID = static_cast(ESM::MagicEffect::refIdToIndex(v.mEffectID)); - f(ioEffectID, v.mSkill, v.mAttribute, v.mRange, v.mArea, v.mDuration, v.mMagnMin, v.mMagnMax); - if constexpr (loading) - { - v.mEffectID = ESM::MagicEffect::indexToRefId(ioEffectID); - } + f(v.mEffectID, v.mSkill, v.mAttribute, v.mRange, v.mArea, v.mDuration, v.mMagnMin, v.mMagnMax); } void EffectList::load(ESMReader& esm) @@ -46,8 +81,11 @@ namespace ESM void EffectList::add(ESMReader& esm) { + EsmENAMstruct bin; + esm.getSubComposite(bin); + ENAMstruct s; - esm.getSubComposite(s); + fromBinary(bin, s); mList.push_back({ s, static_cast(mList.size()) }); } @@ -55,7 +93,9 @@ namespace ESM { for (const IndexedENAMstruct& enam : mList) { - esm.writeNamedComposite("ENAM", enam.mData); + EsmENAMstruct bin; + toBinary(enam.mData, bin); + esm.writeNamedComposite("ENAM", bin); } } diff --git a/components/esm3/loadingr.cpp b/components/esm3/loadingr.cpp index b0b5065434..76a47f5178 100644 --- a/components/esm3/loadingr.cpp +++ b/components/esm3/loadingr.cpp @@ -8,21 +8,44 @@ namespace ESM { - template - constexpr bool loading = !std::is_const_v>; + namespace + { + // IRDT format defined by Morrowind.esm + struct EsmIRDTstruct + { + float mWeight; + int32_t mValue, mEffectID[4], mSkills[4], mAttributes[4]; + }; - template T> + void toBinary(const Ingredient::IRDTstruct& src, EsmIRDTstruct& dst) + { + dst.mWeight = src.mWeight; + dst.mValue = src.mValue; + for (int i = 0; i < 4; ++i) + { + dst.mEffectID[i] = ESM::MagicEffect::refIdToIndex(src.mEffectID[i]); + dst.mSkills[i] = src.mSkills[i]; + dst.mAttributes[i] = src.mAttributes[i]; + } + } + + void fromBinary(const EsmIRDTstruct& src, Ingredient::IRDTstruct& dst) + { + dst.mWeight = src.mWeight; + dst.mValue = src.mValue; + for (int i = 0; i < 4; ++i) + { + dst.mEffectID[i] = ESM::MagicEffect::indexToRefId(src.mEffectID[i]); + dst.mSkills[i] = src.mSkills[i]; + dst.mAttributes[i] = src.mAttributes[i]; + } + } + } + + template T> void decompose(T&& v, const auto& f) { - 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); - } + f(v.mWeight, v.mValue, v.mEffectID, v.mSkills, v.mAttributes); } void Ingredient::load(ESMReader& esm, bool& isDeleted) @@ -48,7 +71,9 @@ namespace ESM mName = esm.getHString(); break; case fourCC("IRDT"): - esm.getSubComposite(mData); + EsmIRDTstruct bin; + esm.getSubComposite(bin); + fromBinary(bin, mData); hasData = true; break; case fourCC("SCRI"): @@ -75,21 +100,21 @@ namespace ESM // horrible hack to fix broken data in records for (int i = 0; i < 4; ++i) { - 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) + 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] != 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) + 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; } @@ -108,7 +133,9 @@ namespace ESM esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); - esm.writeNamedComposite("IRDT", mData); + EsmIRDTstruct bin; + toBinary(mData, bin); + esm.writeNamedComposite("IRDT", bin); esm.writeHNOCRefId("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); } diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index f8fa5e3e93..0119ad821a 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -25,153 +25,153 @@ namespace ESM 0x1048, 0x1048, 0x1048, 0x1048 }; } - const MagicEffectId MagicEffect::WaterBreathing("WaterBreathing"); - const MagicEffectId MagicEffect::SwiftSwim("SwiftSwim"); - const MagicEffectId MagicEffect::WaterWalking("WaterWalking"); - const MagicEffectId MagicEffect::Shield("Shield"); - const MagicEffectId MagicEffect::FireShield("FireShield"); - const MagicEffectId MagicEffect::LightningShield("LightningShield"); - const MagicEffectId MagicEffect::FrostShield("FrostShield"); - const MagicEffectId MagicEffect::Burden("Burden"); - const MagicEffectId MagicEffect::Feather("Feather"); - const MagicEffectId MagicEffect::Jump("Jump"); - const MagicEffectId MagicEffect::Levitate("Levitate"); - const MagicEffectId MagicEffect::SlowFall("SlowFall"); - const MagicEffectId MagicEffect::Lock("Lock"); - const MagicEffectId MagicEffect::Open("Open"); - const MagicEffectId MagicEffect::FireDamage("FireDamage"); - const MagicEffectId MagicEffect::ShockDamage("ShockDamage"); - const MagicEffectId MagicEffect::FrostDamage("FrostDamage"); - const MagicEffectId MagicEffect::DrainAttribute("DrainAttribute"); - const MagicEffectId MagicEffect::DrainHealth("DrainHealth"); - const MagicEffectId MagicEffect::DrainMagicka("DrainMagicka"); - const MagicEffectId MagicEffect::DrainFatigue("DrainFatigue"); - const MagicEffectId MagicEffect::DrainSkill("DrainSkill"); - const MagicEffectId MagicEffect::DamageAttribute("DamageAttribute"); - const MagicEffectId MagicEffect::DamageHealth("DamageHealth"); - const MagicEffectId MagicEffect::DamageMagicka("DamageMagicka"); - const MagicEffectId MagicEffect::DamageFatigue("DamageFatigue"); - const MagicEffectId MagicEffect::DamageSkill("DamageSkill"); - const MagicEffectId MagicEffect::Poison("Poison"); - const MagicEffectId MagicEffect::WeaknessToFire("WeaknessToFire"); - const MagicEffectId MagicEffect::WeaknessToFrost("WeaknessToFrost"); - const MagicEffectId MagicEffect::WeaknessToShock("WeaknessToShock"); - const MagicEffectId MagicEffect::WeaknessToMagicka("WeaknessToMagicka"); - const MagicEffectId MagicEffect::WeaknessToCommonDisease("WeaknessToCommonDisease"); - const MagicEffectId MagicEffect::WeaknessToBlightDisease("WeaknessToBlightDisease"); - const MagicEffectId MagicEffect::WeaknessToCorprusDisease("WeaknessToCorprusDisease"); - const MagicEffectId MagicEffect::WeaknessToPoison("WeaknessToPoison"); - const MagicEffectId MagicEffect::WeaknessToNormalWeapons("WeaknessToNormalWeapons"); - const MagicEffectId MagicEffect::DisintegrateWeapon("DisintegrateWeapon"); - const MagicEffectId MagicEffect::DisintegrateArmor("DisintegrateArmor"); - const MagicEffectId MagicEffect::Invisibility("Invisibility"); - const MagicEffectId MagicEffect::Chameleon("Chameleon"); - const MagicEffectId MagicEffect::Light("Light"); - const MagicEffectId MagicEffect::Sanctuary("Sanctuary"); - const MagicEffectId MagicEffect::NightEye("NightEye"); - const MagicEffectId MagicEffect::Charm("Charm"); - const MagicEffectId MagicEffect::Paralyze("Paralyze"); - const MagicEffectId MagicEffect::Silence("Silence"); - const MagicEffectId MagicEffect::Blind("Blind"); - const MagicEffectId MagicEffect::Sound("Sound"); - const MagicEffectId MagicEffect::CalmHumanoid("CalmHumanoid"); - const MagicEffectId MagicEffect::CalmCreature("CalmCreature"); - const MagicEffectId MagicEffect::FrenzyHumanoid("FrenzyHumanoid"); - const MagicEffectId MagicEffect::FrenzyCreature("FrenzyCreature"); - const MagicEffectId MagicEffect::DemoralizeHumanoid("DemoralizeHumanoid"); - const MagicEffectId MagicEffect::DemoralizeCreature("DemoralizeCreature"); - const MagicEffectId MagicEffect::RallyHumanoid("RallyHumanoid"); - const MagicEffectId MagicEffect::RallyCreature("RallyCreature"); - const MagicEffectId MagicEffect::Dispel("Dispel"); - const MagicEffectId MagicEffect::Soultrap("Soultrap"); - const MagicEffectId MagicEffect::Telekinesis("Telekinesis"); - const MagicEffectId MagicEffect::Mark("Mark"); - const MagicEffectId MagicEffect::Recall("Recall"); - const MagicEffectId MagicEffect::DivineIntervention("DivineIntervention"); - const MagicEffectId MagicEffect::AlmsiviIntervention("AlmsiviIntervention"); - const MagicEffectId MagicEffect::DetectAnimal("DetectAnimal"); - const MagicEffectId MagicEffect::DetectEnchantment("DetectEnchantment"); - const MagicEffectId MagicEffect::DetectKey("DetectKey"); - const MagicEffectId MagicEffect::SpellAbsorption("SpellAbsorption"); - const MagicEffectId MagicEffect::Reflect("Reflect"); - const MagicEffectId MagicEffect::CureCommonDisease("CureCommonDisease"); - const MagicEffectId MagicEffect::CureBlightDisease("CureBlightDisease"); - const MagicEffectId MagicEffect::CureCorprusDisease("CureCorprusDisease"); - const MagicEffectId MagicEffect::CurePoison("CurePoison"); - const MagicEffectId MagicEffect::CureParalyzation("CureParalyzation"); - const MagicEffectId MagicEffect::RestoreAttribute("RestoreAttribute"); - const MagicEffectId MagicEffect::RestoreHealth("RestoreHealth"); - const MagicEffectId MagicEffect::RestoreMagicka("RestoreMagicka"); - const MagicEffectId MagicEffect::RestoreFatigue("RestoreFatigue"); - const MagicEffectId MagicEffect::RestoreSkill("RestoreSkill"); - const MagicEffectId MagicEffect::FortifyAttribute("FortifyAttribute"); - const MagicEffectId MagicEffect::FortifyHealth("FortifyHealth"); - const MagicEffectId MagicEffect::FortifyMagicka("FortifyMagicka"); - const MagicEffectId MagicEffect::FortifyFatigue("FortifyFatigue"); - const MagicEffectId MagicEffect::FortifySkill("FortifySkill"); - const MagicEffectId MagicEffect::FortifyMaximumMagicka("FortifyMaximumMagicka"); - const MagicEffectId MagicEffect::AbsorbAttribute("AbsorbAttribute"); - const MagicEffectId MagicEffect::AbsorbHealth("AbsorbHealth"); - const MagicEffectId MagicEffect::AbsorbMagicka("AbsorbMagicka"); - const MagicEffectId MagicEffect::AbsorbFatigue("AbsorbFatigue"); - const MagicEffectId MagicEffect::AbsorbSkill("AbsorbSkill"); - const MagicEffectId MagicEffect::ResistFire("ResistFire"); - const MagicEffectId MagicEffect::ResistFrost("ResistFrost"); - const MagicEffectId MagicEffect::ResistShock("ResistShock"); - const MagicEffectId MagicEffect::ResistMagicka("ResistMagicka"); - const MagicEffectId MagicEffect::ResistCommonDisease("ResistCommonDisease"); - const MagicEffectId MagicEffect::ResistBlightDisease("ResistBlightDisease"); - const MagicEffectId MagicEffect::ResistCorprusDisease("ResistCorprusDisease"); - const MagicEffectId MagicEffect::ResistPoison("ResistPoison"); - const MagicEffectId MagicEffect::ResistNormalWeapons("ResistNormalWeapons"); - const MagicEffectId MagicEffect::ResistParalysis("ResistParalysis"); - const MagicEffectId MagicEffect::RemoveCurse("RemoveCurse"); - const MagicEffectId MagicEffect::TurnUndead("TurnUndead"); - const MagicEffectId MagicEffect::SummonScamp("SummonScamp"); - const MagicEffectId MagicEffect::SummonClannfear("SummonClannfear"); - const MagicEffectId MagicEffect::SummonDaedroth("SummonDaedroth"); - const MagicEffectId MagicEffect::SummonDremora("SummonDremora"); - const MagicEffectId MagicEffect::SummonAncestralGhost("SummonAncestralGhost"); - const MagicEffectId MagicEffect::SummonSkeletalMinion("SummonSkeletalMinion"); - const MagicEffectId MagicEffect::SummonBonewalker("SummonBonewalker"); - const MagicEffectId MagicEffect::SummonGreaterBonewalker("SummonGreaterBonewalker"); - const MagicEffectId MagicEffect::SummonBonelord("SummonBonelord"); - const MagicEffectId MagicEffect::SummonWingedTwilight("SummonWingedTwilight"); - const MagicEffectId MagicEffect::SummonHunger("SummonHunger"); - const MagicEffectId MagicEffect::SummonGoldenSaint("SummonGoldenSaint"); - const MagicEffectId MagicEffect::SummonFlameAtronach("SummonFlameAtronach"); - const MagicEffectId MagicEffect::SummonFrostAtronach("SummonFrostAtronach"); - const MagicEffectId MagicEffect::SummonStormAtronach("SummonStormAtronach"); - const MagicEffectId MagicEffect::FortifyAttack("FortifyAttack"); - const MagicEffectId MagicEffect::CommandCreature("CommandCreature"); - const MagicEffectId MagicEffect::CommandHumanoid("CommandHumanoid"); - const MagicEffectId MagicEffect::BoundDagger("BoundDagger"); - const MagicEffectId MagicEffect::BoundLongsword("BoundLongsword"); - const MagicEffectId MagicEffect::BoundMace("BoundMace"); - const MagicEffectId MagicEffect::BoundBattleAxe("BoundBattleAxe"); - const MagicEffectId MagicEffect::BoundSpear("BoundSpear"); - const MagicEffectId MagicEffect::BoundLongbow("BoundLongbow"); - const MagicEffectId MagicEffect::ExtraSpell("ExtraSpell"); - const MagicEffectId MagicEffect::BoundCuirass("BoundCuirass"); - const MagicEffectId MagicEffect::BoundHelm("BoundHelm"); - const MagicEffectId MagicEffect::BoundBoots("BoundBoots"); - const MagicEffectId MagicEffect::BoundShield("BoundShield"); - const MagicEffectId MagicEffect::BoundGloves("BoundGloves"); - const MagicEffectId MagicEffect::Corprus("Corprus"); - const MagicEffectId MagicEffect::Vampirism("Vampirism"); - const MagicEffectId MagicEffect::SummonCenturionSphere("SummonCenturionSphere"); - const MagicEffectId MagicEffect::SunDamage("SunDamage"); - const MagicEffectId MagicEffect::StuntedMagicka("StuntedMagicka"); + const StringRefId MagicEffect::WaterBreathing("WaterBreathing"); + const StringRefId MagicEffect::SwiftSwim("SwiftSwim"); + const StringRefId MagicEffect::WaterWalking("WaterWalking"); + const StringRefId MagicEffect::Shield("Shield"); + const StringRefId MagicEffect::FireShield("FireShield"); + const StringRefId MagicEffect::LightningShield("LightningShield"); + const StringRefId MagicEffect::FrostShield("FrostShield"); + const StringRefId MagicEffect::Burden("Burden"); + const StringRefId MagicEffect::Feather("Feather"); + const StringRefId MagicEffect::Jump("Jump"); + const StringRefId MagicEffect::Levitate("Levitate"); + const StringRefId MagicEffect::SlowFall("SlowFall"); + const StringRefId MagicEffect::Lock("Lock"); + const StringRefId MagicEffect::Open("Open"); + const StringRefId MagicEffect::FireDamage("FireDamage"); + const StringRefId MagicEffect::ShockDamage("ShockDamage"); + const StringRefId MagicEffect::FrostDamage("FrostDamage"); + const StringRefId MagicEffect::DrainAttribute("DrainAttribute"); + const StringRefId MagicEffect::DrainHealth("DrainHealth"); + const StringRefId MagicEffect::DrainMagicka("DrainMagicka"); + const StringRefId MagicEffect::DrainFatigue("DrainFatigue"); + const StringRefId MagicEffect::DrainSkill("DrainSkill"); + const StringRefId MagicEffect::DamageAttribute("DamageAttribute"); + const StringRefId MagicEffect::DamageHealth("DamageHealth"); + const StringRefId MagicEffect::DamageMagicka("DamageMagicka"); + const StringRefId MagicEffect::DamageFatigue("DamageFatigue"); + const StringRefId MagicEffect::DamageSkill("DamageSkill"); + const StringRefId MagicEffect::Poison("Poison"); + const StringRefId MagicEffect::WeaknessToFire("WeaknessToFire"); + const StringRefId MagicEffect::WeaknessToFrost("WeaknessToFrost"); + const StringRefId MagicEffect::WeaknessToShock("WeaknessToShock"); + const StringRefId MagicEffect::WeaknessToMagicka("WeaknessToMagicka"); + const StringRefId MagicEffect::WeaknessToCommonDisease("WeaknessToCommonDisease"); + const StringRefId MagicEffect::WeaknessToBlightDisease("WeaknessToBlightDisease"); + const StringRefId MagicEffect::WeaknessToCorprusDisease("WeaknessToCorprusDisease"); + const StringRefId MagicEffect::WeaknessToPoison("WeaknessToPoison"); + const StringRefId MagicEffect::WeaknessToNormalWeapons("WeaknessToNormalWeapons"); + const StringRefId MagicEffect::DisintegrateWeapon("DisintegrateWeapon"); + const StringRefId MagicEffect::DisintegrateArmor("DisintegrateArmor"); + const StringRefId MagicEffect::Invisibility("Invisibility"); + const StringRefId MagicEffect::Chameleon("Chameleon"); + const StringRefId MagicEffect::Light("Light"); + const StringRefId MagicEffect::Sanctuary("Sanctuary"); + const StringRefId MagicEffect::NightEye("NightEye"); + const StringRefId MagicEffect::Charm("Charm"); + const StringRefId MagicEffect::Paralyze("Paralyze"); + const StringRefId MagicEffect::Silence("Silence"); + const StringRefId MagicEffect::Blind("Blind"); + const StringRefId MagicEffect::Sound("Sound"); + const StringRefId MagicEffect::CalmHumanoid("CalmHumanoid"); + const StringRefId MagicEffect::CalmCreature("CalmCreature"); + const StringRefId MagicEffect::FrenzyHumanoid("FrenzyHumanoid"); + const StringRefId MagicEffect::FrenzyCreature("FrenzyCreature"); + const StringRefId MagicEffect::DemoralizeHumanoid("DemoralizeHumanoid"); + const StringRefId MagicEffect::DemoralizeCreature("DemoralizeCreature"); + const StringRefId MagicEffect::RallyHumanoid("RallyHumanoid"); + const StringRefId MagicEffect::RallyCreature("RallyCreature"); + const StringRefId MagicEffect::Dispel("Dispel"); + const StringRefId MagicEffect::Soultrap("Soultrap"); + const StringRefId MagicEffect::Telekinesis("Telekinesis"); + const StringRefId MagicEffect::Mark("Mark"); + const StringRefId MagicEffect::Recall("Recall"); + const StringRefId MagicEffect::DivineIntervention("DivineIntervention"); + const StringRefId MagicEffect::AlmsiviIntervention("AlmsiviIntervention"); + const StringRefId MagicEffect::DetectAnimal("DetectAnimal"); + const StringRefId MagicEffect::DetectEnchantment("DetectEnchantment"); + const StringRefId MagicEffect::DetectKey("DetectKey"); + const StringRefId MagicEffect::SpellAbsorption("SpellAbsorption"); + const StringRefId MagicEffect::Reflect("Reflect"); + const StringRefId MagicEffect::CureCommonDisease("CureCommonDisease"); + const StringRefId MagicEffect::CureBlightDisease("CureBlightDisease"); + const StringRefId MagicEffect::CureCorprusDisease("CureCorprusDisease"); + const StringRefId MagicEffect::CurePoison("CurePoison"); + const StringRefId MagicEffect::CureParalyzation("CureParalyzation"); + const StringRefId MagicEffect::RestoreAttribute("RestoreAttribute"); + const StringRefId MagicEffect::RestoreHealth("RestoreHealth"); + const StringRefId MagicEffect::RestoreMagicka("RestoreMagicka"); + const StringRefId MagicEffect::RestoreFatigue("RestoreFatigue"); + const StringRefId MagicEffect::RestoreSkill("RestoreSkill"); + const StringRefId MagicEffect::FortifyAttribute("FortifyAttribute"); + const StringRefId MagicEffect::FortifyHealth("FortifyHealth"); + const StringRefId MagicEffect::FortifyMagicka("FortifyMagicka"); + const StringRefId MagicEffect::FortifyFatigue("FortifyFatigue"); + const StringRefId MagicEffect::FortifySkill("FortifySkill"); + const StringRefId MagicEffect::FortifyMaximumMagicka("FortifyMaximumMagicka"); + const StringRefId MagicEffect::AbsorbAttribute("AbsorbAttribute"); + const StringRefId MagicEffect::AbsorbHealth("AbsorbHealth"); + const StringRefId MagicEffect::AbsorbMagicka("AbsorbMagicka"); + const StringRefId MagicEffect::AbsorbFatigue("AbsorbFatigue"); + const StringRefId MagicEffect::AbsorbSkill("AbsorbSkill"); + const StringRefId MagicEffect::ResistFire("ResistFire"); + const StringRefId MagicEffect::ResistFrost("ResistFrost"); + const StringRefId MagicEffect::ResistShock("ResistShock"); + const StringRefId MagicEffect::ResistMagicka("ResistMagicka"); + const StringRefId MagicEffect::ResistCommonDisease("ResistCommonDisease"); + const StringRefId MagicEffect::ResistBlightDisease("ResistBlightDisease"); + const StringRefId MagicEffect::ResistCorprusDisease("ResistCorprusDisease"); + const StringRefId MagicEffect::ResistPoison("ResistPoison"); + const StringRefId MagicEffect::ResistNormalWeapons("ResistNormalWeapons"); + const StringRefId MagicEffect::ResistParalysis("ResistParalysis"); + const StringRefId MagicEffect::RemoveCurse("RemoveCurse"); + const StringRefId MagicEffect::TurnUndead("TurnUndead"); + const StringRefId MagicEffect::SummonScamp("SummonScamp"); + const StringRefId MagicEffect::SummonClannfear("SummonClannfear"); + const StringRefId MagicEffect::SummonDaedroth("SummonDaedroth"); + const StringRefId MagicEffect::SummonDremora("SummonDremora"); + const StringRefId MagicEffect::SummonAncestralGhost("SummonAncestralGhost"); + const StringRefId MagicEffect::SummonSkeletalMinion("SummonSkeletalMinion"); + const StringRefId MagicEffect::SummonBonewalker("SummonBonewalker"); + const StringRefId MagicEffect::SummonGreaterBonewalker("SummonGreaterBonewalker"); + const StringRefId MagicEffect::SummonBonelord("SummonBonelord"); + const StringRefId MagicEffect::SummonWingedTwilight("SummonWingedTwilight"); + const StringRefId MagicEffect::SummonHunger("SummonHunger"); + const StringRefId MagicEffect::SummonGoldenSaint("SummonGoldenSaint"); + const StringRefId MagicEffect::SummonFlameAtronach("SummonFlameAtronach"); + const StringRefId MagicEffect::SummonFrostAtronach("SummonFrostAtronach"); + const StringRefId MagicEffect::SummonStormAtronach("SummonStormAtronach"); + const StringRefId MagicEffect::FortifyAttack("FortifyAttack"); + const StringRefId MagicEffect::CommandCreature("CommandCreature"); + const StringRefId MagicEffect::CommandHumanoid("CommandHumanoid"); + const StringRefId MagicEffect::BoundDagger("BoundDagger"); + const StringRefId MagicEffect::BoundLongsword("BoundLongsword"); + const StringRefId MagicEffect::BoundMace("BoundMace"); + const StringRefId MagicEffect::BoundBattleAxe("BoundBattleAxe"); + const StringRefId MagicEffect::BoundSpear("BoundSpear"); + const StringRefId MagicEffect::BoundLongbow("BoundLongbow"); + const StringRefId MagicEffect::ExtraSpell("ExtraSpell"); + const StringRefId MagicEffect::BoundCuirass("BoundCuirass"); + const StringRefId MagicEffect::BoundHelm("BoundHelm"); + const StringRefId MagicEffect::BoundBoots("BoundBoots"); + const StringRefId MagicEffect::BoundShield("BoundShield"); + const StringRefId MagicEffect::BoundGloves("BoundGloves"); + const StringRefId MagicEffect::Corprus("Corprus"); + const StringRefId MagicEffect::Vampirism("Vampirism"); + const StringRefId MagicEffect::SummonCenturionSphere("SummonCenturionSphere"); + const StringRefId MagicEffect::SunDamage("SunDamage"); + const StringRefId MagicEffect::StuntedMagicka("StuntedMagicka"); // Tribunal only - const MagicEffectId MagicEffect::SummonFabricant("SummonFabricant"); + const StringRefId MagicEffect::SummonFabricant("SummonFabricant"); // Bloodmoon only - const MagicEffectId MagicEffect::SummonWolf("SummonWolf"); - const MagicEffectId MagicEffect::SummonBear("SummonBear"); - const MagicEffectId MagicEffect::SummonBonewolf("SummonBonewolf"); - const MagicEffectId MagicEffect::SummonCreature04("SummonCreature04"); - const MagicEffectId MagicEffect::SummonCreature05("SummonCreature05"); + const StringRefId MagicEffect::SummonWolf("SummonWolf"); + const StringRefId MagicEffect::SummonBear("SummonBear"); + const StringRefId MagicEffect::SummonBonewolf("SummonBonewolf"); + const StringRefId MagicEffect::SummonCreature04("SummonCreature04"); + const StringRefId MagicEffect::SummonCreature05("SummonCreature05"); void MagicEffect::load(ESMReader& esm, bool& isDeleted) { @@ -183,7 +183,7 @@ namespace ESM if (index < 0 || index >= Length) esm.fail("Invalid Index!"); - mId = *indexToRefId(index).getIf(); + mId = indexToRefId(index); esm.getSubNameIs("MEDT"); esm.getSubHeader(); @@ -285,75 +285,74 @@ namespace ESM namespace { - std::map makeResistancesMap() + std::unordered_map makeResistancesMap() { - std::map effects; + std::unordered_map effects{ + { MagicEffect::DisintegrateArmor, MagicEffect::Sanctuary }, + { MagicEffect::DisintegrateWeapon, MagicEffect::Sanctuary }, - effects[MagicEffect::DisintegrateArmor] = MagicEffect::Sanctuary; - effects[MagicEffect::DisintegrateWeapon] = MagicEffect::Sanctuary; + { MagicEffect::DrainAttribute, MagicEffect::ResistMagicka }, + { MagicEffect::DrainHealth, MagicEffect::ResistMagicka }, + { MagicEffect::DrainMagicka, MagicEffect::ResistMagicka }, + { MagicEffect::DrainFatigue, MagicEffect::ResistMagicka }, + { MagicEffect::DrainSkill, MagicEffect::ResistMagicka }, + { MagicEffect::DamageAttribute, MagicEffect::ResistMagicka }, + { MagicEffect::DamageHealth, MagicEffect::ResistMagicka }, + { MagicEffect::DamageMagicka, MagicEffect::ResistMagicka }, + { MagicEffect::DamageFatigue, MagicEffect::ResistMagicka }, + { MagicEffect::DamageSkill, MagicEffect::ResistMagicka }, - effects[MagicEffect::DrainAttribute] = MagicEffect::ResistMagicka; - effects[MagicEffect::DrainHealth] = MagicEffect::ResistMagicka; - effects[MagicEffect::DrainMagicka] = MagicEffect::ResistMagicka; - effects[MagicEffect::DrainFatigue] = MagicEffect::ResistMagicka; - effects[MagicEffect::DrainSkill] = MagicEffect::ResistMagicka; - effects[MagicEffect::DamageAttribute] = MagicEffect::ResistMagicka; - effects[MagicEffect::DamageHealth] = MagicEffect::ResistMagicka; - effects[MagicEffect::DamageMagicka] = MagicEffect::ResistMagicka; - effects[MagicEffect::DamageFatigue] = MagicEffect::ResistMagicka; - effects[MagicEffect::DamageSkill] = MagicEffect::ResistMagicka; + { MagicEffect::AbsorbAttribute, MagicEffect::ResistMagicka }, + { MagicEffect::AbsorbHealth, MagicEffect::ResistMagicka }, + { MagicEffect::AbsorbMagicka, MagicEffect::ResistMagicka }, + { MagicEffect::AbsorbFatigue, MagicEffect::ResistMagicka }, + { MagicEffect::AbsorbSkill, MagicEffect::ResistMagicka }, - effects[MagicEffect::AbsorbAttribute] = MagicEffect::ResistMagicka; - effects[MagicEffect::AbsorbHealth] = MagicEffect::ResistMagicka; - effects[MagicEffect::AbsorbMagicka] = MagicEffect::ResistMagicka; - effects[MagicEffect::AbsorbFatigue] = MagicEffect::ResistMagicka; - effects[MagicEffect::AbsorbSkill] = MagicEffect::ResistMagicka; + { MagicEffect::WeaknessToFire, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToFrost, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToShock, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToMagicka, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToCommonDisease, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToBlightDisease, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToCorprusDisease, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToPoison, MagicEffect::ResistMagicka }, + { MagicEffect::WeaknessToNormalWeapons, MagicEffect::ResistMagicka }, - effects[MagicEffect::WeaknessToFire] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToFrost] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToShock] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToMagicka] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToCommonDisease] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToBlightDisease] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToCorprusDisease] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToPoison] = MagicEffect::ResistMagicka; - effects[MagicEffect::WeaknessToNormalWeapons] = MagicEffect::ResistMagicka; + { MagicEffect::Burden, MagicEffect::ResistMagicka }, + { MagicEffect::Charm, MagicEffect::ResistMagicka }, + { MagicEffect::Silence, MagicEffect::ResistMagicka }, + { MagicEffect::Blind, MagicEffect::ResistMagicka }, + { MagicEffect::Sound, MagicEffect::ResistMagicka }, - effects[MagicEffect::Burden] = MagicEffect::ResistMagicka; - effects[MagicEffect::Charm] = MagicEffect::ResistMagicka; - effects[MagicEffect::Silence] = MagicEffect::ResistMagicka; - effects[MagicEffect::Blind] = MagicEffect::ResistMagicka; - effects[MagicEffect::Sound] = MagicEffect::ResistMagicka; + { MagicEffect::CalmHumanoid, MagicEffect::ResistMagicka }, + { MagicEffect::CalmCreature, MagicEffect::ResistMagicka }, + { MagicEffect::FrenzyHumanoid, MagicEffect::ResistMagicka }, + { MagicEffect::FrenzyCreature, MagicEffect::ResistMagicka }, + { MagicEffect::DemoralizeHumanoid, MagicEffect::ResistMagicka }, + { MagicEffect::DemoralizeCreature, MagicEffect::ResistMagicka }, + { MagicEffect::RallyHumanoid, MagicEffect::ResistMagicka }, + { MagicEffect::RallyCreature, MagicEffect::ResistMagicka }, - effects[MagicEffect::CalmHumanoid] = MagicEffect::ResistMagicka; - effects[MagicEffect::CalmCreature] = MagicEffect::ResistMagicka; - effects[MagicEffect::FrenzyHumanoid] = MagicEffect::ResistMagicka; - effects[MagicEffect::FrenzyCreature] = MagicEffect::ResistMagicka; - effects[MagicEffect::DemoralizeHumanoid] = MagicEffect::ResistMagicka; - effects[MagicEffect::DemoralizeCreature] = MagicEffect::ResistMagicka; - effects[MagicEffect::RallyHumanoid] = MagicEffect::ResistMagicka; - effects[MagicEffect::RallyCreature] = MagicEffect::ResistMagicka; - - effects[MagicEffect::TurnUndead] = MagicEffect::ResistMagicka; - - effects[MagicEffect::FireDamage] = MagicEffect::ResistFire; - effects[MagicEffect::FrostDamage] = MagicEffect::ResistFrost; - effects[MagicEffect::ShockDamage] = MagicEffect::ResistShock; - effects[MagicEffect::Vampirism] = MagicEffect::ResistCommonDisease; - effects[MagicEffect::Corprus] = MagicEffect::ResistCorprusDisease; - effects[MagicEffect::Poison] = MagicEffect::ResistPoison; - effects[MagicEffect::Paralyze] = MagicEffect::ResistParalysis; + { MagicEffect::TurnUndead, MagicEffect::ResistMagicka }, + { MagicEffect::FireDamage, MagicEffect::ResistFire }, + { MagicEffect::FrostDamage, MagicEffect::ResistFrost }, + { MagicEffect::ShockDamage, MagicEffect::ResistShock }, + { MagicEffect::Vampirism, MagicEffect::ResistCommonDisease }, + { MagicEffect::Corprus, MagicEffect::ResistCorprusDisease }, + { MagicEffect::Poison, MagicEffect::ResistPoison }, + { MagicEffect::Paralyze, MagicEffect::ResistParalysis }, + }; return effects; } } - RefId MagicEffect::getResistanceEffect(const MagicEffectId& effectId) + RefId MagicEffect::getResistanceEffect(RefId effectId) { // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // - static const std::map effects = makeResistancesMap(); + static const std::unordered_map effects = makeResistancesMap(); if (const auto it = effects.find(effectId); it != effects.end()) return it->second; @@ -363,68 +362,67 @@ namespace ESM namespace { - std::map makeWeaknessesMap() + std::unordered_map makeWeaknessesMap() { - static std::map effects; + std::unordered_map effects{ + { MagicEffect::DrainAttribute, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DrainHealth, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DrainMagicka, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DrainFatigue, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DrainSkill, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DamageAttribute, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DamageHealth, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DamageMagicka, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DamageFatigue, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DamageSkill, MagicEffect::WeaknessToMagicka }, - effects[MagicEffect::DrainAttribute] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DrainHealth] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DrainMagicka] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DrainFatigue] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DrainSkill] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DamageAttribute] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DamageHealth] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DamageMagicka] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DamageFatigue] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DamageSkill] = MagicEffect::WeaknessToMagicka; + { MagicEffect::AbsorbAttribute, MagicEffect::WeaknessToMagicka }, + { MagicEffect::AbsorbHealth, MagicEffect::WeaknessToMagicka }, + { MagicEffect::AbsorbMagicka, MagicEffect::WeaknessToMagicka }, + { MagicEffect::AbsorbFatigue, MagicEffect::WeaknessToMagicka }, + { MagicEffect::AbsorbSkill, MagicEffect::WeaknessToMagicka }, - effects[MagicEffect::AbsorbAttribute] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::AbsorbHealth] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::AbsorbMagicka] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::AbsorbFatigue] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::AbsorbSkill] = MagicEffect::WeaknessToMagicka; + { MagicEffect::WeaknessToFire, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToFrost, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToShock, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToMagicka, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToCommonDisease, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToBlightDisease, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToCorprusDisease, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToPoison, MagicEffect::WeaknessToMagicka }, + { MagicEffect::WeaknessToNormalWeapons, MagicEffect::WeaknessToMagicka }, - effects[MagicEffect::WeaknessToFire] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToFrost] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToShock] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToMagicka] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToCommonDisease] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToBlightDisease] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToCorprusDisease] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToPoison] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::WeaknessToNormalWeapons] = MagicEffect::WeaknessToMagicka; + { MagicEffect::Burden, MagicEffect::WeaknessToMagicka }, + { MagicEffect::Charm, MagicEffect::WeaknessToMagicka }, + { MagicEffect::Silence, MagicEffect::WeaknessToMagicka }, + { MagicEffect::Blind, MagicEffect::WeaknessToMagicka }, + { MagicEffect::Sound, MagicEffect::WeaknessToMagicka }, - effects[MagicEffect::Burden] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::Charm] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::Silence] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::Blind] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::Sound] = MagicEffect::WeaknessToMagicka; + { MagicEffect::CalmHumanoid, MagicEffect::WeaknessToMagicka }, + { MagicEffect::CalmCreature, MagicEffect::WeaknessToMagicka }, + { MagicEffect::FrenzyHumanoid, MagicEffect::WeaknessToMagicka }, + { MagicEffect::FrenzyCreature, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DemoralizeHumanoid, MagicEffect::WeaknessToMagicka }, + { MagicEffect::DemoralizeCreature, MagicEffect::WeaknessToMagicka }, + { MagicEffect::RallyHumanoid, MagicEffect::WeaknessToMagicka }, + { MagicEffect::RallyCreature, MagicEffect::WeaknessToMagicka }, - effects[MagicEffect::CalmHumanoid] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::CalmCreature] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::FrenzyHumanoid] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::FrenzyCreature] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DemoralizeHumanoid] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::DemoralizeCreature] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::RallyHumanoid] = MagicEffect::WeaknessToMagicka; - effects[MagicEffect::RallyCreature] = MagicEffect::WeaknessToMagicka; - - effects[MagicEffect::TurnUndead] = MagicEffect::WeaknessToMagicka; - - effects[MagicEffect::FireDamage] = MagicEffect::WeaknessToFire; - effects[MagicEffect::FrostDamage] = MagicEffect::WeaknessToFrost; - effects[MagicEffect::ShockDamage] = MagicEffect::WeaknessToShock; - effects[MagicEffect::Vampirism] = MagicEffect::WeaknessToCommonDisease; - effects[MagicEffect::Corprus] = MagicEffect::WeaknessToCorprusDisease; - effects[MagicEffect::Poison] = MagicEffect::WeaknessToPoison; + { MagicEffect::TurnUndead, MagicEffect::WeaknessToMagicka }, + { MagicEffect::FireDamage, MagicEffect::WeaknessToFire }, + { MagicEffect::FrostDamage, MagicEffect::WeaknessToFrost }, + { MagicEffect::ShockDamage, MagicEffect::WeaknessToShock }, + { MagicEffect::Vampirism, MagicEffect::WeaknessToCommonDisease }, + { MagicEffect::Corprus, MagicEffect::WeaknessToCorprusDisease }, + { MagicEffect::Poison, MagicEffect::WeaknessToPoison }, + }; return effects; } } - RefId MagicEffect::getWeaknessEffect(const MagicEffectId& effectId) + RefId MagicEffect::getWeaknessEffect(RefId effectId) { - static const std::map effects = makeWeaknessesMap(); + static const std::unordered_map effects = makeWeaknessesMap(); if (const auto it = effects.find(effectId); it != effects.end()) return it->second; @@ -433,7 +431,7 @@ namespace ESM } // Map effect index to GMST name - const std::array sGmstEffectIds = { + static constexpr std::array sGmstEffectIds = { "sEffectWaterBreathing", "sEffectSwiftSwim", "sEffectWaterWalking", @@ -583,158 +581,7 @@ namespace ESM "sEffectSummonCreature05", }; - static const std::array sMagicEffectNames{ - "WaterBreathing", - "SwiftSwim", - "WaterWalking", - "Shield", - "FireShield", - "LightningShield", - "FrostShield", - "Burden", - "Feather", - "Jump", - "Levitate", - "SlowFall", - "Lock", - "Open", - "FireDamage", - "ShockDamage", - "FrostDamage", - "DrainAttribute", - "DrainHealth", - "DrainMagicka", - "DrainFatigue", - "DrainSkill", - "DamageAttribute", - "DamageHealth", - "DamageMagicka", - "DamageFatigue", - "DamageSkill", - "Poison", - "WeaknessToFire", - "WeaknessToFrost", - "WeaknessToShock", - "WeaknessToMagicka", - "WeaknessToCommonDisease", - "WeaknessToBlightDisease", - "WeaknessToCorprusDisease", - "WeaknessToPoison", - "WeaknessToNormalWeapons", - "DisintegrateWeapon", - "DisintegrateArmor", - "Invisibility", - "Chameleon", - "Light", - "Sanctuary", - "NightEye", - "Charm", - "Paralyze", - "Silence", - "Blind", - "Sound", - "CalmHumanoid", - "CalmCreature", - "FrenzyHumanoid", - "FrenzyCreature", - "DemoralizeHumanoid", - "DemoralizeCreature", - "RallyHumanoid", - "RallyCreature", - "Dispel", - "Soultrap", - "Telekinesis", - "Mark", - "Recall", - "DivineIntervention", - "AlmsiviIntervention", - "DetectAnimal", - "DetectEnchantment", - "DetectKey", - "SpellAbsorption", - "Reflect", - "CureCommonDisease", - "CureBlightDisease", - "CureCorprusDisease", - "CurePoison", - "CureParalyzation", - "RestoreAttribute", - "RestoreHealth", - "RestoreMagicka", - "RestoreFatigue", - "RestoreSkill", - "FortifyAttribute", - "FortifyHealth", - "FortifyMagicka", - "FortifyFatigue", - "FortifySkill", - "FortifyMaximumMagicka", - "AbsorbAttribute", - "AbsorbHealth", - "AbsorbMagicka", - "AbsorbFatigue", - "AbsorbSkill", - "ResistFire", - "ResistFrost", - "ResistShock", - "ResistMagicka", - "ResistCommonDisease", - "ResistBlightDisease", - "ResistCorprusDisease", - "ResistPoison", - "ResistNormalWeapons", - "ResistParalysis", - "RemoveCurse", - "TurnUndead", - "SummonScamp", - "SummonClannfear", - "SummonDaedroth", - "SummonDremora", - "SummonAncestralGhost", - "SummonSkeletalMinion", - "SummonBonewalker", - "SummonGreaterBonewalker", - "SummonBonelord", - "SummonWingedTwilight", - "SummonHunger", - "SummonGoldenSaint", - "SummonFlameAtronach", - "SummonFrostAtronach", - "SummonStormAtronach", - "FortifyAttack", - "CommandCreature", - "CommandHumanoid", - "BoundDagger", - "BoundLongsword", - "BoundMace", - "BoundBattleAxe", - "BoundSpear", - "BoundLongbow", - "ExtraSpell", - "BoundCuirass", - "BoundHelm", - "BoundBoots", - "BoundShield", - "BoundGloves", - "Corprus", - "Vampirism", - "SummonCenturionSphere", - "SunDamage", - "StuntedMagicka", - - // Tribunal only - "SummonFabricant", - - // Bloodmoon only - "SummonWolf", - "SummonBear", - "SummonBonewolf", - "SummonCreature04", - "SummonCreature05", - }; - - static const std::array sMagicEffectIds - { + static const std::array sMagicEffectIds{ MagicEffect::WaterBreathing, MagicEffect::SwiftSwim, MagicEffect::WaterWalking, @@ -884,21 +731,20 @@ namespace ESM MagicEffect::SummonCreature05, }; - template - static std::map initStringToIntMap(const Collection& strings) + template > + static std::map initToIndexMap( + const Collection& data, Comparator comp = Comparator()) { - std::map map; - for (size_t i = 0; i < strings.size(); i++) - map[strings[i]] = static_cast(i); - + std::map map(comp); + for (size_t i = 0; i < data.size(); ++i) + map[data[i]] = static_cast(i); return map; } const std::map sGmstEffectIdToIndexMap - = initStringToIntMap(sGmstEffectIds); + = initToIndexMap(sGmstEffectIds, Misc::StringUtils::CiComp()); - const std::map sMagicEffectNamesToIndexMap - = initStringToIntMap(sMagicEffectNames); + const std::map sMagicEffectIdToIndexMap = initToIndexMap(sMagicEffectIds); MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { @@ -911,8 +757,8 @@ namespace ESM return MDT_Feet; if (index == 118 || index == 119) return MDT_Level; - if ((index >= 28 && index <= 36) || (index >= 90 && index <= 99) || index == 40 || index == 47 - || index == 57 || index == 68) + if ((index >= 28 && index <= 36) || (index >= 90 && index <= 99) || index == 40 || index == 47 || index == 57 + || index == 68) return MDT_Percentage; return MDT_Points; @@ -950,10 +796,8 @@ namespace ESM return color; } - std::string_view MagicEffect::refIdToGmstString(const RefId& effectId) + std::string_view MagicEffect::refIdToGmstString(RefId effectId) { - if (effectId.empty()) - return {}; int index = refIdToIndex(effectId); if (index < 0 || index >= Length) return {}; @@ -975,46 +819,18 @@ namespace ESM return sMagicEffectIds[index]; } - int MagicEffect::refIdToIndex(const RefId& effectId) + int MagicEffect::refIdToIndex(RefId effectId) { - if (effectId.empty()) - return -1; - for (size_t i = 0; i < sMagicEffectIds.size(); ++i) - if (sMagicEffectIds[i] == effectId) - return static_cast(i); + const auto it = sMagicEffectIdToIndexMap.find(effectId); + if (it != sMagicEffectIdToIndexMap.end()) + return it->second; return -1; } - RefId MagicEffect::nameToRefId(std::string_view name) - { - auto it = sMagicEffectNamesToIndexMap.find(name); - if (it == sMagicEffectNamesToIndexMap.end()) - return {}; - return sMagicEffectIds[it->second]; - } - - std::string_view MagicEffect::refIdToName(const RefId& effectId) - { - if (effectId.empty()) - return {}; - auto it = sMagicEffectNamesToIndexMap.find(effectId.getRefIdString()); - if (it == sMagicEffectNamesToIndexMap.end()) - return {}; - return sMagicEffectNames[it->second]; - } - std::string_view MagicEffect::indexToName(int index) { if (index < 0 || index >= Length) return {}; - return sMagicEffectNames[index]; - } - - int MagicEffect::indexNameToIndex(std::string_view name) - { - auto it = sMagicEffectNamesToIndexMap.find(name); - if (it == sMagicEffectNamesToIndexMap.end()) - return -1; - return it->second; + return sMagicEffectIds[index].getRefIdString(); } } diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index 23e3c11491..b4c0063d13 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -18,8 +18,6 @@ namespace ESM class ESMReader; class ESMWriter; - using MagicEffectId = StringRefId; - struct MagicEffect { constexpr static RecNameInts sRecordId = REC_MGEF; @@ -28,7 +26,7 @@ namespace ESM static std::string_view getRecordType() { return "MagicEffect"; } uint32_t mRecordFlags; - MagicEffectId mId; + RefId mId; enum Flags { @@ -86,9 +84,9 @@ namespace ESM }; // 36 bytes /// Returns the effect that provides resistance against \a effect (or empty RefId if there's none) - static RefId getResistanceEffect(const MagicEffectId& effect); + static RefId getResistanceEffect(RefId effect); /// Returns the effect that induces weakness against \a effect (or empty RefId if there's none) - static RefId getWeaknessEffect(const MagicEffectId& effect); + static RefId getWeaknessEffect(RefId effect); MagnitudeDisplayType getMagnitudeDisplayType() const; @@ -108,167 +106,163 @@ namespace ESM osg::Vec4f getColor() const; - static const MagicEffectId WaterBreathing; - static const MagicEffectId SwiftSwim; - static const MagicEffectId WaterWalking; - static const MagicEffectId Shield; - static const MagicEffectId FireShield; - static const MagicEffectId LightningShield; - static const MagicEffectId FrostShield; - static const MagicEffectId Burden; - static const MagicEffectId Feather; - static const MagicEffectId Jump; - static const MagicEffectId Levitate; - static const MagicEffectId SlowFall; - static const MagicEffectId Lock; - static const MagicEffectId Open; - static const MagicEffectId FireDamage; - static const MagicEffectId ShockDamage; - static const MagicEffectId FrostDamage; - static const MagicEffectId DrainAttribute; - static const MagicEffectId DrainHealth; - static const MagicEffectId DrainMagicka; - static const MagicEffectId DrainFatigue; - static const MagicEffectId DrainSkill; - static const MagicEffectId DamageAttribute; - static const MagicEffectId DamageHealth; - static const MagicEffectId DamageMagicka; - static const MagicEffectId DamageFatigue; - static const MagicEffectId DamageSkill; - static const MagicEffectId Poison; - static const MagicEffectId WeaknessToFire; - static const MagicEffectId WeaknessToFrost; - static const MagicEffectId WeaknessToShock; - static const MagicEffectId WeaknessToMagicka; - static const MagicEffectId WeaknessToCommonDisease; - static const MagicEffectId WeaknessToBlightDisease; - static const MagicEffectId WeaknessToCorprusDisease; - static const MagicEffectId WeaknessToPoison; - static const MagicEffectId WeaknessToNormalWeapons; - static const MagicEffectId DisintegrateWeapon; - static const MagicEffectId DisintegrateArmor; - static const MagicEffectId Invisibility; - static const MagicEffectId Chameleon; - static const MagicEffectId Light; - static const MagicEffectId Sanctuary; - static const MagicEffectId NightEye; - static const MagicEffectId Charm; - static const MagicEffectId Paralyze; - static const MagicEffectId Silence; - static const MagicEffectId Blind; - static const MagicEffectId Sound; - static const MagicEffectId CalmHumanoid; - static const MagicEffectId CalmCreature; - static const MagicEffectId FrenzyHumanoid; - static const MagicEffectId FrenzyCreature; - static const MagicEffectId DemoralizeHumanoid; - static const MagicEffectId DemoralizeCreature; - static const MagicEffectId RallyHumanoid; - static const MagicEffectId RallyCreature; - static const MagicEffectId Dispel; - static const MagicEffectId Soultrap; - static const MagicEffectId Telekinesis; - static const MagicEffectId Mark; - static const MagicEffectId Recall; - static const MagicEffectId DivineIntervention; - static const MagicEffectId AlmsiviIntervention; - static const MagicEffectId DetectAnimal; - static const MagicEffectId DetectEnchantment; - static const MagicEffectId DetectKey; - static const MagicEffectId SpellAbsorption; - static const MagicEffectId Reflect; - static const MagicEffectId CureCommonDisease; - static const MagicEffectId CureBlightDisease; - static const MagicEffectId CureCorprusDisease; - static const MagicEffectId CurePoison; - static const MagicEffectId CureParalyzation; - static const MagicEffectId RestoreAttribute; - static const MagicEffectId RestoreHealth; - static const MagicEffectId RestoreMagicka; - static const MagicEffectId RestoreFatigue; - static const MagicEffectId RestoreSkill; - static const MagicEffectId FortifyAttribute; - static const MagicEffectId FortifyHealth; - static const MagicEffectId FortifyMagicka; - static const MagicEffectId FortifyFatigue; - static const MagicEffectId FortifySkill; - static const MagicEffectId FortifyMaximumMagicka; - static const MagicEffectId AbsorbAttribute; - static const MagicEffectId AbsorbHealth; - static const MagicEffectId AbsorbMagicka; - static const MagicEffectId AbsorbFatigue; - static const MagicEffectId AbsorbSkill; - static const MagicEffectId ResistFire; - static const MagicEffectId ResistFrost; - static const MagicEffectId ResistShock; - static const MagicEffectId ResistMagicka; - static const MagicEffectId ResistCommonDisease; - static const MagicEffectId ResistBlightDisease; - static const MagicEffectId ResistCorprusDisease; - static const MagicEffectId ResistPoison; - static const MagicEffectId ResistNormalWeapons; - static const MagicEffectId ResistParalysis; - static const MagicEffectId RemoveCurse; - static const MagicEffectId TurnUndead; - static const MagicEffectId SummonScamp; - static const MagicEffectId SummonClannfear; - static const MagicEffectId SummonDaedroth; - static const MagicEffectId SummonDremora; - static const MagicEffectId SummonAncestralGhost; - static const MagicEffectId SummonSkeletalMinion; - static const MagicEffectId SummonBonewalker; - static const MagicEffectId SummonGreaterBonewalker; - static const MagicEffectId SummonBonelord; - static const MagicEffectId SummonWingedTwilight; - static const MagicEffectId SummonHunger; - static const MagicEffectId SummonGoldenSaint; - static const MagicEffectId SummonFlameAtronach; - static const MagicEffectId SummonFrostAtronach; - static const MagicEffectId SummonStormAtronach; - static const MagicEffectId FortifyAttack; - static const MagicEffectId CommandCreature; - static const MagicEffectId CommandHumanoid; - static const MagicEffectId BoundDagger; - static const MagicEffectId BoundLongsword; - static const MagicEffectId BoundMace; - static const MagicEffectId BoundBattleAxe; - static const MagicEffectId BoundSpear; - static const MagicEffectId BoundLongbow; - static const MagicEffectId ExtraSpell; - static const MagicEffectId BoundCuirass; - static const MagicEffectId BoundHelm; - static const MagicEffectId BoundBoots; - static const MagicEffectId BoundShield; - static const MagicEffectId BoundGloves; - static const MagicEffectId Corprus; - static const MagicEffectId Vampirism; - static const MagicEffectId SummonCenturionSphere; - static const MagicEffectId SunDamage; - static const MagicEffectId StuntedMagicka; + static const StringRefId WaterBreathing; + static const StringRefId SwiftSwim; + static const StringRefId WaterWalking; + static const StringRefId Shield; + static const StringRefId FireShield; + static const StringRefId LightningShield; + static const StringRefId FrostShield; + static const StringRefId Burden; + static const StringRefId Feather; + static const StringRefId Jump; + static const StringRefId Levitate; + static const StringRefId SlowFall; + static const StringRefId Lock; + static const StringRefId Open; + static const StringRefId FireDamage; + static const StringRefId ShockDamage; + static const StringRefId FrostDamage; + static const StringRefId DrainAttribute; + static const StringRefId DrainHealth; + static const StringRefId DrainMagicka; + static const StringRefId DrainFatigue; + static const StringRefId DrainSkill; + static const StringRefId DamageAttribute; + static const StringRefId DamageHealth; + static const StringRefId DamageMagicka; + static const StringRefId DamageFatigue; + static const StringRefId DamageSkill; + static const StringRefId Poison; + static const StringRefId WeaknessToFire; + static const StringRefId WeaknessToFrost; + static const StringRefId WeaknessToShock; + static const StringRefId WeaknessToMagicka; + static const StringRefId WeaknessToCommonDisease; + static const StringRefId WeaknessToBlightDisease; + static const StringRefId WeaknessToCorprusDisease; + static const StringRefId WeaknessToPoison; + static const StringRefId WeaknessToNormalWeapons; + static const StringRefId DisintegrateWeapon; + static const StringRefId DisintegrateArmor; + static const StringRefId Invisibility; + static const StringRefId Chameleon; + static const StringRefId Light; + static const StringRefId Sanctuary; + static const StringRefId NightEye; + static const StringRefId Charm; + static const StringRefId Paralyze; + static const StringRefId Silence; + static const StringRefId Blind; + static const StringRefId Sound; + static const StringRefId CalmHumanoid; + static const StringRefId CalmCreature; + static const StringRefId FrenzyHumanoid; + static const StringRefId FrenzyCreature; + static const StringRefId DemoralizeHumanoid; + static const StringRefId DemoralizeCreature; + static const StringRefId RallyHumanoid; + static const StringRefId RallyCreature; + static const StringRefId Dispel; + static const StringRefId Soultrap; + static const StringRefId Telekinesis; + static const StringRefId Mark; + static const StringRefId Recall; + static const StringRefId DivineIntervention; + static const StringRefId AlmsiviIntervention; + static const StringRefId DetectAnimal; + static const StringRefId DetectEnchantment; + static const StringRefId DetectKey; + static const StringRefId SpellAbsorption; + static const StringRefId Reflect; + static const StringRefId CureCommonDisease; + static const StringRefId CureBlightDisease; + static const StringRefId CureCorprusDisease; + static const StringRefId CurePoison; + static const StringRefId CureParalyzation; + static const StringRefId RestoreAttribute; + static const StringRefId RestoreHealth; + static const StringRefId RestoreMagicka; + static const StringRefId RestoreFatigue; + static const StringRefId RestoreSkill; + static const StringRefId FortifyAttribute; + static const StringRefId FortifyHealth; + static const StringRefId FortifyMagicka; + static const StringRefId FortifyFatigue; + static const StringRefId FortifySkill; + static const StringRefId FortifyMaximumMagicka; + static const StringRefId AbsorbAttribute; + static const StringRefId AbsorbHealth; + static const StringRefId AbsorbMagicka; + static const StringRefId AbsorbFatigue; + static const StringRefId AbsorbSkill; + static const StringRefId ResistFire; + static const StringRefId ResistFrost; + static const StringRefId ResistShock; + static const StringRefId ResistMagicka; + static const StringRefId ResistCommonDisease; + static const StringRefId ResistBlightDisease; + static const StringRefId ResistCorprusDisease; + static const StringRefId ResistPoison; + static const StringRefId ResistNormalWeapons; + static const StringRefId ResistParalysis; + static const StringRefId RemoveCurse; + static const StringRefId TurnUndead; + static const StringRefId SummonScamp; + static const StringRefId SummonClannfear; + static const StringRefId SummonDaedroth; + static const StringRefId SummonDremora; + static const StringRefId SummonAncestralGhost; + static const StringRefId SummonSkeletalMinion; + static const StringRefId SummonBonewalker; + static const StringRefId SummonGreaterBonewalker; + static const StringRefId SummonBonelord; + static const StringRefId SummonWingedTwilight; + static const StringRefId SummonHunger; + static const StringRefId SummonGoldenSaint; + static const StringRefId SummonFlameAtronach; + static const StringRefId SummonFrostAtronach; + static const StringRefId SummonStormAtronach; + static const StringRefId FortifyAttack; + static const StringRefId CommandCreature; + static const StringRefId CommandHumanoid; + static const StringRefId BoundDagger; + static const StringRefId BoundLongsword; + static const StringRefId BoundMace; + static const StringRefId BoundBattleAxe; + static const StringRefId BoundSpear; + static const StringRefId BoundLongbow; + static const StringRefId ExtraSpell; + static const StringRefId BoundCuirass; + static const StringRefId BoundHelm; + static const StringRefId BoundBoots; + static const StringRefId BoundShield; + static const StringRefId BoundGloves; + static const StringRefId Corprus; + static const StringRefId Vampirism; + static const StringRefId SummonCenturionSphere; + static const StringRefId SunDamage; + static const StringRefId StuntedMagicka; // Tribunal only - static const MagicEffectId SummonFabricant; + static const StringRefId SummonFabricant; // Bloodmoon only - static const MagicEffectId SummonWolf; - static const MagicEffectId SummonBear; - static const MagicEffectId SummonBonewolf; - static const MagicEffectId SummonCreature04; - static const MagicEffectId SummonCreature05; + static const StringRefId SummonWolf; + static const StringRefId SummonBear; + static const StringRefId SummonBonewolf; + static const StringRefId SummonCreature04; + static const StringRefId SummonCreature05; static constexpr int Length = 143; - static std::string_view refIdToGmstString(const RefId& effectId); + static std::string_view refIdToGmstString(RefId effectId); static RefId effectGmstIdToRefId(std::string_view gmstId); static RefId indexToRefId(int index); - static int refIdToIndex(const RefId& effectId); - - static RefId nameToRefId(std::string_view name); - static std::string_view refIdToName(const RefId& effectId); + static int refIdToIndex(RefId effectId); static std::string_view indexToName(int index); - static int indexNameToIndex(std::string_view name); }; } #endif diff --git a/components/esm3/magiceffects.hpp b/components/esm3/magiceffects.hpp index ab93288d92..d33ff96e9a 100644 --- a/components/esm3/magiceffects.hpp +++ b/components/esm3/magiceffects.hpp @@ -24,7 +24,7 @@ namespace ESM struct SummonKey { - SummonKey(const ESM::RefId& effectId, const ESM::RefId& sourceId, int32_t index) + SummonKey(ESM::RefId effectId, const ESM::RefId& sourceId, int32_t index) : mEffectId(effectId) , mSourceId(sourceId) , mEffectIndex(index) From c6cc1be34769dd4dff6dc1340d732be6158f06ff Mon Sep 17 00:00:00 2001 From: Telvanni 4Life Date: Wed, 7 Jan 2026 15:12:34 -0500 Subject: [PATCH 14/14] - Fixed OpenMW-CS incorrectly passing magic effect ids - Resolved merge conflicts with !4323 - Minor refactor of affectsSkill helper function - Removed redundant class definition --- apps/opencs/model/world/nestedcoladapterimp.hpp | 6 ++---- apps/opencs/model/world/refidadapterimp.cpp | 5 ++--- apps/openmw/mwmechanics/summoning.hpp | 1 - components/esm3/activespells.cpp | 14 ++++++-------- components/esm3/creaturestats.cpp | 4 ++-- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 596341e029..0361187846 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -319,9 +319,7 @@ namespace CSMWorld switch (subColIndex) { case 0: - if (effect.mEffectID.empty()) - return QVariant(); - return QString::fromStdString(effect.mEffectID.getRefIdString()); + return ESM::MagicEffect::refIdToIndex(effect.mEffectID); case 1: { if (effect.mEffectID == ESM::MagicEffect::DrainSkill @@ -373,7 +371,7 @@ namespace CSMWorld { case 0: { - effect.mEffectID = ESM::RefId::deserializeText(value.toString().toStdString()); + effect.mEffectID = ESM::MagicEffect::indexToRefId(value.toInt()); if (effect.mEffectID == ESM::MagicEffect::DrainSkill || effect.mEffectID == ESM::MagicEffect::DamageSkill || effect.mEffectID == ESM::MagicEffect::RestoreSkill diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 4d4ca1ab6a..87093934b3 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -149,7 +149,7 @@ QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData( switch (subColIndex) { case 0: - return effectId.empty() ? QVariant() : QString::fromStdString(effectId.getRefIdString()); + return ESM::MagicEffect::refIdToIndex(effectId); case 1: { if (effectId == ESM::MagicEffect::DrainSkill || effectId == ESM::MagicEffect::DamageSkill @@ -183,8 +183,7 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedData( if (subRowIndex < 0 || subRowIndex >= 4) throw std::runtime_error("index out of range"); - ESM::RefId effectId - = value.toString().isEmpty() ? ESM::RefId() : ESM::RefId::deserializeText(value.toString().toStdString()); + ESM::RefId effectId = ESM::MagicEffect::indexToRefId(value.toInt()); switch (subColIndex) { diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 7818230fc4..88ab6d0e42 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -9,7 +9,6 @@ namespace ESM { class RefId; - class StringRefId; } namespace MWWorld { diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index 2ddbc4265c..657808e60b 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -62,12 +62,8 @@ namespace ESM MagicEffect::FortifySkill, MagicEffect::AbsorbSkill, }; - if (effectId.empty()) - return false; - for (size_t i = 0; i < affectsSkillEffects.size(); ++i) - if (affectsSkillEffects[i] == effectId) - return true; - return false; + return std::find(affectsSkillEffects.begin(), affectsSkillEffects.end(), effectId) + != affectsSkillEffects.end(); } void saveImpl(ESMWriter& esm, const std::vector& spells, NAME tag) @@ -90,7 +86,7 @@ namespace ESM for (auto& effect : params.mEffects) { - esm.writeHNT("MGEF", effect.mEffectId); + esm.writeHNT("MGEF", ESM::MagicEffect::refIdToIndex(effect.mEffectId)); if (const ESM::RefId* id = std::get_if(&effect.mArg)) { if (!id->empty()) @@ -175,8 +171,10 @@ namespace ESM while (esm.isNextSub("MGEF")) { + int32_t effectId; ActiveEffect effect; - esm.getHT(effect.mEffectId); + esm.getHT(effectId); + effect.mEffectId = ESM::MagicEffect::indexToRefId(effectId); if (format <= MaxActorIdSaveGameFormatVersion) { int32_t arg = -1; diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index 5d30fbd07a..c034a6a0d1 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -121,8 +121,8 @@ namespace ESM esm.getHNT(actorId, "ACID"); mSummonedCreatureMap[SummonKey(ESM::MagicEffect::indexToRefId(magicEffect), source, effectIndex)] = actorId; - mSummonedCreatures.emplace(ESM::MagicEffect::indexToRefId( - magicEffect), RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }); + mSummonedCreatures.emplace(ESM::MagicEffect::indexToRefId(magicEffect), + RefNum{ .mIndex = static_cast(actorId), .mContentFile = -1 }); } } else