From bdfea4ecad19c951a9d47636e7357e6da555d485 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 16 Aug 2019 23:15:19 -0400 Subject: [PATCH] Implemented binary resistances and some more Port From (https://github.com/TrinityCore/TrinityCore/commit/ca26c33145cb40ae7fd2c84fc7577fc6f11bdbbf) --- .../Framework/Constants/Spells/SpellConst.cs | 6 +- Source/Game/Entities/Creature/Creature.cs | 11 +- Source/Game/Entities/Unit/Unit.Combat.cs | 115 +++++++++----- Source/Game/Entities/Unit/Unit.Spells.cs | 85 ++++++++-- Source/Game/Spells/Auras/AuraEffect.cs | 4 +- Source/Game/Spells/SpellEffects.cs | 49 +----- Source/Game/Spells/SpellInfo.cs | 8 +- Source/Game/Spells/SpellManager.cs | 149 ++++++++++++++++++ 8 files changed, 322 insertions(+), 105 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index 5ae577955..7638fff96 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -1809,7 +1809,7 @@ namespace Framework.Constants ConeLine = 0x04, ShareDamage = 0x08, NoInitialThreat = 0x10, - IsTalent = 0x20, + AuraCC = 0x20, DontBreakStealth = 0x40, DirectDamage = 0x100, Charge = 0x200, @@ -1822,6 +1822,10 @@ namespace Framework.Constants ReqCasterBehindTarget = 0x20000, AllowInflightTarget = 0x40000, NeedsAmmoData = 0x80000, + BinarySpell = 0x00100000, + SchoolmaskNormalWithMagic = 0x00200000, + LiquidAura = 0x00400000, + IsTalent = 0x00800000, Negative = NegativeEff0 | NegativeEff1 | NegativeEff2 } diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index e9709c096..a41d8ae00 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1712,13 +1712,18 @@ namespace Game.Entities ForcedDespawn(msTimeToDespawn, forceRespawnTimer); } + bool HasMechanicTemplateImmunity(uint mask) + { + return !GetOwnerGUID().IsPlayer() && GetCreatureTemplate().MechanicImmuneMask.HasAnyFlag(mask); + } + public override bool IsImmunedToSpell(SpellInfo spellInfo, Unit caster) { if (spellInfo == null) return false; // Creature is immune to main mechanic of the spell - if (Convert.ToBoolean(GetCreatureTemplate().MechanicImmuneMask & (1 << ((int)spellInfo.Mechanic - 1)))) + if (spellInfo.Mechanic > Mechanics.None && HasMechanicTemplateImmunity(1u << ((int)spellInfo.Mechanic - 1))) return true; // This check must be done instead of 'if (GetCreatureTemplate().MechanicImmuneMask & (1 << (spellInfo.Mechanic - 1)))' for not break @@ -1735,6 +1740,7 @@ namespace Game.Entities break; } } + if (immunedToAllEffects) return true; @@ -1746,7 +1752,8 @@ namespace Game.Entities SpellEffectInfo effect = spellInfo.GetEffect(GetMap().GetDifficultyID(), index); if (effect == null) return true; - if (Convert.ToBoolean(GetCreatureTemplate().MechanicImmuneMask & (1 << ((int)effect.Mechanic - 1)))) + + if (effect.Mechanic > Mechanics.None && HasMechanicTemplateImmunity(1u << ((int)effect.Mechanic - 1))) return true; if (GetCreatureTemplate().CreatureType == CreatureType.Mechanical && effect.Effect == SpellEffectName.Heal) diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 2b8faf493..75c456847 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -2290,47 +2290,28 @@ namespace Game.Entities return true; } - uint CalcSpellResistance(Unit victim, SpellSchoolMask schoolMask, SpellInfo spellInfo) + uint CalcSpellResistedDamage(Unit attacker, Unit victim, uint damage, SpellSchoolMask schoolMask, SpellInfo spellInfo) { // Magic damage, check for resists - if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Spell)) + if (!Convert.ToBoolean(schoolMask & SpellSchoolMask.Magic)) + return 0; + + // Npcs can have holy resistance + if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy) && victim.GetTypeId() != TypeId.Unit) return 0; // Ignore spells that can't be resisted - if (spellInfo != null && spellInfo.HasAttribute(SpellAttr4.IgnoreResistances)) - return 0; - - byte bossLevel = 83; - uint bossResistanceConstant = 510; - uint resistanceConstant = 0; - uint level = victim.GetLevelForTarget(this); - - if (level == bossLevel) - resistanceConstant = bossResistanceConstant; - else - resistanceConstant = level * 5; - - int baseVictimResistance = (int)victim.GetResistance(schoolMask); - baseVictimResistance += GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); - - Player player = ToPlayer(); - if (player) - baseVictimResistance -= player.GetSpellPenetrationItemMod(); - - // Resistance can't be lower then 0 - int victimResistance = Math.Max(baseVictimResistance, 0); - - if (victimResistance > 0) + if (spellInfo != null) { - int ignoredResistance = GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask); - ignoredResistance = Math.Min(ignoredResistance, 100); - MathFunctions.ApplyPct(ref victimResistance, 100 - ignoredResistance); + if (spellInfo.HasAttribute(SpellAttr4.IgnoreResistances)) + return 0; + + // Binary spells can't have damage part resisted + if (spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)) + return 0; } - if (victimResistance <= 0) - return 0; - - float averageResist = (float)victimResistance / (float)(victimResistance + resistanceConstant); + float averageResist = GetEffectiveResistChance(this, schoolMask, victim, spellInfo); float[] discreteResistProbability = new float[11]; for (uint i = 0; i < 11; ++i) @@ -2354,7 +2335,69 @@ namespace Game.Entities while (r >= probabilitySum && resistance < 10) probabilitySum += discreteResistProbability[++resistance]; - return resistance * 10; + float damageResisted = damage * resistance / 10; + if (damageResisted > 0.0f) // if any damage was resisted + { + int ignoredResistance = 0; + + ignoredResistance += GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)schoolMask); + + ignoredResistance = Math.Min(ignoredResistance, 100); + MathFunctions.ApplyPct(ref damageResisted, 100 - ignoredResistance); + + // Spells with melee and magic school mask, decide whether resistance or armor absorb is higher + if (spellInfo != null && spellInfo.HasAttribute(SpellCustomAttributes.SchoolmaskNormalWithMagic)) + { + uint damageAfterArmor = CalcArmorReducedDamage(attacker, victim, damage, spellInfo, WeaponAttackType.BaseAttack); + uint armorReduction = damage - damageAfterArmor; + if (armorReduction < damageResisted) // pick the lower one, the weakest resistance counts + damageResisted = armorReduction; + } + } + + return (uint)damageResisted; + } + + float GetEffectiveResistChance(Unit owner, SpellSchoolMask schoolMask, Unit victim, SpellInfo spellInfo) + { + float victimResistance = (float)victim.GetResistance(schoolMask); + if (owner) + { + // pets inherit 100% of masters penetration + // excluding traps + Player player = owner.GetSpellModOwner(); + if (player != null && owner.GetEntry() != SharedConst.WorldTrigger) + { + victimResistance += (float)player.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); + victimResistance -= (float)player.GetSpellPenetrationItemMod(); + } + else + victimResistance += (float)owner.GetTotalAuraModifierByMiscMask(AuraType.ModTargetResistance, (int)schoolMask); + } + + // holy resistance exists in pve and comes from level difference, ignore template values + if (schoolMask.HasAnyFlag(SpellSchoolMask.Holy)) + victimResistance = 0.0f; + + // Chaos Bolt exception, ignore all target resistances (unknown attribute?) + if (spellInfo != null && spellInfo.SpellFamilyName == SpellFamilyNames.Warlock && spellInfo.Id == 116858) + victimResistance = 0.0f; + + victimResistance = Math.Max(victimResistance, 0.0f); + if (owner) + victimResistance += Math.Max((victim.GetLevelForTarget(owner) - owner.GetLevelForTarget(victim)) * 5.0f, 0.0f); + + uint bossLevel = 83; + float bossResistanceConstant = 510.0f; + uint level = victim.GetLevelForTarget(this); + float resistanceConstant = 0.0f; + + if (level == bossLevel) + resistanceConstant = bossResistanceConstant; + else + resistanceConstant = level * 5.0f; + + return victimResistance / (victimResistance + resistanceConstant); } public void CalcAbsorbResist(DamageInfo damageInfo) @@ -2362,8 +2405,8 @@ namespace Game.Entities if (!damageInfo.GetVictim() || !damageInfo.GetVictim().IsAlive() || damageInfo.GetDamage() == 0) return; - uint spellResistance = CalcSpellResistance(damageInfo.GetVictim(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo()); - damageInfo.ResistDamage(MathFunctions.CalculatePct(damageInfo.GetDamage(), spellResistance)); + uint resistedDamage = CalcSpellResistedDamage(damageInfo.GetAttacker(), damageInfo.GetVictim(), damageInfo.GetDamage(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo()); + damageInfo.ResistDamage(resistedDamage); // Ignore Absorption Auras float auraAbsorbMod = GetMaxPositiveAuraModifierByMiscMask(AuraType.ModTargetAbsorbSchool, (uint)damageInfo.GetSchoolMask()); diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 95fda40e3..3d2d3d7c3 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -1566,8 +1566,8 @@ namespace Game.Entities if (mechanic != 0) { var mechanicList = m_spellImmune[(int)SpellImmunity.Mechanic]; - if (mechanicList.ContainsKey(mechanic)) - return true; + if (mechanicList.ContainsKey(mechanic)) + return true; } bool immuneToAllEffects = true; @@ -1588,6 +1588,7 @@ namespace Game.Entities if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects. return true; + uint schoolImmunityMask = 0; var schoolList = m_spellImmune[(int)SpellImmunity.School]; foreach (var pair in schoolList) { @@ -1597,16 +1598,28 @@ namespace Game.Entities SpellInfo immuneSpellInfo = Global.SpellMgr.GetSpellInfo(pair.Value); if (!(immuneSpellInfo != null && immuneSpellInfo.IsPositive() && spellInfo.IsPositive() && caster && IsFriendlyTo(caster))) if (!spellInfo.CanPierceImmuneAura(immuneSpellInfo)) - return true; + schoolImmunityMask |= pair.Key; } + if (((SpellSchoolMask)schoolImmunityMask & spellInfo.GetSchoolMask()) == spellInfo.GetSchoolMask()) + return true; + return false; } public uint GetSchoolImmunityMask() { uint mask = 0; - var mechanicList = m_spellImmune[(int)SpellImmunity.School]; - foreach (var pair in mechanicList) + var schoolList = m_spellImmune[(int)SpellImmunity.School]; + foreach (var pair in schoolList) + mask |= pair.Key; + + return mask; + } + public uint GetDamageImmunityMask() + { + uint mask = 0; + var damageList = m_spellImmune[(int)SpellImmunity.Damage]; + foreach (var pair in damageList) mask |= pair.Key; return mask; @@ -1669,16 +1682,14 @@ namespace Game.Entities public bool IsImmunedToDamage(SpellSchoolMask schoolMask) { // If m_immuneToSchool type contain this school type, IMMUNE damage. - var schoolList = m_spellImmune[(int)SpellImmunity.School]; - foreach (var immune in schoolList) - if (Convert.ToBoolean(immune.Key & (uint)schoolMask)) - return true; + uint schoolImmunityMask = GetSchoolImmunityMask(); + if (((SpellSchoolMask)schoolImmunityMask & schoolMask) == schoolMask) // We need to be immune to all types + return true; // If m_immuneToDamage type contain magic, IMMUNE damage. - var damageList = m_spellImmune[(int)SpellImmunity.Damage]; - foreach (var immune in damageList) - if (Convert.ToBoolean(immune.Key & (uint)schoolMask)) - return true; + uint damageImmunityMask = GetDamageImmunityMask(); + if (((SpellSchoolMask)damageImmunityMask & schoolMask) == schoolMask) // We need to be immune to all types + return true; return false; } @@ -2330,8 +2341,12 @@ namespace Game.Entities // Physical Damage if (damageSchoolMask.HasAnyFlag(SpellSchoolMask.Normal)) { - // Get blocked status - blocked = isSpellBlocked(victim, spellInfo, attackType); + // Spells with this attribute were already calculated in MeleeSpellHitResult + if (!spellInfo.HasAttribute(SpellAttr3.BlockableSpell)) + { + // Get blocked status + blocked = isSpellBlocked(victim, spellInfo, attackType); + } } if (crit) @@ -2567,9 +2582,31 @@ namespace Game.Entities RemoveOwnedAura(pair, AuraRemoveMode.Death); } } - public void RemoveMovementImpairingAuras() + public void RemoveMovementImpairingAuras(bool withRoot) { - RemoveAurasWithMechanic((1 << (int)Mechanics.Snare) | (1 << (int)Mechanics.Root)); + if (withRoot) + RemoveAurasWithMechanic(1 << (int)Mechanics.Root); + + // Snares + foreach (var pair in m_appliedAuras) + { + Aura aura = pair.Value.GetBase(); + if (aura.GetSpellInfo().Mechanic == Mechanics.Snare) + { + RemoveAura(pair); + continue; + } + + // turn off snare auras by setting amount to 0 + foreach (SpellEffectInfo effect in aura.GetSpellInfo().GetEffectsForDifficulty(GetMap().GetDifficultyID())) + { + if (effect == null || !effect.IsEffect()) + continue; + + if (Convert.ToBoolean((1 << (int)effect.EffectIndex) & pair.Value.GetEffectMask())) + aura.GetEffect(effect.EffectIndex).ChangeAmount(0); + } + } } public void RemoveAllAurasRequiringDeadTarget() { @@ -3614,6 +3651,20 @@ namespace Game.Entities } } + public void RemoveAurasByShapeShift() + { + uint mechanic_mask = (1 << (int)Mechanics.Snare) | (1 << (int)Mechanics.Root); + foreach (var pair in m_appliedAuras) + { + Aura aura = pair.Value.GetBase(); + if ((aura.GetSpellInfo().GetAllEffectsMechanicMask() & mechanic_mask) != 0 && !aura.GetSpellInfo().HasAttribute(SpellCustomAttributes.AuraCC)) + { + RemoveAura(pair); + continue; + } + } + } + void RemoveAreaAurasDueToLeaveWorld() { // make sure that all area auras not applied on self are removed diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 2f3a1fb2c..6c677e06d 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -1310,7 +1310,7 @@ namespace Game.Spells case ShapeShiftForm.MoonkinForm: { // remove movement affects - target.RemoveMovementImpairingAuras(); + target.RemoveAurasByShapeShift(); // and polymorphic affects if (target.IsPolymorphed()) @@ -1351,7 +1351,7 @@ namespace Game.Spells if (target.GetClass() == Class.Druid) { // Remove movement impairing effects also when shifting out - target.RemoveMovementImpairingAuras(); + target.RemoveAurasByShapeShift(); } } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 1340d9a70..0ec452057 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -346,14 +346,14 @@ namespace Game.Spells // Vanish (not exist) case 18461: { - unitTarget.RemoveMovementImpairingAuras(); + unitTarget.RemoveMovementImpairingAuras(true); unitTarget.RemoveAurasByType(AuraType.ModStalked); return; } // Demonic Empowerment -- succubus case 54437: { - unitTarget.RemoveMovementImpairingAuras(); + unitTarget.RemoveMovementImpairingAuras(true); unitTarget.RemoveAurasByType(AuraType.ModStalked); unitTarget.RemoveAurasByType(AuraType.ModStun); @@ -1300,49 +1300,6 @@ namespace Game.Spells } m_caster.EnergizeBySpell(unitTarget, m_spellInfo.Id, damage, power); - - // Mad Alchemist's Potion - if (m_spellInfo.Id == 45051) - { - // find elixirs on target - bool guardianFound = false; - bool battleFound = false; - foreach (var app in unitTarget.GetAppliedAuras()) - { - uint spell_id = app.Value.GetBase().GetId(); - if (!guardianFound) - if (Global.SpellMgr.IsSpellMemberOfSpellGroup(spell_id, SpellGroup.ElixirGuardian)) - guardianFound = true; - if (!battleFound) - if (Global.SpellMgr.IsSpellMemberOfSpellGroup(spell_id, SpellGroup.ElixirBattle)) - battleFound = true; - if (battleFound && guardianFound) - break; - } - - // get all available elixirs by mask and spell level - List avalibleElixirs = new List(); - if (!guardianFound) - Global.SpellMgr.GetSetOfSpellsInSpellGroup(SpellGroup.ElixirGuardian, out avalibleElixirs); - if (!battleFound) - Global.SpellMgr.GetSetOfSpellsInSpellGroup(SpellGroup.ElixirBattle, out avalibleElixirs); - foreach (int spellId in avalibleElixirs) - { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo((uint)spellId); - if (spellInfo.SpellLevel < m_spellInfo.SpellLevel || spellInfo.SpellLevel > unitTarget.getLevel()) - avalibleElixirs.Remove(spellId); - else if (Global.SpellMgr.IsSpellMemberOfSpellGroup((uint)spellId, SpellGroup.ElixirShattrath)) - avalibleElixirs.Remove(spellId); - else if (Global.SpellMgr.IsSpellMemberOfSpellGroup((uint)spellId, SpellGroup.ElixirUnstable)) - avalibleElixirs.Remove(spellId); - } - - if (!avalibleElixirs.Empty()) - { - // cast random elixir on target - m_caster.CastSpell(unitTarget, (uint)avalibleElixirs.SelectRandom(), true, m_CastItem); - } - } } [SpellEffectHandler(SpellEffectName.EnergizePct)] @@ -2988,7 +2945,7 @@ namespace Game.Spells case 30918: // Improved Sprint { // Removes snares and roots. - unitTarget.RemoveMovementImpairingAuras(); + unitTarget.RemoveMovementImpairingAuras(true); break; } // Plant Warmaul Ogre Banner diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index 8fb30bbdc..48b418ec2 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -2433,7 +2433,13 @@ namespace Game.Spells target.ApplySpellImmune(Id, SpellImmunity.Mechanic, i, apply); if (apply && HasAttribute(SpellAttr1.DispelAurasOnImmunity)) - target.RemoveAurasWithMechanic(mechanicImmunity, AuraRemoveMode.Default, Id); + { + // exception for purely snare mechanic (eg. hands of freedom)! + if (mechanicImmunity == (1 << (int)Mechanics.Snare)) + target.RemoveMovementImpairingAuras(false); + else + target.RemoveAurasWithMechanic(mechanicImmunity, AuraRemoveMode.Default, Id); + } } uint dispelImmunity = immuneInfo.DispelImmune; diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index 0532e1f7c..2ef5599f1 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -2155,6 +2155,14 @@ namespace Game.Entities switch (effect.ApplyAuraName) { + case AuraType.ModPossess: + case AuraType.ModConfuse: + case AuraType.ModCharm: + case AuraType.AoeCharm: + case AuraType.ModFear: + case AuraType.ModStun: + spellInfo.AttributesCu |= SpellCustomAttributes.AuraCC; + break; case AuraType.PeriodicHeal: case AuraType.PeriodicDamage: case AuraType.PeriodicDamagePercent: @@ -2232,6 +2240,89 @@ namespace Game.Entities } } + // spells ignoring hit result should not be binary + if (!spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) + { + bool setFlag = false; + foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None)) + { + if (effect == null) + continue; + + if (effect.IsEffect()) + { + switch (effect.Effect) + { + case SpellEffectName.SchoolDamage: + case SpellEffectName.WeaponDamage: + case SpellEffectName.WeaponDamageNoschool: + case SpellEffectName.NormalizedWeaponDmg: + case SpellEffectName.WeaponPercentDamage: + case SpellEffectName.TriggerSpell: + case SpellEffectName.TriggerSpellWithValue: + break; + case SpellEffectName.PersistentAreaAura: + case SpellEffectName.ApplyAura: + case SpellEffectName.ApplyAreaAuraParty: + case SpellEffectName.ApplyAreaAuraRaid: + case SpellEffectName.ApplyAreaAuraFriend: + case SpellEffectName.ApplyAreaAuraEnemy: + case SpellEffectName.ApplyAreaAuraPet: + case SpellEffectName.ApplyAreaAuraOwner: + { + if (effect.ApplyAuraName == AuraType.PeriodicDamage || + effect.ApplyAuraName == AuraType.PeriodicDamagePercent || + effect.ApplyAuraName == AuraType.PeriodicDummy || + effect.ApplyAuraName == AuraType.PeriodicLeech || + effect.ApplyAuraName == AuraType.PeriodicHealthFunnel || + effect.ApplyAuraName == AuraType.PeriodicDummy) + break; + + goto default; + } + default: + { + // No value and not interrupt cast or crowd control without SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY flag + if (effect.CalcValue() == 0 && !((effect.Effect == SpellEffectName.InterruptCast || spellInfo.HasAttribute(SpellCustomAttributes.AuraCC)) && !spellInfo.HasAttribute(SpellAttr0.UnaffectedByInvulnerability))) + break; + + // Sindragosa Frost Breath + if (spellInfo.Id == 69649 || spellInfo.Id == 71056 || spellInfo.Id == 71057 || spellInfo.Id == 71058 || spellInfo.Id == 73061 || spellInfo.Id == 73062 || spellInfo.Id == 73063 || spellInfo.Id == 73064) + break; + + // Frostbolt + if (spellInfo.SpellFamilyName == SpellFamilyNames.Mage && spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x20u)) + break; + + // Frost Fever + if (spellInfo.Id == 55095) + break; + + // Haunt + if (spellInfo.SpellFamilyName == SpellFamilyNames.Warlock && spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x40000u)) + break; + + setFlag = true; + break; + } + } + + if (setFlag) + { + spellInfo.AttributesCu |= SpellCustomAttributes.BinarySpell; + break; + } + } + } + } + + // Remove normal school mask to properly calculate damage + if (spellInfo.SchoolMask.HasAnyFlag(SpellSchoolMask.Normal) && spellInfo.SchoolMask.HasAnyFlag(SpellSchoolMask.Magic)) + { + spellInfo.SchoolMask &= ~SpellSchoolMask.Normal; + spellInfo.AttributesCu |= SpellCustomAttributes.SchoolmaskNormalWithMagic; + } + if (!spellInfo._IsPositiveEffect(0, false)) spellInfo.AttributesCu |= SpellCustomAttributes.NegativeEff0; @@ -2244,9 +2335,67 @@ namespace Game.Entities if (talentSpells.Contains(spellInfo.Id)) spellInfo.AttributesCu |= SpellCustomAttributes.IsTalent; + switch (spellInfo.SpellFamilyName) + { + case SpellFamilyNames.Warrior: + // Shout / Piercing Howl + if (spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x20000u)/* || spellInfo->SpellFamilyFlags[1] & 0x20*/) + spellInfo.AttributesCu |= SpellCustomAttributes.AuraCC; + break; + case SpellFamilyNames.Druid: + // Roar + if (spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x8u)) + spellInfo.AttributesCu |= SpellCustomAttributes.AuraCC; + break; + case SpellFamilyNames.Generic: + // Stoneclaw Totem effect + if (spellInfo.Id == 5729) + spellInfo.AttributesCu |= SpellCustomAttributes.AuraCC; + break; + default: + break; + } + spellInfo._InitializeExplicitTargetMask(); } + // addition for binary spells, ommit spells triggering other spells + foreach (var spellInfo in mSpellInfoMap.Values) + { + if (spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)) + continue; + + bool allNonBinary = true; + bool overrideAttr = false; + foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None)) + { + if (effect == null) + continue; + + if (effect.IsAura() && effect.TriggerSpell != 0) + { + switch (effect.ApplyAuraName) + { + case AuraType.PeriodicTriggerSpell: + case AuraType.PeriodicTriggerSpellWithValue: + SpellInfo triggerSpell = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell); + if (triggerSpell != null) + { + overrideAttr = true; + if (triggerSpell.HasAttribute(SpellCustomAttributes.BinarySpell)) + allNonBinary = false; + } + break; + default: + break; + } + } + } + + if (overrideAttr && allNonBinary) + spellInfo.AttributesCu &= ~SpellCustomAttributes.BinarySpell; + } + Log.outInfo(LogFilter.ServerLoading, "Loaded spell custom attributes in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime)); }