1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-01-20 16:30:54 +00:00
openmw/components/esm3/loadmgef.cpp
Telvanni 4Life 9159788c9b 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<ESM::GameSettings> 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.
2026-01-08 07:44:06 -05:00

836 lines
36 KiB
C++

#include "loadmgef.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "loadskil.hpp"
#include <components/misc/strings/algorithm.hpp>
namespace ESM
{
namespace
{
const int NumberOfHardcodedFlags = 143;
const int HardcodedFlags[NumberOfHardcodedFlags] = { 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0,
0x11d0, 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0, 0x1f0, 0x11d2, 0x11f0, 0x11d0,
0x11d0, 0x11d1, 0x1d2, 0x1f0, 0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0,
0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8, 0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8,
0x11d0, 0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11c4, 0x111b8,
0x1040, 0x104c, 0x104c, 0x104c, 0x104c, 0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc, 0x1cc,
0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2, 0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190,
0x20190, 0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0,
0x1c0, 0x11190, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048,
0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048,
0x1188, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40, 0x11c8, 0x1048, 0x1048,
0x1048, 0x1048, 0x1048, 0x1048 };
}
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 StringRefId MagicEffect::SummonFabricant("SummonFabricant");
// Bloodmoon only
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)
{
isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future)
mRecordFlags = esm.getRecordFlags();
int32_t index = -1;
esm.getHNT(index, "INDX");
if (index < 0 || index >= Length)
esm.fail("Invalid Index!");
mId = indexToRefId(index);
esm.getSubNameIs("MEDT");
esm.getSubHeader();
int32_t school;
esm.getT(school);
mData.mSchool = MagicSchool::indexToSkillRefId(school);
esm.getT(mData.mBaseCost);
esm.getT(mData.mFlags);
esm.getT(mData.mRed);
esm.getT(mData.mGreen);
esm.getT(mData.mBlue);
esm.getT(mData.mUnknown1);
esm.getT(mData.mSpeed);
esm.getT(mData.mUnknown2);
if (esm.getFormatVersion() == DefaultFormatVersion)
{
// don't allow mods to change fixed flags in the legacy format
mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);
if (index >= 0 && index < NumberOfHardcodedFlags)
mData.mFlags |= HardcodedFlags[index];
}
// vanilla MW accepts the _SND subrecords before or after DESC... I hope
// this isn't true for other records, or we have to do a mass-refactor
while (esm.hasMoreSubs())
{
esm.getSubName();
switch (esm.retSubName().toInt())
{
case fourCC("ITEX"):
mIcon = esm.getHString();
break;
case fourCC("PTEX"):
mParticle = esm.getHString();
break;
case fourCC("BSND"):
mBoltSound = esm.getRefId();
break;
case fourCC("CSND"):
mCastSound = esm.getRefId();
break;
case fourCC("HSND"):
mHitSound = esm.getRefId();
break;
case fourCC("ASND"):
mAreaSound = esm.getRefId();
break;
case fourCC("CVFX"):
mCasting = esm.getRefId();
break;
case fourCC("BVFX"):
mBolt = esm.getRefId();
break;
case fourCC("HVFX"):
mHit = esm.getRefId();
break;
case fourCC("AVFX"):
mArea = esm.getRefId();
break;
case fourCC("DESC"):
mDescription = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
}
void MagicEffect::save(ESMWriter& esm, bool /*isDeleted*/) const
{
esm.writeHNT("INDX", refIdToIndex(mId));
esm.startSubRecord("MEDT");
esm.writeT(MagicSchool::skillRefIdToIndex(mData.mSchool));
esm.writeT(mData.mBaseCost);
esm.writeT(mData.mFlags);
esm.writeT(mData.mRed);
esm.writeT(mData.mGreen);
esm.writeT(mData.mBlue);
esm.writeT(mData.mUnknown1);
esm.writeT(mData.mSpeed);
esm.writeT(mData.mUnknown2);
esm.endRecord("MEDT");
esm.writeHNOCString("ITEX", mIcon);
esm.writeHNOCString("PTEX", mParticle);
esm.writeHNOCRefId("BSND", mBoltSound);
esm.writeHNOCRefId("CSND", mCastSound);
esm.writeHNOCRefId("HSND", mHitSound);
esm.writeHNOCRefId("ASND", mAreaSound);
esm.writeHNOCRefId("CVFX", mCasting);
esm.writeHNOCRefId("BVFX", mBolt);
esm.writeHNOCRefId("HVFX", mHit);
esm.writeHNOCRefId("AVFX", mArea);
esm.writeHNOString("DESC", mDescription);
}
namespace
{
std::unordered_map<RefId, RefId> makeResistancesMap()
{
std::unordered_map<RefId, RefId> effects{
{ MagicEffect::DisintegrateArmor, MagicEffect::Sanctuary },
{ 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 },
{ MagicEffect::AbsorbAttribute, MagicEffect::ResistMagicka },
{ MagicEffect::AbsorbHealth, MagicEffect::ResistMagicka },
{ MagicEffect::AbsorbMagicka, MagicEffect::ResistMagicka },
{ MagicEffect::AbsorbFatigue, MagicEffect::ResistMagicka },
{ 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 },
{ MagicEffect::Burden, MagicEffect::ResistMagicka },
{ MagicEffect::Charm, MagicEffect::ResistMagicka },
{ MagicEffect::Silence, MagicEffect::ResistMagicka },
{ MagicEffect::Blind, MagicEffect::ResistMagicka },
{ 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 },
{ 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(RefId effectId)
{
// Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
// <Effect, Effect providing resistance against first effect>
static const std::unordered_map<RefId, RefId> effects = makeResistancesMap();
if (const auto it = effects.find(effectId); it != effects.end())
return it->second;
return {};
}
namespace
{
std::unordered_map<RefId, RefId> makeWeaknessesMap()
{
std::unordered_map<RefId, RefId> 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 },
{ MagicEffect::AbsorbAttribute, MagicEffect::WeaknessToMagicka },
{ MagicEffect::AbsorbHealth, MagicEffect::WeaknessToMagicka },
{ MagicEffect::AbsorbMagicka, MagicEffect::WeaknessToMagicka },
{ MagicEffect::AbsorbFatigue, MagicEffect::WeaknessToMagicka },
{ 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 },
{ MagicEffect::Burden, MagicEffect::WeaknessToMagicka },
{ MagicEffect::Charm, MagicEffect::WeaknessToMagicka },
{ MagicEffect::Silence, MagicEffect::WeaknessToMagicka },
{ MagicEffect::Blind, MagicEffect::WeaknessToMagicka },
{ 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 },
{ 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(RefId effectId)
{
static const std::unordered_map<RefId, RefId> effects = makeWeaknessesMap();
if (const auto it = effects.find(effectId); it != effects.end())
return it->second;
return {};
}
// Map effect index to GMST name
static constexpr std::array<std::string_view, MagicEffect::Length> sGmstEffectIds = {
"sEffectWaterBreathing",
"sEffectSwiftSwim",
"sEffectWaterWalking",
"sEffectShield",
"sEffectFireShield",
"sEffectLightningShield",
"sEffectFrostShield",
"sEffectBurden",
"sEffectFeather",
"sEffectJump",
"sEffectLevitate",
"sEffectSlowFall",
"sEffectLock",
"sEffectOpen",
"sEffectFireDamage",
"sEffectShockDamage",
"sEffectFrostDamage",
"sEffectDrainAttribute",
"sEffectDrainHealth",
"sEffectDrainSpellpoints",
"sEffectDrainFatigue",
"sEffectDrainSkill",
"sEffectDamageAttribute",
"sEffectDamageHealth",
"sEffectDamageMagicka",
"sEffectDamageFatigue",
"sEffectDamageSkill",
"sEffectPoison",
"sEffectWeaknesstoFire",
"sEffectWeaknesstoFrost",
"sEffectWeaknesstoShock",
"sEffectWeaknesstoMagicka",
"sEffectWeaknesstoCommonDisease",
"sEffectWeaknesstoBlightDisease",
"sEffectWeaknesstoCorprusDisease",
"sEffectWeaknesstoPoison",
"sEffectWeaknesstoNormalWeapons",
"sEffectDisintegrateWeapon",
"sEffectDisintegrateArmor",
"sEffectInvisibility",
"sEffectChameleon",
"sEffectLight",
"sEffectSanctuary",
"sEffectNightEye",
"sEffectCharm",
"sEffectParalyze",
"sEffectSilence",
"sEffectBlind",
"sEffectSound",
"sEffectCalmHumanoid",
"sEffectCalmCreature",
"sEffectFrenzyHumanoid",
"sEffectFrenzyCreature",
"sEffectDemoralizeHumanoid",
"sEffectDemoralizeCreature",
"sEffectRallyHumanoid",
"sEffectRallyCreature",
"sEffectDispel",
"sEffectSoultrap",
"sEffectTelekinesis",
"sEffectMark",
"sEffectRecall",
"sEffectDivineIntervention",
"sEffectAlmsiviIntervention",
"sEffectDetectAnimal",
"sEffectDetectEnchantment",
"sEffectDetectKey",
"sEffectSpellAbsorption",
"sEffectReflect",
"sEffectCureCommonDisease",
"sEffectCureBlightDisease",
"sEffectCureCorprusDisease",
"sEffectCurePoison",
"sEffectCureParalyzation",
"sEffectRestoreAttribute",
"sEffectRestoreHealth",
"sEffectRestoreSpellPoints",
"sEffectRestoreFatigue",
"sEffectRestoreSkill",
"sEffectFortifyAttribute",
"sEffectFortifyHealth",
"sEffectFortifySpellpoints",
"sEffectFortifyFatigue",
"sEffectFortifySkill",
"sEffectFortifyMagickaMultiplier",
"sEffectAbsorbAttribute",
"sEffectAbsorbHealth",
"sEffectAbsorbSpellPoints",
"sEffectAbsorbFatigue",
"sEffectAbsorbSkill",
"sEffectResistFire",
"sEffectResistFrost",
"sEffectResistShock",
"sEffectResistMagicka",
"sEffectResistCommonDisease",
"sEffectResistBlightDisease",
"sEffectResistCorprusDisease",
"sEffectResistPoison",
"sEffectResistNormalWeapons",
"sEffectResistParalysis",
"sEffectRemoveCurse",
"sEffectTurnUndead",
"sEffectSummonScamp",
"sEffectSummonClannfear",
"sEffectSummonDaedroth",
"sEffectSummonDremora",
"sEffectSummonAncestralGhost",
"sEffectSummonSkeletalMinion",
"sEffectSummonLeastBonewalker",
"sEffectSummonGreaterBonewalker",
"sEffectSummonBonelord",
"sEffectSummonWingedTwilight",
"sEffectSummonHunger",
"sEffectSummonGoldenSaint",
"sEffectSummonFlameAtronach",
"sEffectSummonFrostAtronach",
"sEffectSummonStormAtronach",
"sEffectFortifyAttackBonus",
"sEffectCommandCreatures",
"sEffectCommandHumanoids",
"sEffectBoundDagger",
"sEffectBoundLongsword",
"sEffectBoundMace",
"sEffectBoundBattleAxe",
"sEffectBoundSpear",
"sEffectBoundLongbow",
"sEffectExtraSpell",
"sEffectBoundCuirass",
"sEffectBoundHelm",
"sEffectBoundBoots",
"sEffectBoundShield",
"sEffectBoundGloves",
"sEffectCorpus", // NB this typo. (bethesda made it)
"sEffectVampirism",
"sEffectSummonCenturionSphere",
"sEffectSunDamage",
"sEffectStuntedMagicka",
// tribunal
"sEffectSummonFabricant",
// bloodmoon
"sEffectSummonCreature01",
"sEffectSummonCreature02",
"sEffectSummonCreature03",
"sEffectSummonCreature04",
"sEffectSummonCreature05",
};
static const std::array<RefId, MagicEffect::Length> sMagicEffectIds{
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
MagicEffect::SummonFabricant,
// bloodmoon
MagicEffect::SummonWolf,
MagicEffect::SummonBear,
MagicEffect::SummonBonewolf,
MagicEffect::SummonCreature04,
MagicEffect::SummonCreature05,
};
template <typename Collection, typename Comparator = std::less<typename Collection::value_type>>
static std::map<typename Collection::value_type, int, Comparator> initToIndexMap(
const Collection& data, Comparator comp = Comparator())
{
std::map<typename Collection::value_type, int, Comparator> map(comp);
for (size_t i = 0; i < data.size(); ++i)
map[data[i]] = static_cast<int>(i);
return map;
}
const std::map<std::string_view, int, Misc::StringUtils::CiComp> sGmstEffectIdToIndexMap
= initToIndexMap(sGmstEffectIds, Misc::StringUtils::CiComp());
const std::map<RefId, int> sMagicEffectIdToIndexMap = initToIndexMap(sMagicEffectIds);
MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const
{
int index = refIdToIndex(mId);
if (mData.mFlags & NoMagnitude)
return MDT_None;
if (index == 84)
return MDT_TimesInt;
if (index == 59 || (index >= 64 && index <= 66))
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)
return MDT_Percentage;
return MDT_Points;
}
void MagicEffect::blank()
{
mRecordFlags = 0;
mData.mSchool = ESM::Skill::Alteration;
mData.mBaseCost = 0;
mData.mFlags = 0;
mData.mRed = 0;
mData.mGreen = 0;
mData.mBlue = 0;
mData.mSpeed = 1;
mIcon.clear();
mParticle.clear();
mCasting = ESM::RefId();
mHit = ESM::RefId();
mArea = ESM::RefId();
mBolt = ESM::RefId();
mCastSound = ESM::RefId();
mBoltSound = ESM::RefId();
mHitSound = ESM::RefId();
mAreaSound = ESM::RefId();
mDescription.clear();
}
osg::Vec4f MagicEffect::getColor() const
{
osg::Vec4f color{ mData.mRed / 255.f, mData.mGreen / 255.f, mData.mBlue / 255.f, 1.f };
if (mData.mFlags & NegativeLight)
return osg::Vec4f(1.f, 1.f, 1.f, 2.f) - color;
return color;
}
std::string_view MagicEffect::refIdToGmstString(RefId effectId)
{
int index = refIdToIndex(effectId);
if (index < 0 || index >= Length)
return {};
return sGmstEffectIds[index];
}
RefId MagicEffect::effectGmstIdToRefId(std::string_view gmstId)
{
auto it = sGmstEffectIdToIndexMap.find(gmstId);
if (it == sGmstEffectIdToIndexMap.end())
return {};
return sMagicEffectIds[it->second];
}
RefId MagicEffect::indexToRefId(int index)
{
if (index < 0 || index >= Length)
return {};
return sMagicEffectIds[index];
}
int MagicEffect::refIdToIndex(RefId effectId)
{
const auto it = sMagicEffectIdToIndexMap.find(effectId);
if (it != sMagicEffectIdToIndexMap.end())
return it->second;
return -1;
}
std::string_view MagicEffect::indexToName(int index)
{
if (index < 0 || index >= Length)
return {};
return sMagicEffectIds[index].getRefIdString();
}
}