mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 06:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			654 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			654 lines
		
	
	
	
		
			21 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 };
 | 
						|
    }
 | 
						|
 | 
						|
    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");
 | 
						|
 | 
						|
        mId = indexToRefId(mIndex);
 | 
						|
 | 
						|
        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 (mIndex >= 0 && mIndex < NumberOfHardcodedFlags)
 | 
						|
                mData.mFlags |= HardcodedFlags[mIndex];
 | 
						|
        }
 | 
						|
 | 
						|
        // 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", mIndex);
 | 
						|
 | 
						|
        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);
 | 
						|
    }
 | 
						|
 | 
						|
    short MagicEffect::getResistanceEffect(short effect)
 | 
						|
    {
 | 
						|
        // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute
 | 
						|
 | 
						|
        // <Effect, Effect providing resistance against first effect>
 | 
						|
        static std::map<short, short> effects;
 | 
						|
        if (effects.empty())
 | 
						|
        {
 | 
						|
            effects[DisintegrateArmor] = Sanctuary;
 | 
						|
            effects[DisintegrateWeapon] = Sanctuary;
 | 
						|
 | 
						|
            for (int i = DrainAttribute; i <= DamageSkill; ++i)
 | 
						|
                effects[i] = ResistMagicka;
 | 
						|
            for (int i = AbsorbAttribute; i <= AbsorbSkill; ++i)
 | 
						|
                effects[i] = ResistMagicka;
 | 
						|
            for (int i = WeaknessToFire; i <= WeaknessToNormalWeapons; ++i)
 | 
						|
                effects[i] = ResistMagicka;
 | 
						|
 | 
						|
            effects[Burden] = ResistMagicka;
 | 
						|
            effects[Charm] = ResistMagicka;
 | 
						|
            effects[Silence] = ResistMagicka;
 | 
						|
            effects[Blind] = ResistMagicka;
 | 
						|
            effects[Sound] = ResistMagicka;
 | 
						|
 | 
						|
            for (int i = 0; i < 2; ++i)
 | 
						|
            {
 | 
						|
                effects[CalmHumanoid + i] = ResistMagicka;
 | 
						|
                effects[FrenzyHumanoid + i] = ResistMagicka;
 | 
						|
                effects[DemoralizeHumanoid + i] = ResistMagicka;
 | 
						|
                effects[RallyHumanoid + i] = ResistMagicka;
 | 
						|
            }
 | 
						|
 | 
						|
            effects[TurnUndead] = ResistMagicka;
 | 
						|
 | 
						|
            effects[FireDamage] = ResistFire;
 | 
						|
            effects[FrostDamage] = ResistFrost;
 | 
						|
            effects[ShockDamage] = ResistShock;
 | 
						|
            effects[Vampirism] = ResistCommonDisease;
 | 
						|
            effects[Corprus] = ResistCorprusDisease;
 | 
						|
            effects[Poison] = ResistPoison;
 | 
						|
            effects[Paralyze] = ResistParalysis;
 | 
						|
        }
 | 
						|
 | 
						|
        if (effects.find(effect) != effects.end())
 | 
						|
            return effects[effect];
 | 
						|
        else
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    short MagicEffect::getWeaknessEffect(short effect)
 | 
						|
    {
 | 
						|
        static std::map<short, short> effects;
 | 
						|
        if (effects.empty())
 | 
						|
        {
 | 
						|
            for (int i = DrainAttribute; i <= DamageSkill; ++i)
 | 
						|
                effects[i] = WeaknessToMagicka;
 | 
						|
            for (int i = AbsorbAttribute; i <= AbsorbSkill; ++i)
 | 
						|
                effects[i] = WeaknessToMagicka;
 | 
						|
            for (int i = WeaknessToFire; i <= WeaknessToNormalWeapons; ++i)
 | 
						|
                effects[i] = WeaknessToMagicka;
 | 
						|
 | 
						|
            effects[Burden] = WeaknessToMagicka;
 | 
						|
            effects[Charm] = WeaknessToMagicka;
 | 
						|
            effects[Silence] = WeaknessToMagicka;
 | 
						|
            effects[Blind] = WeaknessToMagicka;
 | 
						|
            effects[Sound] = WeaknessToMagicka;
 | 
						|
 | 
						|
            for (int i = 0; i < 2; ++i)
 | 
						|
            {
 | 
						|
                effects[CalmHumanoid + i] = WeaknessToMagicka;
 | 
						|
                effects[FrenzyHumanoid + i] = WeaknessToMagicka;
 | 
						|
                effects[DemoralizeHumanoid + i] = WeaknessToMagicka;
 | 
						|
                effects[RallyHumanoid + i] = WeaknessToMagicka;
 | 
						|
            }
 | 
						|
 | 
						|
            effects[TurnUndead] = WeaknessToMagicka;
 | 
						|
 | 
						|
            effects[FireDamage] = WeaknessToFire;
 | 
						|
            effects[FrostDamage] = WeaknessToFrost;
 | 
						|
            effects[ShockDamage] = WeaknessToShock;
 | 
						|
            effects[Vampirism] = WeaknessToCommonDisease;
 | 
						|
            effects[Corprus] = WeaknessToCorprusDisease;
 | 
						|
            effects[Poison] = WeaknessToPoison;
 | 
						|
 | 
						|
            effects[Paralyze] = -1;
 | 
						|
        }
 | 
						|
 | 
						|
        if (effects.find(effect) != effects.end())
 | 
						|
            return effects[effect];
 | 
						|
        else
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    // Map effect ID to GMST name
 | 
						|
    const std::array<std::string, MagicEffect::Length> MagicEffect::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",
 | 
						|
    };
 | 
						|
 | 
						|
    // Map effect ID to identifying name
 | 
						|
    const std::array<std::string_view, MagicEffect::Length> 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",
 | 
						|
 | 
						|
        // tribunal
 | 
						|
        "SummonFabricant",
 | 
						|
 | 
						|
        // bloodmoon
 | 
						|
        "SummonWolf",
 | 
						|
        "SummonBear",
 | 
						|
        "SummonBonewolf",
 | 
						|
        "SummonCreature04",
 | 
						|
        "SummonCreature05",
 | 
						|
    };
 | 
						|
 | 
						|
    template <typename Collection>
 | 
						|
    static std::map<std::string_view, int, Misc::StringUtils::CiComp> initStringToIntMap(const Collection& strings)
 | 
						|
    {
 | 
						|
        std::map<std::string_view, int, Misc::StringUtils::CiComp> map;
 | 
						|
        for (size_t i = 0; i < strings.size(); i++)
 | 
						|
            map[strings[i]] = i;
 | 
						|
 | 
						|
        return map;
 | 
						|
    }
 | 
						|
 | 
						|
    const std::map<std::string_view, int, Misc::StringUtils::CiComp> MagicEffect::sGmstEffectIdToIndexMap
 | 
						|
        = initStringToIntMap(MagicEffect::sGmstEffectIds);
 | 
						|
 | 
						|
    const std::map<std::string_view, int, Misc::StringUtils::CiComp> MagicEffect::sIndexNameToIndexMap
 | 
						|
        = initStringToIntMap(MagicEffect::sIndexNames);
 | 
						|
 | 
						|
    class FindSecond
 | 
						|
    {
 | 
						|
        std::string_view mName;
 | 
						|
 | 
						|
    public:
 | 
						|
        FindSecond(std::string_view name)
 | 
						|
            : mName(name)
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        bool operator()(const std::pair<short, std::string>& item) const
 | 
						|
        {
 | 
						|
            if (Misc::StringUtils::ciEqual(item.second, mName))
 | 
						|
                return true;
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const
 | 
						|
    {
 | 
						|
        if (mData.mFlags & NoMagnitude)
 | 
						|
            return MDT_None;
 | 
						|
        if (mIndex == 84)
 | 
						|
            return MDT_TimesInt;
 | 
						|
        if (mIndex == 59 || (mIndex >= 64 && mIndex <= 66))
 | 
						|
            return MDT_Feet;
 | 
						|
        if (mIndex == 118 || mIndex == 119)
 | 
						|
            return MDT_Level;
 | 
						|
        if ((mIndex >= 28 && mIndex <= 36) || (mIndex >= 90 && mIndex <= 99) || mIndex == 40 || mIndex == 47
 | 
						|
            || mIndex == 57 || mIndex == 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 = 0;
 | 
						|
 | 
						|
        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;
 | 
						|
    }
 | 
						|
 | 
						|
    const std::string& MagicEffect::indexToGmstString(int effectID)
 | 
						|
    {
 | 
						|
        if (effectID < 0 || static_cast<std::size_t>(effectID) >= sGmstEffectIds.size())
 | 
						|
            throw std::runtime_error(std::string("Unimplemented effect ID ") + std::to_string(effectID));
 | 
						|
 | 
						|
        return sGmstEffectIds[effectID];
 | 
						|
    }
 | 
						|
 | 
						|
    std::string_view MagicEffect::indexToName(int effectID)
 | 
						|
    {
 | 
						|
        if (effectID < 0 || static_cast<std::size_t>(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())
 | 
						|
            throw std::runtime_error("Unimplemented effect " + std::string(effect));
 | 
						|
 | 
						|
        return name->second;
 | 
						|
    }
 | 
						|
 | 
						|
    int MagicEffect::effectGmstIdToIndex(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;
 | 
						|
    }
 | 
						|
 | 
						|
    RefId MagicEffect::indexToRefId(int index)
 | 
						|
    {
 | 
						|
        if (index == -1)
 | 
						|
            return RefId();
 | 
						|
        return RefId::index(sRecordId, static_cast<std::uint32_t>(index));
 | 
						|
    }
 | 
						|
}
 |