#include "loadmgef.hpp" #include #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" namespace { static const char *sIds[ESM::MagicEffect::Length] = { "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" }; 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 }; } namespace ESM { unsigned int MagicEffect::sRecordId = REC_MGEF; void MagicEffect::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future) esm.getHNT(mIndex, "INDX"); mId = indexToId (mIndex); esm.getHNT(mData, "MEDT", 36); if (esm.getFormat() == 0) { // don't allow mods to change fixed flags in the legacy format mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight); if (mIndex>=0 && mIndex::value: mIcon = esm.getHString(); break; case ESM::FourCC<'P','T','E','X'>::value: mParticle = esm.getHString(); break; case ESM::FourCC<'B','S','N','D'>::value: mBoltSound = esm.getHString(); break; case ESM::FourCC<'C','S','N','D'>::value: mCastSound = esm.getHString(); break; case ESM::FourCC<'H','S','N','D'>::value: mHitSound = esm.getHString(); break; case ESM::FourCC<'A','S','N','D'>::value: mAreaSound = esm.getHString(); break; case ESM::FourCC<'C','V','F','X'>::value: mCasting = esm.getHString(); break; case ESM::FourCC<'B','V','F','X'>::value: mBolt = esm.getHString(); break; case ESM::FourCC<'H','V','F','X'>::value: mHit = esm.getHString(); break; case ESM::FourCC<'A','V','F','X'>::value: mArea = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; default: esm.fail("Unknown subrecord"); } } } void MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("MEDT", mData, 36); esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("PTEX", mParticle); esm.writeHNOCString("BSND", mBoltSound); esm.writeHNOCString("CSND", mCastSound); esm.writeHNOCString("HSND", mHitSound); esm.writeHNOCString("ASND", mAreaSound); esm.writeHNOCString("CVFX", mCasting); esm.writeHNOCString("BVFX", mBolt); esm.writeHNOCString("HVFX", mHit); esm.writeHNOCString("AVFX", mArea); esm.writeHNOString("DESC", mDescription); } short MagicEffect::getResistanceEffect(short effect) { // Source https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attribute // std::map effects; effects[DisintegrateArmor] = Sanctuary; effects[DisintegrateWeapon] = Sanctuary; for (int i=0; i<5; ++i) effects[DrainAttribute+i] = ResistMagicka; for (int i=0; i<5; ++i) effects[DamageAttribute+i] = ResistMagicka; for (int i=0; i<5; ++i) effects[AbsorbAttribute+i] = ResistMagicka; for (int i=0; i<10; ++i) effects[WeaknessToFire+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) { std::map effects; for (int i=0; i<5; ++i) effects[DrainAttribute+i] = WeaknessToMagicka; for (int i=0; i<5; ++i) effects[DamageAttribute+i] = WeaknessToMagicka; for (int i=0; i<5; ++i) effects[AbsorbAttribute+i] = WeaknessToMagicka; for (int i=0; i<10; ++i) effects[WeaknessToFire+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; } static std::map genNameMap() { // Map effect ID to GMST name // http://www.uesp.net/morrow/hints/mweffects.shtml std::map names; names[85] ="sEffectAbsorbAttribute"; names[88] ="sEffectAbsorbFatigue"; names[86] ="sEffectAbsorbHealth"; names[87] ="sEffectAbsorbSpellPoints"; names[89] ="sEffectAbsorbSkill"; names[63] ="sEffectAlmsiviIntervention"; names[47] ="sEffectBlind"; names[123] ="sEffectBoundBattleAxe"; names[129] ="sEffectBoundBoots"; names[127] ="sEffectBoundCuirass"; names[120] ="sEffectBoundDagger"; names[131] ="sEffectBoundGloves"; names[128] ="sEffectBoundHelm"; names[125] ="sEffectBoundLongbow"; names[126] ="sEffectExtraSpell"; names[121] ="sEffectBoundLongsword"; names[122] ="sEffectBoundMace"; names[130] ="sEffectBoundShield"; names[124] ="sEffectBoundSpear"; names[7] ="sEffectBurden"; names[50] ="sEffectCalmCreature"; names[49] ="sEffectCalmHumanoid"; names[40] ="sEffectChameleon"; names[44] ="sEffectCharm"; names[118] ="sEffectCommandCreatures"; names[119] ="sEffectCommandHumanoids"; names[132] ="sEffectCorpus"; // NB this typo. (bethesda made it) names[70] ="sEffectCureBlightDisease"; names[69] ="sEffectCureCommonDisease"; names[71] ="sEffectCureCorprusDisease"; names[73] ="sEffectCureParalyzation"; names[72] ="sEffectCurePoison"; names[22] ="sEffectDamageAttribute"; names[25] ="sEffectDamageFatigue"; names[23] ="sEffectDamageHealth"; names[24] ="sEffectDamageMagicka"; names[26] ="sEffectDamageSkill"; names[54] ="sEffectDemoralizeCreature"; names[53] ="sEffectDemoralizeHumanoid"; names[64] ="sEffectDetectAnimal"; names[65] ="sEffectDetectEnchantment"; names[66] ="sEffectDetectKey"; names[38] ="sEffectDisintegrateArmor"; names[37] ="sEffectDisintegrateWeapon"; names[57] ="sEffectDispel"; names[62] ="sEffectDivineIntervention"; names[17] ="sEffectDrainAttribute"; names[20] ="sEffectDrainFatigue"; names[18] ="sEffectDrainHealth"; names[19] ="sEffectDrainSpellpoints"; names[21] ="sEffectDrainSkill"; names[8] ="sEffectFeather"; names[14] ="sEffectFireDamage"; names[4] ="sEffectFireShield"; names[117] ="sEffectFortifyAttackBonus"; names[79] ="sEffectFortifyAttribute"; names[82] ="sEffectFortifyFatigue"; names[80] ="sEffectFortifyHealth"; names[81] ="sEffectFortifySpellpoints"; names[84] ="sEffectFortifyMagickaMultiplier"; names[83] ="sEffectFortifySkill"; names[52] ="sEffectFrenzyCreature"; names[51] ="sEffectFrenzyHumanoid"; names[16] ="sEffectFrostDamage"; names[6] ="sEffectFrostShield"; names[39] ="sEffectInvisibility"; names[9] ="sEffectJump"; names[10] ="sEffectLevitate"; names[41] ="sEffectLight"; names[5] ="sEffectLightningShield"; names[12] ="sEffectLock"; names[60] ="sEffectMark"; names[43] ="sEffectNightEye"; names[13] ="sEffectOpen"; names[45] ="sEffectParalyze"; names[27] ="sEffectPoison"; names[56] ="sEffectRallyCreature"; names[55] ="sEffectRallyHumanoid"; names[61] ="sEffectRecall"; names[68] ="sEffectReflect"; names[100] ="sEffectRemoveCurse"; names[95] ="sEffectResistBlightDisease"; names[94] ="sEffectResistCommonDisease"; names[96] ="sEffectResistCorprusDisease"; names[90] ="sEffectResistFire"; names[91] ="sEffectResistFrost"; names[93] ="sEffectResistMagicka"; names[98] ="sEffectResistNormalWeapons"; names[99] ="sEffectResistParalysis"; names[97] ="sEffectResistPoison"; names[92] ="sEffectResistShock"; names[74] ="sEffectRestoreAttribute"; names[77] ="sEffectRestoreFatigue"; names[75] ="sEffectRestoreHealth"; names[76] ="sEffectRestoreSpellPoints"; names[78] ="sEffectRestoreSkill"; names[42] ="sEffectSanctuary"; names[3] ="sEffectShield"; names[15] ="sEffectShockDamage"; names[46] ="sEffectSilence"; names[11] ="sEffectSlowFall"; names[58] ="sEffectSoultrap"; names[48] ="sEffectSound"; names[67] ="sEffectSpellAbsorption"; names[136] ="sEffectStuntedMagicka"; names[106] ="sEffectSummonAncestralGhost"; names[110] ="sEffectSummonBonelord"; names[108] ="sEffectSummonLeastBonewalker"; names[134] ="sEffectSummonCenturionSphere"; names[103] ="sEffectSummonClannfear"; names[104] ="sEffectSummonDaedroth"; names[105] ="sEffectSummonDremora"; names[114] ="sEffectSummonFlameAtronach"; names[115] ="sEffectSummonFrostAtronach"; names[113] ="sEffectSummonGoldenSaint"; names[109] ="sEffectSummonGreaterBonewalker"; names[112] ="sEffectSummonHunger"; names[102] ="sEffectSummonScamp"; names[107] ="sEffectSummonSkeletalMinion"; names[116] ="sEffectSummonStormAtronach"; names[111] ="sEffectSummonWingedTwilight"; names[135] ="sEffectSunDamage"; names[1] ="sEffectSwiftSwim"; names[59] ="sEffectTelekinesis"; names[101] ="sEffectTurnUndead"; names[133] ="sEffectVampirism"; names[0] ="sEffectWaterBreathing"; names[2] ="sEffectWaterWalking"; names[33] ="sEffectWeaknesstoBlightDisease"; names[32] ="sEffectWeaknesstoCommonDisease"; names[34] ="sEffectWeaknesstoCorprusDisease"; names[28] ="sEffectWeaknesstoFire"; names[29] ="sEffectWeaknesstoFrost"; names[31] ="sEffectWeaknesstoMagicka"; names[36] ="sEffectWeaknesstoNormalWeapons"; names[35] ="sEffectWeaknesstoPoison"; names[30] ="sEffectWeaknesstoShock"; // bloodmoon names[138] ="sEffectSummonCreature01"; names[139] ="sEffectSummonCreature02"; names[140] ="sEffectSummonCreature03"; names[141] ="sEffectSummonCreature04"; names[142] ="sEffectSummonCreature05"; // tribunal names[137] ="sEffectSummonFabricant"; return names; } const std::map MagicEffect::sNames = genNameMap(); const std::string &MagicEffect::effectIdToString(short effectID) { std::map::const_iterator name = sNames.find(effectID); if(name == sNames.end()) throw std::runtime_error(std::string("Unimplemented effect ID ")+std::to_string(effectID)); return name->second; } class FindSecond { const std::string &mName; public: FindSecond(const std::string &name) : mName(name) { } bool operator()(const std::pair &item) const { if(Misc::StringUtils::ciEqual(item.second, mName)) return true; return false; } }; short MagicEffect::effectStringToId(const std::string &effect) { std::map::const_iterator name; name = std::find_if(sNames.begin(), sNames.end(), FindSecond(effect)); if(name == sNames.end()) throw std::runtime_error(std::string("Unimplemented effect ")+effect); return name->first; } 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() { mData.mSchool = 0; mData.mBaseCost = 0; mData.mFlags = 0; mData.mRed = 0; mData.mGreen = 0; mData.mBlue = 0; mData.mSpeed = 0; mIcon.clear(); mParticle.clear(); mCasting.clear(); mHit.clear(); mArea.clear(); mBolt.clear(); mCastSound.clear(); mBoltSound.clear(); mHitSound.clear(); mAreaSound.clear(); mDescription.clear(); } std::string MagicEffect::indexToId (int index) { std::ostringstream stream; if (index!=-1) { stream << "#"; if (index<100) { stream << "0"; if (index<10) stream << "0"; } stream << index; if (index>=0 && index