From 5c4a7511ff0395c173bf8cdc04915a72141a2662 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 8 Sep 2021 17:40:50 -0400 Subject: [PATCH] Core/Spells: Cleanup spell effects Port From (https://github.com/TrinityCore/TrinityCore/commit/8a4e1119ac21e2d1112d1717337597fe073e495f) --- .../Framework/Constants/Spells/SpellConst.cs | 2 +- Source/Game/AI/CoreAI/UnitAI.cs | 83 +- Source/Game/AI/SmartScripts/SmartAIManager.cs | 8 +- Source/Game/Achievements/CriteriaHandler.cs | 5 +- Source/Game/Chat/Commands/LookupCommands.cs | 12 +- Source/Game/Conditions/ConditionManager.cs | 69 +- Source/Game/Entities/Creature/Creature.cs | 16 +- Source/Game/Entities/Creature/Gossip.cs | 6 +- Source/Game/Entities/Creature/Trainer.cs | 6 +- .../Entities/Object/Update/UpdateFields.cs | 6 +- Source/Game/Entities/Object/WorldObject.cs | 9 +- Source/Game/Entities/Player/Player.Items.cs | 7 +- Source/Game/Entities/Player/Player.Quest.cs | 7 +- Source/Game/Entities/Player/Player.Spells.cs | 14 +- Source/Game/Entities/Player/Player.Talents.cs | 18 +- Source/Game/Entities/Player/Player.cs | 15 +- Source/Game/Entities/StatSystem.cs | 19 +- Source/Game/Entities/Totem.cs | 9 +- Source/Game/Entities/Unit/Unit.Pets.cs | 6 +- Source/Game/Entities/Unit/Unit.Spells.cs | 93 +- Source/Game/Entities/Unit/Unit.cs | 16 +- Source/Game/Globals/ObjectManager.cs | 27 +- Source/Game/Handlers/PetHandler.cs | 4 +- Source/Game/Scripting/SpellScript.cs | 38 +- Source/Game/Spells/Auras/Aura.cs | 141 +- Source/Game/Spells/Auras/AuraEffect.cs | 24 +- Source/Game/Spells/Spell.cs | 709 +++-- Source/Game/Spells/SpellEffects.cs | 39 +- Source/Game/Spells/SpellInfo.cs | 397 ++- Source/Game/Spells/SpellManager.cs | 2328 ++++++++++------- Source/Scripts/Spells/DeathKnight.cs | 14 +- Source/Scripts/Spells/Druid.cs | 9 +- Source/Scripts/Spells/Generic.cs | 68 +- Source/Scripts/Spells/Hunter.cs | 2 +- Source/Scripts/Spells/Items.cs | 17 +- Source/Scripts/Spells/Mage.cs | 28 +- Source/Scripts/Spells/Monk.cs | 14 +- Source/Scripts/Spells/Priest.cs | 31 +- Source/Scripts/Spells/Quest.cs | 13 +- Source/Scripts/Spells/Warlock.cs | 26 +- Source/Scripts/Spells/Warrior.cs | 10 +- 41 files changed, 2378 insertions(+), 1987 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index dc527c384..fd2a1dcb8 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -2087,7 +2087,7 @@ namespace Framework.Constants public enum SpellEffectName { Any = -1, - Null = 0, + None = 0, Instakill = 1, SchoolDamage = 2, Dummy = 3, diff --git a/Source/Game/AI/CoreAI/UnitAI.cs b/Source/Game/AI/CoreAI/UnitAI.cs index 2f64d5ef9..92467d657 100644 --- a/Source/Game/AI/CoreAI/UnitAI.cs +++ b/Source/Game/AI/CoreAI/UnitAI.cs @@ -345,13 +345,9 @@ namespace Game.AI } else { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - var targetType = effect.TargetA.GetTarget(); - + var targetType = spellEffectInfo.TargetA.GetTarget(); if (targetType == Targets.UnitTargetEnemy || targetType == Targets.DestTargetEnemy) { if (AIInfo.target < AITarget.Victim) @@ -363,7 +359,7 @@ namespace Game.AI AIInfo.target = AITarget.Enemy; } - if (effect.Effect == SpellEffectName.ApplyAura) + if (spellEffectInfo.IsEffect(SpellEffectName.ApplyAura)) { if (targetType == Targets.UnitTargetEnemy) { @@ -384,73 +380,70 @@ namespace Game.AI AIInfo.Effects = 0; AIInfo.Targets = 0; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - // Spell targets self. - if (effect.TargetA.GetTarget() == Targets.UnitCaster) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCaster) AIInfo.Targets |= 1 << ((int)SelectTargetType.Self - 1); // Spell targets a single enemy. - if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy || - effect.TargetA.GetTarget() == Targets.DestTargetEnemy) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.DestTargetEnemy) AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleEnemy - 1); // Spell targets AoE at enemy. - if (effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || - effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || - effect.TargetA.GetTarget() == Targets.SrcCaster || - effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.SrcCaster || + spellEffectInfo.TargetA.GetTarget() == Targets.DestDynobjEnemy) AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeEnemy - 1); // Spell targets an enemy. - if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy || - effect.TargetA.GetTarget() == Targets.DestTargetEnemy || - effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || - effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || - effect.TargetA.GetTarget() == Targets.SrcCaster || - effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.DestTargetEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || + spellEffectInfo.TargetA.GetTarget() == Targets.SrcCaster || + spellEffectInfo.TargetA.GetTarget() == Targets.DestDynobjEnemy) AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyEnemy - 1); // Spell targets a single friend (or self). - if (effect.TargetA.GetTarget() == Targets.UnitCaster || - effect.TargetA.GetTarget() == Targets.UnitTargetAlly || - effect.TargetA.GetTarget() == Targets.UnitTargetParty) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCaster || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetAlly || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetParty) AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleFriend - 1); // Spell targets AoE friends. - if (effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || - effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || - effect.TargetA.GetTarget() == Targets.SrcCaster) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCasterAreaParty || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || + spellEffectInfo.TargetA.GetTarget() == Targets.SrcCaster) AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeFriend - 1); // Spell targets any friend (or self). - if (effect.TargetA.GetTarget() == Targets.UnitCaster || - effect.TargetA.GetTarget() == Targets.UnitTargetAlly || - effect.TargetA.GetTarget() == Targets.UnitTargetParty || - effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || - effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || - effect.TargetA.GetTarget() == Targets.SrcCaster) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCaster || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetAlly || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitTargetParty || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitCasterAreaParty || + spellEffectInfo.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || + spellEffectInfo.TargetA.GetTarget() == Targets.SrcCaster) AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyFriend - 1); // Make sure that this spell includes a damage effect. - if (effect.Effect == SpellEffectName.SchoolDamage || - effect.Effect == SpellEffectName.Instakill || - effect.Effect == SpellEffectName.EnvironmentalDamage || - effect.Effect == SpellEffectName.HealthLeech) + if (spellEffectInfo.Effect == SpellEffectName.SchoolDamage || + spellEffectInfo.Effect == SpellEffectName.Instakill || + spellEffectInfo.Effect == SpellEffectName.EnvironmentalDamage || + spellEffectInfo.Effect == SpellEffectName.HealthLeech) AIInfo.Effects |= 1 << ((int)SelectEffect.Damage - 1); // Make sure that this spell includes a healing effect (or an apply aura with a periodic heal). - if (effect.Effect == SpellEffectName.Heal || - effect.Effect == SpellEffectName.HealMaxHealth || - effect.Effect == SpellEffectName.HealMechanical || - (effect.Effect == SpellEffectName.ApplyAura && effect.ApplyAuraName == AuraType.PeriodicHeal)) + if (spellEffectInfo.Effect == SpellEffectName.Heal || + spellEffectInfo.Effect == SpellEffectName.HealMaxHealth || + spellEffectInfo.Effect == SpellEffectName.HealMechanical || + (spellEffectInfo.Effect == SpellEffectName.ApplyAura && spellEffectInfo.ApplyAuraName == AuraType.PeriodicHeal)) AIInfo.Effects |= 1 << ((int)SelectEffect.Healing - 1); // Make sure that this spell applies an aura. - if (effect.Effect == SpellEffectName.ApplyAura) + if (spellEffectInfo.Effect == SpellEffectName.ApplyAura) AIInfo.Effects |= 1 << ((int)SelectEffect.Aura - 1); } diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index b6c72029f..9692e7c7f 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -1046,13 +1046,13 @@ namespace Game.AI return false; SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell, Difficulty.None); - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect != null && (effect.IsEffect(SpellEffectName.KillCredit) || effect.IsEffect(SpellEffectName.KillCredit2))) + if (spellEffectInfo.IsEffect(SpellEffectName.KillCredit) || spellEffectInfo.IsEffect(SpellEffectName.KillCredit2)) { - if (effect.TargetA.GetTarget() == Targets.UnitCaster) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCaster) Log.outError(LogFilter.Sql, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} Effect: SPELL_EFFECT_KILL_CREDIT: (SpellId: {4} targetA: {5} - targetB: {6}) has invalid target for this Action", - e.EntryOrGuid, e.GetScriptType(), e.EventId, e.GetActionType(), e.Action.cast.spell, effect.TargetA.GetTarget(), effect.TargetB.GetTarget()); + e.EntryOrGuid, e.GetScriptType(), e.EventId, e.GetActionType(), e.Action.cast.spell, spellEffectInfo.TargetA.GetTarget(), spellEffectInfo.TargetB.GetTarget()); } } break; diff --git a/Source/Game/Achievements/CriteriaHandler.cs b/Source/Game/Achievements/CriteriaHandler.cs index f2f5ccdb3..c16cd4ccc 100644 --- a/Source/Game/Achievements/CriteriaHandler.cs +++ b/Source/Game/Achievements/CriteriaHandler.cs @@ -4175,14 +4175,13 @@ namespace Game.Achievements criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId); return false; } - SpellEffectInfo effect = spellEntry.GetEffect(Aura.EffectIndex); - if (effect == null) + if (spellEntry.GetEffects().Count <= Aura.EffectIndex) { Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has wrong spell effect index in value2 ({3}), ignored.", criteria.Id, criteria.Entry.Type, DataType, Aura.EffectIndex); return false; } - if (effect.ApplyAuraName == 0) + if (spellEntry.GetEffect(Aura.EffectIndex).ApplyAuraName == 0) { Log.outError(LogFilter.Sql, "Table `criteria_data` (Entry: {0} Type: {1}) for data type {2} has non-aura spell effect (ID: {3} Effect: {4}), ignores.", criteria.Id, criteria.Entry.Type, DataType, Aura.SpellId, Aura.EffectIndex); diff --git a/Source/Game/Chat/Commands/LookupCommands.cs b/Source/Game/Chat/Commands/LookupCommands.cs index 7059bc390..c45cc481c 100644 --- a/Source/Game/Chat/Commands/LookupCommands.cs +++ b/Source/Game/Chat/Commands/LookupCommands.cs @@ -1121,10 +1121,10 @@ namespace Game.Chat } bool known = target && target.HasSpell(spellInfo.Id); - SpellEffectInfo effect = spellInfo.GetEffect(0); - bool learn = effect?.Effect == SpellEffectName.LearnSpell; + SpellEffectInfo spellEffectInfo = spellInfo.GetEffect(0); + bool learn = spellEffectInfo.Effect == SpellEffectName.LearnSpell; - SpellInfo learnSpellInfo = effect != null ? Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, spellInfo.Difficulty) : null; + SpellInfo learnSpellInfo = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, spellInfo.Difficulty); bool talent = spellInfo.HasAttribute(SpellCustomAttributes.IsTalent); bool passive = spellInfo.IsPassive(); @@ -1195,10 +1195,10 @@ namespace Game.Chat } bool known = target && target.HasSpell(id); - SpellEffectInfo effect = spellInfo.GetEffect(0); - bool learn = (effect.Effect == SpellEffectName.LearnSpell); + SpellEffectInfo spellEffectInfo = spellInfo.GetEffect(0); + bool learn = spellEffectInfo.Effect == SpellEffectName.LearnSpell; - SpellInfo learnSpellInfo = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); + SpellInfo learnSpellInfo = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, Difficulty.None); bool talent = spellInfo.HasAttribute(SpellCustomAttributes.IsTalent); bool passive = spellInfo.IsPassive(); diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs index 56ce5f9d3..7d8560d9f 100644 --- a/Source/Game/Conditions/ConditionManager.cs +++ b/Source/Game/Conditions/ConditionManager.cs @@ -577,35 +577,31 @@ namespace Game { uint conditionEffMask = cond.SourceGroup; List sharedMasks = new(); - for (byte i = 0; i < SpellConst.MaxEffects; ++i) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - SpellEffectInfo effect = spellInfo.GetEffect(i); - if (effect == null) - continue; - // additional checks by condition type - if ((conditionEffMask & (1 << i)) != 0) + if ((conditionEffMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0) { switch (cond.ConditionType) { case ConditionTypes.ObjectEntryGuid: { - SpellCastTargetFlags implicitTargetMask = SpellInfo.GetTargetFlagMask(effect.TargetA.GetObjectType()) | SpellInfo.GetTargetFlagMask(effect.TargetB.GetObjectType()); + SpellCastTargetFlags implicitTargetMask = SpellInfo.GetTargetFlagMask(spellEffectInfo.TargetA.GetObjectType()) | SpellInfo.GetTargetFlagMask(spellEffectInfo.TargetB.GetObjectType()); if (implicitTargetMask.HasFlag(SpellCastTargetFlags.UnitMask) && cond.ConditionValue1 != (uint)TypeId.Unit && cond.ConditionValue1 != (uint)TypeId.Player) { - Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{i} - target requires ConditionValue1 to be either TYPEID_UNIT ({(uint)TypeId.Unit}) or TYPEID_PLAYER ({(uint)TypeId.Player})"); + Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{spellEffectInfo.EffectIndex} - target requires ConditionValue1 to be either TYPEID_UNIT ({(uint)TypeId.Unit}) or TYPEID_PLAYER ({(uint)TypeId.Player})"); return; } if (implicitTargetMask.HasFlag(SpellCastTargetFlags.GameobjectMask) && cond.ConditionValue1 != (uint)TypeId.GameObject) { - Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{i} - target requires ConditionValue1 to be TYPEID_GAMEOBJECT ({(uint)TypeId.GameObject})"); + Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{spellEffectInfo.EffectIndex} - target requires ConditionValue1 to be TYPEID_GAMEOBJECT ({(uint)TypeId.GameObject})"); return; } if (implicitTargetMask.HasFlag(SpellCastTargetFlags.CorpseMask) && cond.ConditionValue1 != (uint)TypeId.Corpse) { - Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{i} - target requires ConditionValue1 to be TYPEID_CORPSE ({(uint)TypeId.Corpse})"); + Log.outError(LogFilter.Sql, $"{cond} in `condition` table - spell {spellInfo.Id} EFFECT_{spellEffectInfo.EffectIndex} - target requires ConditionValue1 to be TYPEID_CORPSE ({(uint)TypeId.Corpse})"); return; } break; @@ -616,21 +612,16 @@ namespace Game } // check if effect is already a part of some shared mask - if (sharedMasks.Any(mask => !!Convert.ToBoolean(mask & (1 << i)))) + if (sharedMasks.Any(mask => !!Convert.ToBoolean(mask & (1 << (int)spellEffectInfo.EffectIndex)))) continue; // build new shared mask with found effect - uint sharedMask = (uint)(1 << i); - List cmp = effect.ImplicitTargetConditions; - for (byte effIndex = (byte)(i + 1); effIndex < SpellConst.MaxEffects; ++effIndex) - { - SpellEffectInfo inner = spellInfo.GetEffect(effIndex); - if (inner == null) - continue; + uint sharedMask = (uint)(1 << (int)spellEffectInfo.EffectIndex); + List cmp = spellEffectInfo.ImplicitTargetConditions; + for (uint effIndex = spellEffectInfo.EffectIndex + 1; effIndex < spellInfo.GetEffects().Count; ++effIndex) + if (spellInfo.GetEffect(effIndex).ImplicitTargetConditions == cmp) + sharedMask |= (uint)(1 << (int)effIndex); - if (inner.ImplicitTargetConditions == cmp) - sharedMask |= (uint)(1 << effIndex); - } sharedMasks.Add(sharedMask); } @@ -645,15 +636,11 @@ namespace Game if (((1 << firstEffIndex) & effectMask) != 0) break; - if (firstEffIndex >= SpellConst.MaxEffects) + if (firstEffIndex >= spellInfo.GetEffects().Count) return; - SpellEffectInfo effect = spellInfo.GetEffect(firstEffIndex); - if (effect == null) - continue; - // get shared data - List sharedList = effect.ImplicitTargetConditions; + List sharedList = spellInfo.GetEffect(firstEffIndex).ImplicitTargetConditions; // there's already data entry for that sharedMask if (sharedList != null) @@ -672,15 +659,11 @@ namespace Game // add new list, create new shared mask sharedList = new List(); bool assigned = false; - for (byte i = firstEffIndex; i < SpellConst.MaxEffects; ++i) + for (uint i = firstEffIndex; i < spellInfo.GetEffects().Count; ++i) { - SpellEffectInfo eff = spellInfo.GetEffect(i); - if (eff == null) - continue; - - if (((1 << i) & commonMask) != 0) + if (((1 << (int)i) & commonMask) != 0) { - eff.ImplicitTargetConditions = sharedList; + spellInfo.GetEffect(i).ImplicitTargetConditions = sharedList; assigned = true; } } @@ -966,19 +949,15 @@ namespace Game uint origGroup = cond.SourceGroup; - for (byte i = 0; i < SpellConst.MaxEffects; ++i) + foreach (SpellEffectInfo spellEffectInfo in spellInfo.GetEffects()) { - if (((1 << i) & cond.SourceGroup) == 0) + if (((1 << (int)spellEffectInfo.EffectIndex) & cond.SourceGroup) == 0) continue; - SpellEffectInfo effect = spellInfo.GetEffect(i); - if (effect == null) + if (spellEffectInfo.ChainTargets > 0) continue; - if (effect.ChainTargets > 0) - continue; - - switch (effect.TargetA.GetSelectionCategory()) + switch (spellEffectInfo.TargetA.GetSelectionCategory()) { case SpellTargetSelectionCategories.Nearby: case SpellTargetSelectionCategories.Cone: @@ -990,7 +969,7 @@ namespace Game break; } - switch (effect.TargetB.GetSelectionCategory()) + switch (spellEffectInfo.TargetB.GetSelectionCategory()) { case SpellTargetSelectionCategories.Nearby: case SpellTargetSelectionCategories.Cone: @@ -1002,8 +981,8 @@ namespace Game break; } - Log.outError(LogFilter.Sql, "SourceEntry {0} SourceGroup {1} in `condition` table - spell {2} does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_, _CHAIN_ for effect {3}, SourceGroup needs correction, ignoring.", cond.SourceEntry, origGroup, cond.SourceEntry, i); - cond.SourceGroup &= ~(uint)(1 << i); + Log.outError(LogFilter.Sql, "SourceEntry {0} SourceGroup {1} in `condition` table - spell {2} does not have implicit targets of types: _AREA_, _CONE_, _NEARBY_, _CHAIN_ for effect {3}, SourceGroup needs correction, ignoring.", cond.SourceEntry, origGroup, cond.SourceEntry, spellEffectInfo.EffectIndex); + cond.SourceGroup &= ~(1u << (int)spellEffectInfo.EffectIndex); } // all effects were removed, no need to add the condition at all if (cond.SourceGroup == 0) diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index a77e3ea57..a9a5d3ec3 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -2045,12 +2045,12 @@ namespace Game.Entities return false; bool immunedToAllEffects = true; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; - if (!IsImmunedToSpellEffect(spellInfo, effect.EffectIndex, caster)) + if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster)) { immunedToAllEffects = false; break; @@ -2063,16 +2063,12 @@ namespace Game.Entities return base.IsImmunedToSpell(spellInfo, caster); } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, WorldObject caster) { - SpellEffectInfo effect = spellInfo.GetEffect(index); - if (effect == null) + if (GetCreatureTemplate().CreatureType == CreatureType.Mechanical && spellEffectInfo.IsEffect(SpellEffectName.Heal)) return true; - if (GetCreatureTemplate().CreatureType == CreatureType.Mechanical && effect.Effect == SpellEffectName.Heal) - return true; - - return base.IsImmunedToSpellEffect(spellInfo, index, caster); + return base.IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster); } public bool IsElite() diff --git a/Source/Game/Entities/Creature/Gossip.cs b/Source/Game/Entities/Creature/Gossip.cs index ecf9491f6..4b0ba4af9 100644 --- a/Source/Game/Entities/Creature/Gossip.cs +++ b/Source/Game/Entities/Creature/Gossip.cs @@ -444,9 +444,9 @@ namespace Game.Misc SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(quest.RewardSpell, Difficulty.None); if (spellInfo != null) { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.IsEffect(SpellEffectName.LearnSpell)) - packet.LearnSpells.Add(effect.TriggerSpell); + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.LearnSpell)) + packet.LearnSpells.Add(spellEffectInfo.TriggerSpell); } quest.BuildQuestRewards(packet.Rewards, _session.GetPlayer()); diff --git a/Source/Game/Entities/Creature/Trainer.cs b/Source/Game/Entities/Creature/Trainer.cs index b4aa63ad7..c2b74582d 100644 --- a/Source/Game/Entities/Creature/Trainer.cs +++ b/Source/Game/Entities/Creature/Trainer.cs @@ -129,13 +129,13 @@ namespace Game.Entities // check ranks bool hasLearnSpellEffect = false; bool knowsAllLearnedSpells = true; - foreach (SpellEffectInfo spellEffect in Global.SpellMgr.GetSpellInfo(trainerSpell.SpellId, Difficulty.None).GetEffects()) + foreach (var spellEffectInfo in Global.SpellMgr.GetSpellInfo(trainerSpell.SpellId, Difficulty.None).GetEffects()) { - if (spellEffect == null || !spellEffect.IsEffect(SpellEffectName.LearnSpell)) + if (!spellEffectInfo.IsEffect(SpellEffectName.LearnSpell)) continue; hasLearnSpellEffect = true; - if (!player.HasSpell(spellEffect.TriggerSpell)) + if (!player.HasSpell(spellEffectInfo.TriggerSpell)) knowsAllLearnedSpells = false; } diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index 9ec6f2e52..488843764 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -2219,11 +2219,11 @@ namespace Game.Entities SpellInfo transform = Global.SpellMgr.GetSpellInfo(unit.GetTransForm(), unit.GetMap().GetDifficultyID()); if (transform != null) { - foreach (SpellEffectInfo effect in transform.GetEffects()) + foreach (var spellEffectInfo in transform.GetEffects()) { - if (effect != null && effect.IsAura(AuraType.Transform)) + if (spellEffectInfo.IsAura(AuraType.Transform)) { - CreatureTemplate transformInfo = Global.ObjectMgr.GetCreatureTemplate((uint)effect.MiscValue); + CreatureTemplate transformInfo = Global.ObjectMgr.GetCreatureTemplate((uint)spellEffectInfo.MiscValue); if (transformInfo != null) { cinfo = transformInfo; diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 65aec8fef..7ec5b1e40 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -1682,18 +1682,17 @@ namespace Game.Entities return null; } - public int CalculateSpellDamage(Unit target, SpellInfo spellProto, uint effIndex, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) + public int CalculateSpellDamage(Unit target, SpellEffectInfo spellEffectInfo, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) { - return CalculateSpellDamage(out _, target, spellProto, effIndex, basePoints, castItemId, itemLevel); + return CalculateSpellDamage(out _, target, spellEffectInfo, basePoints, castItemId, itemLevel); } // function uses real base points (typically value - 1) - public int CalculateSpellDamage(out float variance, Unit target, SpellInfo spellProto, uint effIndex, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) + public int CalculateSpellDamage(out float variance, Unit target, SpellEffectInfo spellEffectInfo, int? basePoints = null, uint castItemId = 0, int itemLevel = -1) { - SpellEffectInfo effect = spellProto.GetEffect(effIndex); variance = 0.0f; - return effect != null ? effect.CalcValue(out variance, this, basePoints, target, castItemId, itemLevel) : 0; + return spellEffectInfo != null ? spellEffectInfo.CalcValue(out variance, this, basePoints, target, castItemId, itemLevel) : 0; } public float GetSpellMaxRangeForTarget(Unit target, SpellInfo spellInfo) diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 280fa9d3e..74d1e730a 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -5136,9 +5136,10 @@ namespace Game.Entities CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); args.CastItem = artifact; if (artifactPowerRank.AuraPointsOverride != 0) - for (int i = 0; i < SpellConst.MaxEffects; ++i) - if (spellInfo.GetEffect((uint)i) != null) - args.AddSpellMod(SpellValueMod.BasePoint0 + i, (int)artifactPowerRank.AuraPointsOverride); + { + foreach (var spellEffectInfo in spellInfo.GetEffects()) + args.AddSpellMod(SpellValueMod.BasePoint0 + (int)spellEffectInfo.EffectIndex, (int)artifactPowerRank.AuraPointsOverride); + } CastSpell(this, artifactPowerRank.SpellID, args); } diff --git a/Source/Game/Entities/Player/Player.Quest.cs b/Source/Game/Entities/Player/Player.Quest.cs index 091a4ae52..d1ff4264d 100644 --- a/Source/Game/Entities/Player/Player.Quest.cs +++ b/Source/Game/Entities/Player/Player.Quest.cs @@ -107,9 +107,9 @@ namespace Game.Entities // check learned spells state bool found = false; - foreach (SpellEffectInfo eff in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (eff != null && eff.Effect == SpellEffectName.LearnSpell && !HasSpell(eff.TriggerSpell)) + if (spellEffectInfo.IsEffect(SpellEffectName.LearnSpell) && !HasSpell(spellEffectInfo.TriggerSpell)) { found = true; break; @@ -121,9 +121,6 @@ namespace Game.Entities return; SpellEffectInfo effect = spellInfo.GetEffect(0); - if (effect == null) - return; - uint learned_0 = effect.TriggerSpell; if (!HasSpell(learned_0)) { diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index c8b1c7003..e7b443fd1 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -1711,8 +1711,8 @@ namespace Game.Entities // special check to filter things like Shield Wall, the aura is not permanent and must stay even without required item if (!spellInfo.IsPassive()) { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.IsAura()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsAura()) return true; } } @@ -2284,9 +2284,9 @@ namespace Game.Entities // passive spells which apply aura and have an item requirement are to be added manually, instead of casted if (spellInfo.EquippedItemClass >= 0) { - foreach (SpellEffectInfo effectInfo in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effectInfo != null && effectInfo.IsAura()) + if (spellEffectInfo.IsAura()) { if (!HasAura(spellInfo.Id) && HasItemFitToSpellRequirements(spellInfo)) AddAura(spellInfo.Id, this); @@ -3289,10 +3289,10 @@ namespace Game.Entities int effectPct = Math.Max(0, 100 - (lvlDifference * lvlPenaltyFactor)); - for (byte i = 0; i < SpellConst.MaxEffects; ++i) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (spellInfo.GetEffect(i).IsEffect()) - args.AddSpellMod(SpellValueMod.BasePoint0 + i, MathFunctions.CalculatePct(spellInfo.GetEffect(i).CalcValue(this), effectPct)); + if (spellEffectInfo.IsEffect()) + args.AddSpellMod(SpellValueMod.BasePoint0 + (int)spellEffectInfo.EffectIndex, MathFunctions.CalculatePct(spellEffectInfo.CalcValue(this), effectPct)); } } diff --git a/Source/Game/Entities/Player/Player.Talents.cs b/Source/Game/Entities/Player/Player.Talents.cs index d2207c4fc..78754d42a 100644 --- a/Source/Game/Entities/Player/Player.Talents.cs +++ b/Source/Game/Entities/Player/Player.Talents.cs @@ -91,9 +91,9 @@ namespace Game.Entities RemoveSpell(talent.SpellID, true); // search for spells that the talent teaches and unlearn them - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell) - RemoveSpell(effect.TriggerSpell, true); + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.LearnSpell) && spellEffectInfo.TriggerSpell > 0) + RemoveSpell(spellEffectInfo.TriggerSpell, true); if (talent.OverridesSpellID != 0) RemoveOverrideSpell(talent.OverridesSpellID, talent.SpellID); @@ -301,9 +301,9 @@ namespace Game.Entities RemoveSpell(talentInfo.SpellID, true); // search for spells that the talent teaches and unlearn them - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell) - RemoveSpell(effect.TriggerSpell, true); + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.LearnSpell) && spellEffectInfo.TriggerSpell > 0) + RemoveSpell(spellEffectInfo.TriggerSpell, true); if (talentInfo.OverridesSpellID != 0) RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID); @@ -318,9 +318,9 @@ namespace Game.Entities RemoveSpell(talentInfo.SpellID, true); // search for spells that the talent teaches and unlearn them - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.TriggerSpell > 0 && effect.Effect == SpellEffectName.LearnSpell) - RemoveSpell(effect.TriggerSpell, true); + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.LearnSpell) && spellEffectInfo.TriggerSpell > 0) + RemoveSpell(spellEffectInfo.TriggerSpell, true); if (talentInfo.OverridesSpellID != 0) RemoveOverrideSpell(talentInfo.OverridesSpellID, talentInfo.SpellID); diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 07e0600ed..f01e7eec5 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -3636,19 +3636,16 @@ namespace Game.Entities return false; } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, WorldObject caster) { - SpellEffectInfo effect = spellInfo.GetEffect(index); - if (effect == null || !effect.IsEffect()) - return false; - // players are immune to taunt (the aura and the spell effect). - if (effect.IsAura(AuraType.ModTaunt)) - return true; - if (effect.IsEffect(SpellEffectName.AttackMe)) + if (spellEffectInfo.IsAura(AuraType.ModTaunt)) return true; - return base.IsImmunedToSpellEffect(spellInfo, index, caster); + if (spellEffectInfo.IsEffect(SpellEffectName.AttackMe)) + return true; + + return base.IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster); } void RegenerateAll() diff --git a/Source/Game/Entities/StatSystem.cs b/Source/Game/Entities/StatSystem.cs index 50bb66958..f4ba44a18 100644 --- a/Source/Game/Entities/StatSystem.cs +++ b/Source/Game/Entities/StatSystem.cs @@ -1005,12 +1005,12 @@ namespace Game.Entities return 0; int resistMech = 0; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) break; - int effect_mech = (int)spellInfo.GetEffectMechanic(effect.EffectIndex); + int effect_mech = (int)spellInfo.GetEffectMechanic(spellEffectInfo.EffectIndex); if (effect_mech != 0) { int temp = GetTotalAuraModifierByMiscValue(AuraType.ModMechanicResistance, effect_mech); @@ -1538,21 +1538,18 @@ namespace Game.Entities if (chrSpec == null) return; - for (uint i = 0; i < PlayerConst.MaxMasterySpells; ++i) + foreach (uint masterySpellId in chrSpec.MasterySpellID) { - Aura aura = GetAura(chrSpec.MasterySpellID[i]); + Aura aura = GetAura(masterySpellId); if (aura != null) { - foreach (SpellEffectInfo effect in aura.GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in aura.GetSpellInfo().GetEffects()) { - if (effect == null) - continue; - - float mult = effect.BonusCoefficient; + float mult = spellEffectInfo.BonusCoefficient; if (MathFunctions.fuzzyEq(mult, 0.0f)) continue; - aura.GetEffect(effect.EffectIndex).ChangeAmount((int)(value * mult)); + aura.GetEffect(spellEffectInfo.EffectIndex).ChangeAmount((int)(value * mult)); } } } diff --git a/Source/Game/Entities/Totem.cs b/Source/Game/Entities/Totem.cs index 4357f4e44..731257117 100644 --- a/Source/Game/Entities/Totem.cs +++ b/Source/Game/Entities/Totem.cs @@ -143,17 +143,16 @@ namespace Game.Entities AddObjectToRemoveList(); } - public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) + public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, WorldObject caster) { // @todo possibly all negative auras immune? if (GetEntry() == 5925) return false; - SpellEffectInfo effect = spellInfo.GetEffect(index); - if (effect == null) + if (spellEffectInfo == null) return true; - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.PeriodicDamage: case AuraType.PeriodicLeech: @@ -164,7 +163,7 @@ namespace Game.Entities break; } - return base.IsImmunedToSpellEffect(spellInfo, index, caster); + return base.IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster); } public uint GetSpell(byte slot = 0) { return m_spells[slot]; } diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs index 040acfc30..c83862e87 100644 --- a/Source/Game/Entities/Unit/Unit.Pets.cs +++ b/Source/Game/Entities/Unit/Unit.Pets.cs @@ -226,12 +226,12 @@ namespace Game.Entities SpellInfo spInfo = Global.SpellMgr.GetSpellInfo(minion.ToTotem().GetSpell(), GetMap().GetDifficultyID()); if (spInfo != null) { - foreach (SpellEffectInfo effect in spInfo.GetEffects()) + foreach (var spellEffectInfo in spInfo.GetEffects()) { - if (effect == null || effect.Effect != SpellEffectName.Summon) + if (spellEffectInfo == null || !spellEffectInfo.IsEffect(SpellEffectName.Summon)) continue; - RemoveAllMinionsByEntry((uint)effect.MiscValue); + RemoveAllMinionsByEntry((uint)spellEffectInfo.MiscValue); } } } diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index e6098d3f7..40076dfed 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -69,7 +69,7 @@ namespace Game.Entities return DoneAdvertisedBenefit; } - public uint SpellDamageBonusDone(Unit victim, SpellInfo spellProto, uint pdamage, DamageEffectType damagetype, SpellEffectInfo effect, uint stack = 1) + public uint SpellDamageBonusDone(Unit victim, SpellInfo spellProto, uint pdamage, DamageEffectType damagetype, SpellEffectInfo spellEffectInfo, uint stack = 1) { if (spellProto == null || victim == null || damagetype == DamageEffectType.Direct) return pdamage; @@ -83,7 +83,7 @@ namespace Game.Entities { Unit owner = GetOwner(); if (owner != null) - return owner.SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, effect, stack); + return owner.SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, spellEffectInfo, stack); } int DoneTotal = 0; @@ -100,9 +100,9 @@ namespace Game.Entities DoneAdvertisedBenefit += ((Guardian)this).GetBonusDamage(); // Check for table values - if (effect.BonusCoefficientFromAP > 0.0f) + if (spellEffectInfo.BonusCoefficientFromAP > 0.0f) { - float ApCoeffMod = effect.BonusCoefficientFromAP; + float ApCoeffMod = spellEffectInfo.BonusCoefficientFromAP; Player modOwner = GetSpellModOwner(); if (modOwner) { @@ -130,7 +130,7 @@ namespace Game.Entities } // Default calculation - float coeff = effect.BonusCoefficient; + float coeff = spellEffectInfo.BonusCoefficient; if (DoneAdvertisedBenefit != 0) { Player modOwner1 = GetSpellModOwner(); @@ -382,14 +382,14 @@ namespace Game.Entities return damage; } - public uint SpellHealingBonusDone(Unit victim, SpellInfo spellProto, uint healamount, DamageEffectType damagetype, SpellEffectInfo effect, uint stack = 1) + public uint SpellHealingBonusDone(Unit victim, SpellInfo spellProto, uint healamount, DamageEffectType damagetype, SpellEffectInfo spellEffectInfo, uint stack = 1) { // For totems get healing bonus from owner (statue isn't totem in fact) if (IsTypeId(TypeId.Unit) && IsTotem()) { Unit owner = GetOwner(); if (owner) - return owner.SpellHealingBonusDone(victim, spellProto, healamount, damagetype, effect, stack); + return owner.SpellHealingBonusDone(victim, spellProto, healamount, damagetype, spellEffectInfo, stack); } // No bonus healing for potion spells @@ -428,14 +428,14 @@ namespace Game.Entities DoneAdvertisedBenefit += (uint)((Guardian)this).GetBonusDamage(); // Check for table values - float coeff = effect.BonusCoefficient; - if (effect.BonusCoefficientFromAP > 0.0f) + float coeff = spellEffectInfo.BonusCoefficient; + if (spellEffectInfo.BonusCoefficientFromAP > 0.0f) { WeaponAttackType attType = (spellProto.IsRangedWeaponSpell() && spellProto.DmgClass != SpellDmgClass.Melee) ? WeaponAttackType.RangedAttack : WeaponAttackType.BaseAttack; float APbonus = (float)victim.GetTotalAuraModifier(attType == WeaponAttackType.BaseAttack ? AuraType.MeleeAttackPowerAttackerBonus : AuraType.RangedAttackPowerAttackerBonus); APbonus += GetTotalAttackPowerValue(attType); - DoneTotal += (int)(effect.BonusCoefficientFromAP * stack * APbonus); + DoneTotal += (int)(spellEffectInfo.BonusCoefficientFromAP * stack * APbonus); } else if (coeff <= 0.0f) // no AP and no SP coefs, skip { @@ -458,12 +458,9 @@ namespace Game.Entities DoneTotal += (int)(DoneAdvertisedBenefit * coeff * stack); } - foreach (SpellEffectInfo eff in spellProto.GetEffects()) + foreach (var otherSpellEffectInfo in spellProto.GetEffects()) { - if (eff == null) - continue; - - switch (eff.ApplyAuraName) + switch (otherSpellEffectInfo.ApplyAuraName) { // Bonus healing does not apply to these spells case AuraType.PeriodicLeech: @@ -471,7 +468,7 @@ namespace Game.Entities DoneTotal = 0; break; } - if (eff.Effect == SpellEffectName.HealthLeech) + if (otherSpellEffectInfo.IsEffect(SpellEffectName.HealthLeech)) DoneTotal = 0; } @@ -1247,14 +1244,14 @@ namespace Game.Entities } bool immuneToAllEffects = true; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { // State/effect immunities applied by aura expect full spell immunity // Ignore effects with mechanic, they are supposed to be checked separately - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; - if (!IsImmunedToSpellEffect(spellInfo, effect.EffectIndex, caster)) + if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster)) { immuneToAllEffects = false; break; @@ -1313,22 +1310,20 @@ namespace Game.Entities return mask; } - public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, uint index, WorldObject caster) + public virtual bool IsImmunedToSpellEffect(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, WorldObject caster) { if (spellInfo == null) return false; - SpellEffectInfo effect = spellInfo.GetEffect(index); - if (effect == null || !effect.IsEffect()) + if (spellEffectInfo == null || !spellEffectInfo.IsEffect()) return false; // If m_immuneToEffect type contain this effect type, IMMUNE effect. - uint eff = (uint)effect.Effect; var effectList = m_spellImmune[(int)SpellImmunity.Effect]; - if (effectList.ContainsKey(eff)) + if (effectList.ContainsKey((uint)spellEffectInfo.Effect)) return true; - uint mechanic = (uint)effect.Mechanic; + uint mechanic = (uint)spellEffectInfo.Mechanic; if (mechanic != 0) { var mechanicList = m_spellImmune[(int)SpellImmunity.Mechanic]; @@ -1338,7 +1333,7 @@ namespace Game.Entities if (!spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) { - uint aura = (uint)effect.ApplyAuraName; + uint aura = (uint)spellEffectInfo.ApplyAuraName; if (aura != 0) { var list = m_spellImmune[(int)SpellImmunity.State]; @@ -1351,7 +1346,7 @@ namespace Game.Entities var immuneAuraApply = GetAuraEffectsByType(AuraType.ModImmuneAuraApplySchool); foreach (var auraEffect in immuneAuraApply) if (Convert.ToBoolean(auraEffect.GetMiscValue() & (int)spellInfo.GetSchoolMask()) && // Check school - ((caster && !IsFriendlyTo(caster)) || !spellInfo.IsPositiveEffect(index))) // Harmful + ((caster && !IsFriendlyTo(caster)) || !spellInfo.IsPositiveEffect(spellEffectInfo.EffectIndex))) // Harmful return true; } } @@ -2195,9 +2190,9 @@ namespace Game.Entities } // turn off snare auras by setting amount to 0 - foreach (SpellEffectInfo spellEffectInfo in aura.GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in aura.GetSpellInfo().GetEffects()) { - if (spellEffectInfo != null && pair.Value.HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.Mechanic == Mechanics.Snare) + if (pair.Value.HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.Mechanic == Mechanics.Snare) aura.GetEffect(spellEffectInfo.EffectIndex).ChangeAmount(0); } } @@ -2492,12 +2487,13 @@ namespace Game.Entities if (target.IsImmunedToSpell(spellInfo, this)) return null; - for (byte i = 0; i < SpellConst.MaxEffects; ++i) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (!Convert.ToBoolean(effMask & (1 << i))) + if ((effMask & (1 << (int)spellEffectInfo.EffectIndex)) == 0) continue; - if (target.IsImmunedToSpellEffect(spellInfo, i, this)) - effMask &= ~(uint)(1 << i); + + if (target.IsImmunedToSpellEffect(spellInfo, spellEffectInfo, this)) + effMask &= ~(1u << (int)spellEffectInfo.EffectIndex); } if (effMask == 0) @@ -2546,12 +2542,9 @@ namespace Game.Entities { byte i = 0; bool valid = false; - foreach (SpellEffectInfo effect in spellEntry.GetEffects()) + foreach (var spellEffectInfo in spellEntry.GetEffects()) { - if (effect == null) - continue; - - if (effect.ApplyAuraName == AuraType.ControlVehicle) + if (spellEffectInfo.ApplyAuraName == AuraType.ControlVehicle) { valid = true; break; @@ -2575,11 +2568,8 @@ namespace Game.Entities else // This can happen during Player._LoadAuras { int[] bp = new int[SpellConst.MaxEffects]; - foreach (SpellEffectInfo effect in spellEntry.GetEffects()) - { - if (effect != null) - bp[effect.EffectIndex] = effect.BasePoints; - } + foreach (var spellEffectInfo in spellEntry.GetEffects()) + bp[spellEffectInfo.EffectIndex] = spellEffectInfo.BasePoints; bp[i] = seatId; @@ -2643,7 +2633,7 @@ namespace Game.Entities if (spellInfo.Mechanic != 0 && Convert.ToBoolean(mechanicMask & (1 << (int)spellInfo.Mechanic))) return true; - foreach (SpellEffectInfo spellEffectInfo in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) if (spellEffectInfo != null && pair.Value.HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.IsEffect() && spellEffectInfo.Mechanic != 0) if ((mechanicMask & (1 << (int)spellEffectInfo.Mechanic)) != 0) return true; @@ -3891,22 +3881,19 @@ namespace Game.Entities return null; // update basepoints with new values - effect amount will be recalculated in ModStackAmount - foreach (SpellEffectInfo effect in createInfo.GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in createInfo.GetSpellInfo().GetEffects()) { - if (effect == null) - continue; - - AuraEffect eff = foundAura.GetEffect(effect.EffectIndex); - if (eff == null) + AuraEffect auraEff = foundAura.GetEffect(spellEffectInfo.EffectIndex); + if (auraEff == null) continue; int bp; if (createInfo.BaseAmount != null) - bp = createInfo.BaseAmount[effect.EffectIndex]; + bp = createInfo.BaseAmount[spellEffectInfo.EffectIndex]; else - bp = effect.BasePoints; + bp = spellEffectInfo.BasePoints; - eff.m_baseAmount = bp; + auraEff.m_baseAmount = bp; } // correct cast item guid if needed diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 5b4802dd5..9a6649821 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -2089,7 +2089,7 @@ namespace Game.Entities RemoveDynamicUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ChannelObjects), index); } - public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, sbyte effIndex = -1) + public static bool IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo spellInfo = null, SpellEffectInfo spellEffectInfo = null) { // only physical spells damage gets reduced by armor if ((schoolMask & SpellSchoolMask.Normal) == 0) @@ -2101,16 +2101,12 @@ namespace Game.Entities if (spellInfo.HasAttribute(SpellCustomAttributes.IgnoreArmor)) return false; - if (effIndex != -1) + // bleeding effects are not reduced by armor + if (spellEffectInfo != null) { - // bleeding effects are not reduced by armor - SpellEffectInfo effect = spellInfo.GetEffect((uint)effIndex); - if (effect != null) - { - if (effect.ApplyAuraName == AuraType.PeriodicDamage || effect.Effect == SpellEffectName.SchoolDamage) - if (spellInfo.GetEffectMechanicMask((byte)effIndex).HasAnyFlag((1u << (int)Mechanics.Bleed))) - return false; - } + if (spellEffectInfo.ApplyAuraName == AuraType.PeriodicDamage || spellEffectInfo.Effect == SpellEffectName.SchoolDamage) + if (spellInfo.GetEffectMechanicMask(spellEffectInfo.EffectIndex).HasAnyFlag((1u << (int)Mechanics.Bleed))) + return false; } } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 89cdecc38..ea65da715 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -1447,11 +1447,16 @@ namespace Game continue; } - byte i = (byte)((script.Key >> 24) & 0x000000FF); + byte spellEffIndex = (byte)((script.Key >> 24) & 0x000000FF); + if (spellEffIndex >= spellInfo.GetEffects().Count) + { + Log.outError(LogFilter.Sql, $"Table `spell_scripts` has too high effect index {spellEffIndex} for spell (Id: {spellId}) as script id"); + continue; + } + //check for correct spellEffect - SpellEffectInfo effect = spellInfo.GetEffect(i); - if (effect != null && (effect.Effect == 0 || (effect.Effect != SpellEffectName.ScriptEffect && effect.Effect != SpellEffectName.Dummy))) - Log.outError(LogFilter.Sql, "Table `spell_scripts` - spell {0} effect {1} is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, i); + if (spellInfo.GetEffect(spellEffIndex).Effect == 0 || (spellInfo.GetEffect(spellEffIndex).Effect != SpellEffectName.ScriptEffect && spellInfo.GetEffect(spellEffIndex).Effect != SpellEffectName.Dummy)) + Log.outError(LogFilter.Sql, $"Table `spell_scripts` - spell {spellId} effect {spellEffIndex} is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY"); } } public void LoadEventScripts() @@ -1473,11 +1478,11 @@ namespace Game SpellInfo spell = Global.SpellMgr.GetSpellInfo(spellNameEntry.Id, Difficulty.None); if (spell != null) { - foreach (SpellEffectInfo effect in spell.GetEffects()) + foreach (var spellEffectInfo in spell.GetEffects()) { - if (effect != null && effect.Effect == SpellEffectName.SendEvent) - if (effect.MiscValue != 0) - evt_scripts.Add((uint)effect.MiscValue); + if (spellEffectInfo.IsEffect(SpellEffectName.SendEvent)) + if (spellEffectInfo.MiscValue != 0) + evt_scripts.Add((uint)spellEffectInfo.MiscValue); } } } @@ -7516,12 +7521,12 @@ namespace Game if (spellInfo == null) continue; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || effect.Effect != SpellEffectName.QuestComplete) + if (spellEffectInfo.Effect != SpellEffectName.QuestComplete) continue; - uint questId = (uint)effect.MiscValue; + uint questId = (uint)spellEffectInfo.MiscValue; Quest quest = GetQuestTemplate(questId); // some quest referenced in spells not exist (outdated spells) diff --git a/Source/Game/Handlers/PetHandler.cs b/Source/Game/Handlers/PetHandler.cs index 6cd04cf81..51c93a7e9 100644 --- a/Source/Game/Handlers/PetHandler.cs +++ b/Source/Game/Handlers/PetHandler.cs @@ -294,9 +294,9 @@ namespace Game return; } - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect != null && (effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || effect.TargetA.GetTarget() == Targets.DestDynobjEnemy)) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || spellEffectInfo.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || spellEffectInfo.TargetA.GetTarget() == Targets.DestDynobjEnemy) return; } diff --git a/Source/Game/Scripting/SpellScript.cs b/Source/Game/Scripting/SpellScript.cs index 17c5beab7..0d4c4dcaf 100644 --- a/Source/Game/Scripting/SpellScript.cs +++ b/Source/Game/Scripting/SpellScript.cs @@ -134,7 +134,7 @@ namespace Game.Scripting public virtual void Register() { } // Function called on server startup, if returns false script won't be used in core // use for: dbc/template data presence/correctness checks - public virtual bool Validate(SpellInfo spellEntry) { return true; } + public virtual bool Validate(SpellInfo spellInfo) { return true; } // Function called when script is created, if returns false script will be unloaded afterwards // use for: initializing local script variables (DO NOT USE CONSTRUCTOR FOR THIS PURPOSE!) public virtual bool Load() { return true; } @@ -194,15 +194,15 @@ namespace Game.Scripting public override bool CheckEffect(SpellInfo spellEntry, uint effIndex) { - SpellEffectInfo effect = spellEntry.GetEffect(effIndex); - if (effect == null) + if (spellEntry.GetEffects().Count <= effIndex) return false; - if (effect.Effect == 0 && _effName == 0) + SpellEffectInfo spellEffectInfo = spellEntry.GetEffect(effIndex); + if (spellEffectInfo.Effect == 0 && _effName == 0) return true; - if (effect.Effect == 0) + if (spellEffectInfo.Effect == 0) return false; - return (_effName == SpellEffectName.Any) || (effect.Effect == _effName); + return (_effName == SpellEffectName.Any) || (spellEffectInfo.Effect == _effName); } public void Call(uint effIndex) @@ -267,16 +267,16 @@ namespace Game.Scripting _dest = dest; } - public override bool CheckEffect(SpellInfo spellEntry, uint effIndex) + public override bool CheckEffect(SpellInfo spellEntry, uint effIndexToCheck) { if (_targetType == 0) return false; - SpellEffectInfo effect = spellEntry.GetEffect(effIndex); - if (effect == null) + if (spellEntry.GetEffects().Count <= effIndexToCheck) return false; - if (effect.TargetA.GetTarget() != _targetType && effect.TargetB.GetTarget() != _targetType) + SpellEffectInfo spellEffectInfo = spellEntry.GetEffect(effIndexToCheck); + if (spellEffectInfo.TargetA.GetTarget() != _targetType && spellEffectInfo.TargetB.GetTarget() != _targetType) return false; SpellImplicitTargetInfo targetInfo = new(_targetType); @@ -896,15 +896,17 @@ namespace Game.Scripting public override bool CheckEffect(SpellInfo spellEntry, uint effIndex) { - SpellEffectInfo effect = spellEntry.GetEffect(effIndex); - if (effect == null) + if (spellEntry.GetEffects().Count <= effIndex) return false; - if (effect.ApplyAuraName == 0 && effAurName == 0) + SpellEffectInfo spellEffectInfo = spellEntry.GetEffect(effIndex); + if (spellEffectInfo.ApplyAuraName == 0 && effAurName == 0) return true; - if (effect.ApplyAuraName == 0) + + if (spellEffectInfo.ApplyAuraName == 0) return false; - return (effAurName == AuraType.Any) || (effect.ApplyAuraName == effAurName); + + return (effAurName == AuraType.Any) || (spellEffectInfo.ApplyAuraName == effAurName); } AuraType effAurName; @@ -1448,6 +1450,12 @@ namespace Game.Scripting // returns proto of the spell public SpellInfo GetSpellInfo() { return m_aura.GetSpellInfo(); } + + public SpellEffectInfo GetEffectInfo(uint effIndex) + { + return m_aura.GetSpellInfo().GetEffect(effIndex); + } + // returns spellid of the spell public uint GetId() { return m_aura.GetId(); } diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index 59b01ed86..abf99b475 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -129,9 +129,9 @@ namespace Game.Spells if (IsSelfcasted() || caster == null || !caster.IsFriendlyTo(GetTarget())) { bool negativeFound = false; - foreach (SpellEffectInfo effect in GetBase().GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetBase().GetSpellInfo().GetEffects()) { - if (effect != null && (Convert.ToBoolean((1 << (int)effect.EffectIndex) & effMask) && !GetBase().GetSpellInfo().IsPositiveEffect(effect.EffectIndex))) + if (((1 << (int)spellEffectInfo.EffectIndex) & effMask) != 0 && !GetBase().GetSpellInfo().IsPositiveEffect(spellEffectInfo.EffectIndex)) { negativeFound = true; break; @@ -144,9 +144,9 @@ namespace Game.Spells else { bool positiveFound = false; - foreach (SpellEffectInfo effect in GetBase().GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetBase().GetSpellInfo().GetEffects()) { - if (effect != null && (Convert.ToBoolean((1 << (int)effect.EffectIndex) & effMask) && GetBase().GetSpellInfo().IsPositiveEffect(effect.EffectIndex))) + if (((1 << (int)spellEffectInfo.EffectIndex) & effMask) != 0 && GetBase().GetSpellInfo().IsPositiveEffect(spellEffectInfo.EffectIndex)) { positiveFound = true; break; @@ -380,10 +380,10 @@ namespace Game.Spells // shouldn't be in constructor - functions in AuraEffect.AuraEffect use polymorphism _effects = new AuraEffect[SpellConst.MaxEffects]; - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect != null && Convert.ToBoolean(effMask & (1 << (int)effect.EffectIndex))) - _effects[effect.EffectIndex] = new AuraEffect(this, effect, baseAmount != null ? baseAmount[effect.EffectIndex] : null, caster); + if ((effMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0) + _effects[spellEffectInfo.EffectIndex] = new AuraEffect(this, spellEffectInfo, baseAmount != null ? baseAmount[spellEffectInfo.EffectIndex] : null, caster); } } @@ -518,10 +518,10 @@ namespace Game.Spells bool addUnit = true; // check target immunities - for (byte effIndex = 0; effIndex < SpellConst.MaxEffects; ++effIndex) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (unit.IsImmunedToSpellEffect(GetSpellInfo(), effIndex, caster)) - value &= ~(1u << effIndex); + if (unit.IsImmunedToSpellEffect(GetSpellInfo(), spellEffectInfo, caster)) + value &= ~(1u << (int)spellEffectInfo.EffectIndex); } @@ -954,9 +954,9 @@ namespace Game.Spells public bool HasMoreThanOneEffectForType(AuraType auraType) { uint count = 0; - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect != null && HasEffect(effect.EffectIndex) && effect.ApplyAuraName == auraType) + if (HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.ApplyAuraName == auraType) ++count; } @@ -965,9 +965,9 @@ namespace Game.Spells public bool IsArea() { - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect != null && HasEffect(effect.EffectIndex) && effect.IsAreaAuraEffect()) + if (HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.IsAreaAuraEffect()) return true; } return false; @@ -1003,12 +1003,12 @@ namespace Game.Spells if (GetCasterGUID() != GetOwner().GetGUID()) { // owner == caster for area auras, check for possible bad data in DB - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; - if (effect.IsTargetingArea() || effect.IsAreaAuraEffect()) + if (spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect()) return false; } @@ -1496,17 +1496,17 @@ namespace Game.Spells if (IsPassive() && sameCaster && (m_spellInfo.IsDifferentRankOf(existingSpellInfo) || (m_spellInfo.Id == existingSpellInfo.Id && m_castItemGuid.IsEmpty()))) return false; - foreach (SpellEffectInfo effect in existingSpellInfo.GetEffects()) + foreach (var spellEffectInfo in existingSpellInfo.GetEffects()) { // prevent remove triggering aura by triggered aura - if (effect != null && effect.TriggerSpell == GetId()) + if (spellEffectInfo.TriggerSpell == GetId()) return true; } - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { // prevent remove triggered aura by triggering aura refresh - if (effect != null && effect.TriggerSpell == existingAura.GetId()) + if (spellEffectInfo.TriggerSpell == existingAura.GetId()) return true; } @@ -1544,35 +1544,40 @@ namespace Game.Spells return true; // check same periodic auras - for (byte i = 0; i < SpellConst.MaxEffects; i++) + bool hasPeriodicNonAreaEffect(SpellInfo spellInfo) { - SpellEffectInfo effect = m_spellInfo.GetEffect(i); - if (effect == null) - continue; - - switch (effect.ApplyAuraName) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - // DOT or HOT from different casters will stack - case AuraType.PeriodicDamage: - case AuraType.PeriodicDummy: - case AuraType.PeriodicHeal: - case AuraType.PeriodicTriggerSpell: - case AuraType.PeriodicEnergize: - case AuraType.PeriodicManaLeech: - case AuraType.PeriodicLeech: - case AuraType.PowerBurn: - case AuraType.ObsModPower: - case AuraType.ObsModHealth: - case AuraType.PeriodicTriggerSpellWithValue: - SpellEffectInfo existingEffect = m_spellInfo.GetEffect(i); - // periodic auras which target areas are not allowed to stack this way (replenishment for example) - if (effect.IsTargetingArea() || (existingEffect != null && existingEffect.IsTargetingArea())) + switch (spellEffectInfo.ApplyAuraName) + { + // DOT or HOT from different casters will stack + case AuraType.PeriodicDamage: + case AuraType.PeriodicDummy: + case AuraType.PeriodicHeal: + case AuraType.PeriodicTriggerSpell: + case AuraType.PeriodicEnergize: + case AuraType.PeriodicManaLeech: + case AuraType.PeriodicLeech: + case AuraType.PowerBurn: + case AuraType.ObsModPower: + case AuraType.ObsModHealth: + case AuraType.PeriodicTriggerSpellWithValue: + { + // periodic auras which target areas are not allowed to stack this way (replenishment for example) + if (spellEffectInfo.IsTargetingArea()) + return false; + + return true; + } + default: break; - return true; - default: - break; + } } + return false; } + + if (hasPeriodicNonAreaEffect(m_spellInfo) && hasPeriodicNonAreaEffect(existingSpellInfo)) + return true; } if (HasEffectType(AuraType.ControlVehicle) && existingAura.HasEffectType(AuraType.ControlVehicle)) @@ -2398,17 +2403,17 @@ namespace Game.Spells { case TypeId.Unit: case TypeId.Player: - foreach (SpellEffectInfo effect in spellProto.GetEffects()) + foreach (var spellEffectInfo in spellProto.GetEffects()) { - if (effect != null && effect.IsUnitOwnedAuraEffect()) - effMask |= (uint)(1 << (int)effect.EffectIndex); + if (spellEffectInfo.IsUnitOwnedAuraEffect()) + effMask |= (1u << (int)spellEffectInfo.EffectIndex); } break; case TypeId.DynamicObject: - foreach (SpellEffectInfo effect in spellProto.GetEffects()) + foreach (var spellEffectInfo in spellProto.GetEffects()) { - if (effect != null && effect.Effect == SpellEffectName.PersistentAreaAura) - effMask |= (uint)(1 << (int)effect.EffectIndex); + if (spellEffectInfo.Effect == SpellEffectName.PersistentAreaAura) + effMask |= (1u << (int)spellEffectInfo.EffectIndex); } break; default: @@ -2627,13 +2632,13 @@ namespace Game.Spells targets.Add(target, targetPair.Value); } - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect == null || !HasEffect(effect.EffectIndex)) + if (!HasEffect(spellEffectInfo.EffectIndex)) continue; // area auras only - if (effect.Effect == SpellEffectName.ApplyAura) + if (spellEffectInfo.Effect == SpellEffectName.ApplyAura) continue; // skip area update if owner is not in world! @@ -2644,11 +2649,11 @@ namespace Game.Spells continue; List units = new(); - var condList = effect.ImplicitTargetConditions; + var condList = spellEffectInfo.ImplicitTargetConditions; - float radius = effect.CalcRadius(refe); + float radius = spellEffectInfo.CalcRadius(refe); SpellTargetCheckTypes selectionType = SpellTargetCheckTypes.Default; - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.ApplyAreaAuraParty: case SpellEffectName.ApplyAreaAuraPartyNonrandom: @@ -2709,7 +2714,7 @@ namespace Game.Spells if (!targets.ContainsKey(unit)) targets[unit] = 0; - targets[unit] |= 1u << (int)effect.EffectIndex; + targets[unit] |= 1u << (int)spellEffectInfo.EffectIndex; } } } @@ -2717,10 +2722,10 @@ namespace Game.Spells public void AddStaticApplication(Unit target, uint effMask) { // only valid for non-area auras - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect != null && (effMask & (1u << (int)effect.EffectIndex)) != 0 && effect.Effect != SpellEffectName.ApplyAura) - effMask &= ~(1u << (int)effect.EffectIndex); + if ((effMask & (1u << (int)spellEffectInfo.EffectIndex)) != 0 && spellEffectInfo.IsEffect(SpellEffectName.ApplyAura)) + effMask &= ~(1u << (int)spellEffectInfo.EffectIndex); } if (effMask == 0) @@ -2765,18 +2770,18 @@ namespace Game.Spells Unit dynObjOwnerCaster = GetDynobjOwner().GetCaster(); float radius = GetDynobjOwner().GetRadius(); - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) { - if (effect == null || !HasEffect(effect.EffectIndex)) + if (!HasEffect(spellEffectInfo.EffectIndex)) continue; // we can't use effect type like area auras to determine check type, check targets - SpellTargetCheckTypes selectionType = effect.TargetA.GetCheckType(); - if (effect.TargetB.GetReferenceType() == SpellTargetReferenceTypes.Dest) - selectionType = effect.TargetB.GetCheckType(); + SpellTargetCheckTypes selectionType = spellEffectInfo.TargetA.GetCheckType(); + if (spellEffectInfo.TargetB.GetReferenceType() == SpellTargetReferenceTypes.Dest) + selectionType = spellEffectInfo.TargetB.GetCheckType(); List targetList = new(); - var condList = effect.ImplicitTargetConditions; + var condList = spellEffectInfo.ImplicitTargetConditions; WorldObjectSpellAreaTargetCheck check = new(radius, GetDynobjOwner(), dynObjOwnerCaster, dynObjOwnerCaster, GetSpellInfo(), selectionType, condList, SpellTargetObjectTypes.Unit); UnitListSearcher searcher = new(GetDynobjOwner(), targetList, check); @@ -2790,7 +2795,7 @@ namespace Game.Spells if (!targets.ContainsKey(unit)) targets[unit] = 0; - targets[unit] |= 1u << (int)effect.EffectIndex; + targets[unit] |= 1u << (int)spellEffectInfo.EffectIndex; } } } diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 069824c3e..f92bea320 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -2097,8 +2097,8 @@ namespace Game.Spells } //some spell has one aura of mount and one of vehicle - foreach (SpellEffectInfo effect in GetSpellInfo().GetEffects()) - if (effect != null && GetSpellEffectInfo().Effect == SpellEffectName.Summon && effect.MiscValue == GetMiscValue()) + foreach (var spellEffectInfo in GetSpellInfo().GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.Summon) && spellEffectInfo.MiscValue == GetMiscValue()) displayId = 0; } @@ -4910,7 +4910,7 @@ namespace Game.Spells // Consecrate ticks can miss and will not show up in the combat log // dynobj auras must always have a caster - if (GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura && + if (GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) && caster.SpellHitResult(target, GetSpellInfo(), false) != SpellMissInfo.None) return; @@ -4982,7 +4982,7 @@ namespace Game.Spells damage = Unit.SpellCriticalDamageBonus(caster, m_spellInfo, damage, target); // Calculate armor mitigation - if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex())) + if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), GetSpellEffectInfo())) { uint damageReducedArmor = Unit.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetAttackType(), GetBase().GetCasterLevel()); cleanDamage.mitigated_damage += damage - damageReducedArmor; @@ -4991,7 +4991,7 @@ namespace Game.Spells if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage)) { - if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura) + if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura)) damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, GetBase().GetCastItemGUID()); } @@ -5043,7 +5043,7 @@ namespace Game.Spells } // dynobj auras must always have a caster - if (GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura && + if (GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) && caster.SpellHitResult(target, GetSpellInfo(), false) != SpellMissInfo.None) return; @@ -5064,7 +5064,7 @@ namespace Game.Spells damage = Unit.SpellCriticalDamageBonus(caster, m_spellInfo, damage, target); // Calculate armor mitigation - if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), (sbyte)GetEffIndex())) + if (Unit.IsDamageReducedByArmor(GetSpellInfo().GetSchoolMask(), GetSpellInfo(), GetSpellEffectInfo())) { uint damageReducedArmor = Unit.CalcArmorReducedDamage(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetAttackType(), GetBase().GetCasterLevel()); cleanDamage.mitigated_damage += damage - damageReducedArmor; @@ -5073,7 +5073,7 @@ namespace Game.Spells if (!GetSpellInfo().HasAttribute(SpellAttr4.FixedDamage)) { - if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura) + if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura)) damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, GetBase().GetCastItemGUID()); } @@ -5236,7 +5236,7 @@ namespace Game.Spells return; } - if (GetSpellEffectInfo().Effect == SpellEffectName.PersistentAreaAura && + if (GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) && caster.SpellHitResult(target, GetSpellInfo(), false) != SpellMissInfo.None) return; @@ -5648,11 +5648,11 @@ namespace Game.Spells else { List summonedEntries = new(); - foreach (var spellEffect in triggerSpellInfo.GetEffects()) + foreach (var spellEffectInfo in triggerSpellInfo.GetEffects()) { - if (spellEffect != null && spellEffect.Effect == SpellEffectName.Summon) + if (spellEffectInfo.IsEffect(SpellEffectName.Summon)) { - uint summonEntry = (uint)spellEffect.MiscValue; + uint summonEntry = (uint)spellEffectInfo.MiscValue; if (summonEntry != 0) summonedEntries.Add(summonEntry); diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index f08c83e48..2d534f45e 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -241,35 +241,32 @@ namespace Game.Spells SelectExplicitTargets(); uint processedAreaEffectsMask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - // not call for empty effect. // Also some spells use not used effect targets for store targets for dummy effect in triggered spells - if (!effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; // set expected type of implicit targets to be sent to client - SpellCastTargetFlags implicitTargetMask = SpellInfo.GetTargetFlagMask(effect.TargetA.GetObjectType()) | SpellInfo.GetTargetFlagMask(effect.TargetB.GetObjectType()); + SpellCastTargetFlags implicitTargetMask = SpellInfo.GetTargetFlagMask(spellEffectInfo.TargetA.GetObjectType()) | SpellInfo.GetTargetFlagMask(spellEffectInfo.TargetB.GetObjectType()); if (Convert.ToBoolean(implicitTargetMask & SpellCastTargetFlags.Unit)) m_targets.SetTargetFlag(SpellCastTargetFlags.Unit); if (Convert.ToBoolean(implicitTargetMask & (SpellCastTargetFlags.Gameobject | SpellCastTargetFlags.GameobjectItem))) m_targets.SetTargetFlag(SpellCastTargetFlags.Gameobject); - SelectEffectImplicitTargets(effect.EffectIndex, effect.TargetA, ref processedAreaEffectsMask); - SelectEffectImplicitTargets(effect.EffectIndex, effect.TargetB, ref processedAreaEffectsMask); + SelectEffectImplicitTargets(spellEffectInfo, spellEffectInfo.TargetA, ref processedAreaEffectsMask); + SelectEffectImplicitTargets(spellEffectInfo, spellEffectInfo.TargetB, ref processedAreaEffectsMask); // Select targets of effect based on effect type // those are used when no valid target could be added for spell effect based on spell target type // some spell effects use explicit target as a default target added to target map (like SPELL_EFFECT_LEARN_SPELL) // some spell effects add target to target map only when target type specified (like SPELL_EFFECT_WEAPON) // some spell effects don't add anything to target map (confirmed with sniffs) (like SPELL_EFFECT_DESTROY_ALL_TOTEMS) - SelectEffectTypeImplicitTargets(effect.EffectIndex); + SelectEffectTypeImplicitTargets(spellEffectInfo); if (m_targets.HasDst()) - AddDestTarget(m_targets.GetDst(), effect.EffectIndex); + AddDestTarget(m_targets.GetDst(), spellEffectInfo.EffectIndex); if (m_spellInfo.IsChanneled()) { @@ -281,7 +278,7 @@ namespace Game.Spells return; } - uint mask = (1u << (int)effect.EffectIndex); + uint mask = (1u << (int)spellEffectInfo.EffectIndex); foreach (var ihit in m_UniqueTargetInfo) { if (Convert.ToBoolean(ihit.EffectMask & mask)) @@ -328,12 +325,12 @@ namespace Game.Spells m_caster.m_Events.ModifyEventTime(_spellEvent, GetDelayStart() + m_delayMoment); } - void SelectEffectImplicitTargets(uint effIndex, SpellImplicitTargetInfo targetType, ref uint processedEffectMask) + void SelectEffectImplicitTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, ref uint processedEffectMask) { if (targetType.GetTarget() == 0) return; - uint effectMask = (uint)(1 << (int)effIndex); + uint effectMask = (1u << (int)spellEffectInfo.EffectIndex); // set the same target list for all effects // some spells appear to need this, however this requires more research switch (targetType.GetSelectionCategory()) @@ -342,31 +339,28 @@ namespace Game.Spells case SpellTargetSelectionCategories.Cone: case SpellTargetSelectionCategories.Area: case SpellTargetSelectionCategories.Line: + { // targets for effect already selected if (Convert.ToBoolean(effectMask & processedEffectMask)) return; - SpellEffectInfo _effect = m_spellInfo.GetEffect(effIndex); - if (_effect != null) - { - // choose which targets we can select at once - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - { - if (effect == null || effect.EffectIndex <= effIndex) - continue; - if (effect.IsEffect() && - _effect.TargetA.GetTarget() == effect.TargetA.GetTarget() && - _effect.TargetB.GetTarget() == effect.TargetB.GetTarget() && - _effect.ImplicitTargetConditions == effect.ImplicitTargetConditions && - _effect.CalcRadius(m_caster) == effect.CalcRadius(m_caster) && - CheckScriptEffectImplicitTargets(effIndex, effect.EffectIndex)) - { - effectMask |= (uint)(1 << (int)effect.EffectIndex); - } + var effects = GetSpellInfo().GetEffects(); + // choose which targets we can select at once + for (int j = (int)spellEffectInfo.EffectIndex + 1; j < effects.Count; ++j) + { + if (effects[j].IsEffect() && + spellEffectInfo.TargetA.GetTarget() == effects[j].TargetA.GetTarget() && + spellEffectInfo.TargetB.GetTarget() == effects[j].TargetB.GetTarget() && + spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions && + spellEffectInfo.CalcRadius(m_caster) == effects[j].CalcRadius(m_caster) && + CheckScriptEffectImplicitTargets(spellEffectInfo.EffectIndex, (uint)j)) + { + effectMask |= 1u << j; } } processedEffectMask |= effectMask; break; + } default: break; } @@ -374,25 +368,25 @@ namespace Game.Spells switch (targetType.GetSelectionCategory()) { case SpellTargetSelectionCategories.Channel: - SelectImplicitChannelTargets(effIndex, targetType); + SelectImplicitChannelTargets(spellEffectInfo, targetType); break; case SpellTargetSelectionCategories.Nearby: - SelectImplicitNearbyTargets(effIndex, targetType, effectMask); + SelectImplicitNearbyTargets(spellEffectInfo, targetType, effectMask); break; case SpellTargetSelectionCategories.Cone: - SelectImplicitConeTargets(effIndex, targetType, effectMask); + SelectImplicitConeTargets(spellEffectInfo, targetType, effectMask); break; case SpellTargetSelectionCategories.Area: - SelectImplicitAreaTargets(effIndex, targetType, effectMask); + SelectImplicitAreaTargets(spellEffectInfo, targetType, effectMask); break; case SpellTargetSelectionCategories.Traj: // just in case there is no dest, explanation in SelectImplicitDestDestTargets CheckDst(); - SelectImplicitTrajTargets(effIndex, targetType); + SelectImplicitTrajTargets(spellEffectInfo, targetType); break; case SpellTargetSelectionCategories.Line: - SelectImplicitLineTargets(effIndex, targetType, effectMask); + SelectImplicitLineTargets(spellEffectInfo, targetType, effectMask); break; case SpellTargetSelectionCategories.Default: switch (targetType.GetObjectType()) @@ -412,13 +406,13 @@ namespace Game.Spells switch (targetType.GetReferenceType()) { case SpellTargetReferenceTypes.Caster: - SelectImplicitCasterDestTargets(effIndex, targetType); + SelectImplicitCasterDestTargets(spellEffectInfo, targetType); break; case SpellTargetReferenceTypes.Target: - SelectImplicitTargetDestTargets(effIndex, targetType); + SelectImplicitTargetDestTargets(spellEffectInfo, targetType); break; case SpellTargetReferenceTypes.Dest: - SelectImplicitDestDestTargets(effIndex, targetType); + SelectImplicitDestDestTargets(spellEffectInfo, targetType); break; default: Cypher.Assert(false, "Spell.SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT_DEST"); @@ -429,10 +423,10 @@ namespace Game.Spells switch (targetType.GetReferenceType()) { case SpellTargetReferenceTypes.Caster: - SelectImplicitCasterObjectTargets(effIndex, targetType); + SelectImplicitCasterObjectTargets(spellEffectInfo, targetType); break; case SpellTargetReferenceTypes.Target: - SelectImplicitTargetObjectTargets(effIndex, targetType); + SelectImplicitTargetObjectTargets(spellEffectInfo, targetType); break; default: Cypher.Assert(false, "Spell.SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT"); @@ -442,7 +436,7 @@ namespace Game.Spells } break; case SpellTargetSelectionCategories.Nyi: - Log.outDebug(LogFilter.Spells, "SPELL: target type {0}, found in spellID {1}, effect {2} is not implemented yet!", m_spellInfo.Id, effIndex, targetType.GetTarget()); + Log.outDebug(LogFilter.Spells, "SPELL: target type {0}, found in spellID {1}, effect {2} is not implemented yet!", m_spellInfo.Id, spellEffectInfo.EffectIndex, targetType.GetTarget()); break; default: Cypher.Assert(false, "Spell.SelectEffectImplicitTargets: received not implemented select target category"); @@ -450,7 +444,7 @@ namespace Game.Spells } } - void SelectImplicitChannelTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitChannelTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { if (targetType.GetReferenceType() != SpellTargetReferenceTypes.Caster) { @@ -461,9 +455,8 @@ namespace Game.Spells Spell channeledSpell = m_originalCaster.GetCurrentSpell(CurrentSpellTypes.Channeled); if (channeledSpell == null) { - Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitChannelTargets: cannot find channel spell for spell ID {0}, effect {1}", m_spellInfo.Id, effIndex); + Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitChannelTargets: cannot find channel spell for spell ID {0}, effect {1}", m_spellInfo.Id, spellEffectInfo.EffectIndex); return; - } switch (targetType.GetTarget()) @@ -473,13 +466,13 @@ namespace Game.Spells foreach (ObjectGuid channelTarget in m_originalCaster.m_unitData.ChannelObjects) { WorldObject target = Global.ObjAccessor.GetUnit(m_caster, channelTarget); - CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, targetType); // unit target may be no longer avalible - teleported out of map for example Unit unitTarget = target ? target.ToUnit() : null; if (unitTarget) - AddUnitTarget(unitTarget, 1u << (int)effIndex); + AddUnitTarget(unitTarget, 1u << (int)spellEffectInfo.EffectIndex); else - Log.outDebug(LogFilter.Spells, "SPELL: cannot find channel spell target for spell ID {0}, effect {1}", m_spellInfo.Id, effIndex); + Log.outDebug(LogFilter.Spells, "SPELL: cannot find channel spell target for spell ID {0}, effect {1}", m_spellInfo.Id, spellEffectInfo.EffectIndex); } break; } @@ -493,23 +486,23 @@ namespace Game.Spells WorldObject target = !channelObjects.Empty() ? Global.ObjAccessor.GetWorldObject(m_caster, channelObjects[0]) : null; if (target != null) { - CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, targetType); if (target) { SpellDestination dest = new(target); - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); } } else - Log.outDebug(LogFilter.Spells, "SPELL: cannot find channel spell destination for spell ID {0}, effect {1}", m_spellInfo.Id, effIndex); + Log.outDebug(LogFilter.Spells, "SPELL: cannot find channel spell destination for spell ID {0}, effect {1}", m_spellInfo.Id, spellEffectInfo.EffectIndex); } break; } case Targets.DestChannelCaster: { SpellDestination dest = new(channeledSpell.GetCaster()); - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); break; } @@ -519,7 +512,7 @@ namespace Game.Spells } } - void SelectImplicitNearbyTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) + void SelectImplicitNearbyTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, uint effMask) { if (targetType.GetReferenceType() != SpellTargetReferenceTypes.Caster) { @@ -527,10 +520,6 @@ namespace Game.Spells return; } - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; - float range = 0.0f; switch (targetType.GetCheckType()) { @@ -552,12 +541,12 @@ namespace Game.Spells break; } - List condList = effect.ImplicitTargetConditions; + List condList = spellEffectInfo.ImplicitTargetConditions; // handle emergency case - try to use other provided targets if no conditions provided if (targetType.GetCheckType() == SpellTargetCheckTypes.Entry && (condList == null || condList.Empty())) { - Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitNearbyTargets: no conditions entry for target with TARGET_CHECK_ENTRY of spell ID {0}, effect {1} - selecting default targets", m_spellInfo.Id, effIndex); + Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitNearbyTargets: no conditions entry for target with TARGET_CHECK_ENTRY of spell ID {0}, effect {1} - selecting default targets", m_spellInfo.Id, spellEffectInfo.EffectIndex); switch (targetType.GetObjectType()) { case SpellTargetObjectTypes.Gobj: @@ -579,7 +568,7 @@ namespace Game.Spells if (focusObject != null) { SpellDestination dest = new(focusObject); - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); } else @@ -598,16 +587,16 @@ namespace Game.Spells WorldObject target = SearchNearbyTarget(range, targetType.GetObjectType(), targetType.GetCheckType(), condList); if (target == null) { - Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitNearbyTargets: cannot find nearby target for spell ID {0}, effect {1}", m_spellInfo.Id, effIndex); + Log.outDebug(LogFilter.Spells, "Spell.SelectImplicitNearbyTargets: cannot find nearby target for spell ID {0}, effect {1}", m_spellInfo.Id, spellEffectInfo.EffectIndex); SendCastResult(SpellCastResult.BadImplicitTargets); Finish(false); return; } - CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, targetType); if (!target) { - Log.outDebug(LogFilter.Spells, $"Spell.SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {m_spellInfo.Id} set NULL target, effect {effIndex}"); + Log.outDebug(LogFilter.Spells, $"Spell.SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {m_spellInfo.Id} set NULL target, effect {spellEffectInfo.EffectIndex}"); SendCastResult(SpellCastResult.BadImplicitTargets); Finish(false); return; @@ -641,7 +630,7 @@ namespace Game.Spells break; case SpellTargetObjectTypes.Dest: SpellDestination dest = new(target); - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); break; default: @@ -649,10 +638,10 @@ namespace Game.Spells break; } - SelectImplicitChainTargets(effIndex, targetType, target, effMask); + SelectImplicitChainTargets(spellEffectInfo, targetType, target, effMask); } - void SelectImplicitConeTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) + void SelectImplicitConeTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, uint effMask) { if (targetType.GetReferenceType() != SpellTargetReferenceTypes.Caster) { @@ -662,12 +651,9 @@ namespace Game.Spells List targets = new(); SpellTargetObjectTypes objectType = targetType.GetObjectType(); SpellTargetCheckTypes selectionType = targetType.GetCheckType(); - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; - var condList = effect.ImplicitTargetConditions; - float radius = effect.CalcRadius(m_caster) * m_spellValue.RadiusMod; + var condList = spellEffectInfo.ImplicitTargetConditions; + float radius = spellEffectInfo.CalcRadius(m_caster) * m_spellValue.RadiusMod; GridMapTypeMask containerTypeMask = GetSearcherTypeMask(objectType, condList); if (containerTypeMask != 0) @@ -676,7 +662,7 @@ namespace Game.Spells var searcher = new WorldObjectListSearcher(m_caster, targets, spellCone, containerTypeMask); SearchTargets(searcher, containerTypeMask, m_caster, m_caster.GetPosition(), radius); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType); if (!targets.Empty()) { @@ -698,7 +684,7 @@ namespace Game.Spells } } - void SelectImplicitAreaTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) + void SelectImplicitAreaTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, uint effMask) { WorldObject referer = null; switch (targetType.GetReferenceType()) @@ -716,7 +702,7 @@ namespace Game.Spells // find last added target for this effect foreach (var target in m_UniqueTargetInfo) { - if (Convert.ToBoolean(target.EffectMask & (1 << (int)effIndex))) + if (Convert.ToBoolean(target.EffectMask & (1 << (int)spellEffectInfo.EffectIndex))) { referer = Global.ObjAccessor.GetUnit(m_caster, target.TargetGUID); break; @@ -750,9 +736,6 @@ namespace Game.Spells return; } List targets = new(); - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; switch (targetType.GetTarget()) { @@ -764,7 +747,7 @@ namespace Game.Spells { targets.Add(m_targets.GetUnitTarget()); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType); if (!targets.Empty()) { @@ -800,11 +783,11 @@ namespace Game.Spells break; } - float radius = effect.CalcRadius(m_caster) * m_spellValue.RadiusMod; + float radius = spellEffectInfo.CalcRadius(m_caster) * m_spellValue.RadiusMod; - SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), effect.ImplicitTargetConditions); + SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType); if (!targets.Empty()) { @@ -825,7 +808,7 @@ namespace Game.Spells } } - void SelectImplicitCasterDestTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitCasterDestTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { SpellDestination dest = new(m_caster); @@ -839,7 +822,7 @@ namespace Game.Spells dest = new SpellDestination(playerCaster.GetHomebind().posX, playerCaster.GetHomebind().posY, playerCaster.GetHomebind().posZ, playerCaster.GetOrientation(), playerCaster.GetHomebind().GetMapId()); break; case Targets.DestDb: - SpellTargetPosition st = Global.SpellMgr.GetSpellTargetPosition(m_spellInfo.Id, effIndex); + SpellTargetPosition st = Global.SpellMgr.GetSpellTargetPosition(m_spellInfo.Id, spellEffectInfo.EffectIndex); if (st != null) { // @todo fix this check @@ -896,7 +879,7 @@ namespace Game.Spells if (unitCaster == null) break; - float dist = m_spellInfo.GetEffect(effIndex).CalcRadius(unitCaster); + float dist = spellEffectInfo.CalcRadius(unitCaster); float angle = targetType.CalcDirectionAngle(); Position pos = new(dest.Position); @@ -939,51 +922,49 @@ namespace Game.Spells break; } default: - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect != null) + { + float dist = spellEffectInfo.CalcRadius(m_caster); + float angl = targetType.CalcDirectionAngle(); + float objSize = m_caster.GetCombatReach(); + + switch (targetType.GetTarget()) { - float dist = effect.CalcRadius(m_caster); - float angl = targetType.CalcDirectionAngle(); - float objSize = m_caster.GetCombatReach(); - - switch (targetType.GetTarget()) + case Targets.DestCasterSummon: + dist = SharedConst.PetFollowDist; + break; + case Targets.DestCasterRandom: + if (dist > objSize) + dist = objSize + (dist - objSize) * (float)RandomHelper.NextDouble(); + break; + case Targets.DestCasterFrontLeft: + case Targets.DestCasterBackLeft: + case Targets.DestCasterFrontRight: + case Targets.DestCasterBackRight: { - case Targets.DestCasterSummon: - dist = SharedConst.PetFollowDist; - break; - case Targets.DestCasterRandom: - if (dist > objSize) - dist = objSize + (dist - objSize) * (float)RandomHelper.NextDouble(); - break; - case Targets.DestCasterFrontLeft: - case Targets.DestCasterBackLeft: - case Targets.DestCasterFrontRight: - case Targets.DestCasterBackRight: - { - float DefaultTotemDistance = 3.0f; - if (!effect.HasRadius() && !effect.HasMaxRadius()) - dist = DefaultTotemDistance; - break; - } - default: - break; + float DefaultTotemDistance = 3.0f; + if (!spellEffectInfo.HasRadius() && !spellEffectInfo.HasMaxRadius()) + dist = DefaultTotemDistance; + break; } - - if (dist < objSize) - dist = objSize; - - Position pos = new(dest.Position); - m_caster.MovePositionToFirstCollision(pos, dist, angl); - - dest.Relocate(pos); + default: + break; } + + if (dist < objSize) + dist = objSize; + + Position pos = new(dest.Position); + m_caster.MovePositionToFirstCollision(pos, dist, angl); + + dest.Relocate(pos); break; + } } - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); } - void SelectImplicitTargetDestTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitTargetDestTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { WorldObject target = m_targets.GetObjectTarget(); @@ -996,27 +977,25 @@ namespace Game.Spells case Targets.DestTargetAlly: break; default: - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect != null) - { - float angle = targetType.CalcDirectionAngle(); - float dist = effect.CalcRadius(null); - if (targetType.GetTarget() == Targets.DestRandom) - dist *= (float)RandomHelper.NextDouble(); + { + float angle = targetType.CalcDirectionAngle(); + float dist = spellEffectInfo.CalcRadius(null); + if (targetType.GetTarget() == Targets.DestRandom) + dist *= (float)RandomHelper.NextDouble(); - Position pos = new(dest.Position); - target.MovePositionToFirstCollision(pos, dist, angle); + Position pos = new(dest.Position); + target.MovePositionToFirstCollision(pos, dist, angle); - dest.Relocate(pos); - } - break; + dest.Relocate(pos); + } + break; } - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.SetDst(dest); } - void SelectImplicitDestDestTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitDestDestTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { // set destination to caster if no dest provided // can only happen if previous destination target could not be set for some reason @@ -1034,27 +1013,25 @@ namespace Game.Spells case Targets.DestDest: return; default: - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect != null) - { - float angle = targetType.CalcDirectionAngle(); - float dist = effect.CalcRadius(m_caster); - if (targetType.GetTarget() == Targets.DestRandom) - dist *= (float)RandomHelper.NextDouble(); + { + float angle = targetType.CalcDirectionAngle(); + float dist = spellEffectInfo.CalcRadius(m_caster); + if (targetType.GetTarget() == Targets.DestRandom) + dist *= (float)RandomHelper.NextDouble(); - Position pos = new(m_targets.GetDstPos()); - m_caster.MovePositionToFirstCollision(pos, dist, angle); + Position pos = new(m_targets.GetDstPos()); + m_caster.MovePositionToFirstCollision(pos, dist, angle); - dest.Relocate(pos); - } - break; + dest.Relocate(pos); + } + break; } - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.ModDst(dest); } - void SelectImplicitCasterObjectTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitCasterObjectTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { WorldObject target = null; bool checkIfValid = true; @@ -1113,50 +1090,46 @@ namespace Game.Spells break; } - CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, targetType); if (target) { Unit unit = target.ToUnit(); if (unit != null) - AddUnitTarget(unit, 1u << (int)effIndex, checkIfValid); + AddUnitTarget(unit, 1u << (int)spellEffectInfo.EffectIndex, checkIfValid); else { GameObject go = target.ToGameObject(); if (go != null) - AddGOTarget(go, 1u << (int)effIndex); + AddGOTarget(go, 1u << (int)spellEffectInfo.EffectIndex); } } } - void SelectImplicitTargetObjectTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitTargetObjectTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { WorldObject target = m_targets.GetObjectTarget(); - CallScriptObjectTargetSelectHandlers(ref target, effIndex, targetType); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, targetType); Item item = m_targets.GetItemTarget(); if (target != null) { if (target.ToUnit()) - AddUnitTarget(target.ToUnit(), (uint)(1 << (int)effIndex), true, false); + AddUnitTarget(target.ToUnit(), 1u << (int)spellEffectInfo.EffectIndex, true, false); else if (target.IsTypeId(TypeId.GameObject)) - AddGOTarget(target.ToGameObject(), (uint)(1 << (int)effIndex)); + AddGOTarget(target.ToGameObject(), 1u << (int)spellEffectInfo.EffectIndex); - SelectImplicitChainTargets(effIndex, targetType, target, (uint)(1 << (int)effIndex)); + SelectImplicitChainTargets(spellEffectInfo, targetType, target, 1u << (int)spellEffectInfo.EffectIndex); } // Script hook can remove object target and we would wrongly land here else if (item != null) - AddItemTarget(item, (uint)(1 << (int)effIndex)); + AddItemTarget(item, 1u << (int)spellEffectInfo.EffectIndex); } - void SelectImplicitChainTargets(uint effIndex, SpellImplicitTargetInfo targetType, WorldObject target, uint effMask) + void SelectImplicitChainTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, WorldObject target, uint effMask) { - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; - - int maxTargets = effect.ChainTargets; + int maxTargets = spellEffectInfo.ChainTargets; Player modOwner = m_caster.GetSpellModOwner(); if (modOwner) modOwner.ApplySpellMod(m_spellInfo, SpellModOp.ChainTargets, ref maxTargets, this); @@ -1164,16 +1137,17 @@ namespace Game.Spells if (maxTargets > 1) { // mark damage multipliers as used - foreach (SpellEffectInfo eff in m_spellInfo.GetEffects()) - if (eff != null && Convert.ToBoolean(effMask & (1 << (int)eff.EffectIndex))) - m_damageMultipliers[eff.EffectIndex] = 1.0f; + for (int k = (int)spellEffectInfo.EffectIndex; k < m_spellInfo.GetEffects().Count; ++k) + if (Convert.ToBoolean(effMask & (1 << (int)k))) + m_damageMultipliers[spellEffectInfo.EffectIndex] = 1.0f; + m_applyMultiplierMask |= effMask; List targets = new(); - SearchChainTargets(targets, (uint)maxTargets - 1, target, targetType.GetObjectType(), targetType.GetCheckType(), effect.ImplicitTargetConditions, targetType.GetTarget() == Targets.UnitChainhealAlly); + SearchChainTargets(targets, (uint)maxTargets - 1, target, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions, targetType.GetTarget() == Targets.UnitChainhealAlly); // Chain primary target is added earlier - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType); foreach (var obj in targets) { @@ -1193,7 +1167,7 @@ namespace Game.Spells return 0.0f; } - void SelectImplicitTrajTargets(uint effIndex, SpellImplicitTargetInfo targetType) + void SelectImplicitTrajTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType) { if (!m_targets.HasTraj()) return; @@ -1206,9 +1180,8 @@ namespace Game.Spells srcPos.SetOrientation(m_caster.GetOrientation()); float srcToDestDelta = m_targets.GetDstPos().posZ - srcPos.posZ; - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); List targets = new(); - var spellTraj = new WorldObjectSpellTrajTargetCheck(dist2d, srcPos, m_caster, m_spellInfo, targetType.GetCheckType(), effect.ImplicitTargetConditions, SpellTargetObjectTypes.None); + var spellTraj = new WorldObjectSpellTrajTargetCheck(dist2d, srcPos, m_caster, m_spellInfo, targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions, SpellTargetObjectTypes.None); var searcher = new WorldObjectListSearcher(m_caster, targets, spellTraj); SearchTargets(searcher, GridMapTypeMask.All, m_caster, srcPos, dist2d); if (targets.Empty()) @@ -1224,7 +1197,7 @@ namespace Game.Spells // We should check if triggered spell has greater range (which is true in many cases, and initial spell has too short max range) // limit max range to 300 yards, sometimes triggered spells can have 50000yds float bestDist = m_spellInfo.GetMaxRange(false); - SpellInfo triggerSpellInfo = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, GetCastDifficulty()); + SpellInfo triggerSpellInfo = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, GetCastDifficulty()); if (triggerSpellInfo != null) bestDist = Math.Min(Math.Max(bestDist, triggerSpellInfo.GetMaxRange(false)), Math.Min(dist2d, 300.0f)); @@ -1276,19 +1249,16 @@ namespace Game.Spells float z = m_targets.GetSrcPos().posZ + bestDist * (a * bestDist + b); SpellDestination dest = new(x, y, z, unitCaster.GetOrientation()); - CallScriptDestinationTargetSelectHandlers(ref dest, effIndex, targetType); + CallScriptDestinationTargetSelectHandlers(ref dest, spellEffectInfo.EffectIndex, targetType); m_targets.ModDst(dest); } } - void SelectImplicitLineTargets(uint effIndex, SpellImplicitTargetInfo targetType, uint effMask) + void SelectImplicitLineTargets(SpellEffectInfo spellEffectInfo, SpellImplicitTargetInfo targetType, uint effMask) { List targets = new(); SpellTargetObjectTypes objectType = targetType.GetObjectType(); SpellTargetCheckTypes selectionType = targetType.GetCheckType(); - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; Position dst; switch (targetType.GetReferenceType()) @@ -1310,8 +1280,8 @@ namespace Game.Spells return; } - var condList = effect.ImplicitTargetConditions; - float radius = effect.CalcRadius(m_caster) * m_spellValue.RadiusMod; + var condList = spellEffectInfo.ImplicitTargetConditions; + float radius = spellEffectInfo.CalcRadius(m_caster) * m_spellValue.RadiusMod; GridMapTypeMask containerTypeMask = GetSearcherTypeMask(objectType, condList); if (containerTypeMask != 0) @@ -1320,7 +1290,7 @@ namespace Game.Spells WorldObjectListSearcher searcher = new(m_caster, targets, check, containerTypeMask); SearchTargets(searcher, containerTypeMask, m_caster, m_caster, radius); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType); if (!targets.Empty()) { @@ -1351,13 +1321,10 @@ namespace Game.Spells } } - void SelectEffectTypeImplicitTargets(uint effIndex) + void SelectEffectTypeImplicitTargets(SpellEffectInfo spellEffectInfo) { // special case for SPELL_EFFECT_SUMMON_RAF_FRIEND and SPELL_EFFECT_SUMMON_PLAYER, queue them on map for later execution - SpellEffectInfo effect = m_spellInfo.GetEffect(effIndex); - if (effect == null) - return; - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.SummonRafFriend: case SpellEffectName.SummonPlayer: @@ -1365,7 +1332,7 @@ namespace Game.Spells { WorldObject rafTarget = Global.ObjAccessor.FindPlayer(m_caster.ToPlayer().GetTarget()); - CallScriptObjectTargetSelectHandlers(ref rafTarget, effIndex, new SpellImplicitTargetInfo()); + CallScriptObjectTargetSelectHandlers(ref rafTarget, spellEffectInfo.EffectIndex, new SpellImplicitTargetInfo()); // scripts may modify the target - recheck if (rafTarget != null && rafTarget.IsPlayer()) @@ -1374,7 +1341,7 @@ namespace Game.Spells // since we're completely skipping AddUnitTarget logic, we need to check immunity manually // eg. aura 21546 makes target immune to summons Player player = rafTarget.ToPlayer(); - if (player.IsImmunedToSpellEffect(m_spellInfo, effIndex, null)) + if (player.IsImmunedToSpellEffect(m_spellInfo, spellEffectInfo, null)) return; rafTarget.GetMap().AddFarSpellCallback(map => @@ -1384,10 +1351,10 @@ namespace Game.Spells return; // check immunity again in case it changed during update - if (player.IsImmunedToSpellEffect(GetSpellInfo(), effIndex, null)) + if (player.IsImmunedToSpellEffect(GetSpellInfo(), spellEffectInfo, null)) return; - HandleEffects(player, null, null, effIndex, SpellEffectHandleMode.HitTarget); + HandleEffects(player, null, null, spellEffectInfo, SpellEffectHandleMode.HitTarget); }); } } @@ -1397,17 +1364,17 @@ namespace Game.Spells } // select spell implicit targets based on effect type - if (effect.GetImplicitTargetType() == 0) + if (spellEffectInfo.GetImplicitTargetType() == 0) return; - SpellCastTargetFlags targetMask = effect.GetMissingTargetMask(); + SpellCastTargetFlags targetMask = spellEffectInfo.GetMissingTargetMask(); if (targetMask == 0) return; WorldObject target = null; - switch (effect.GetImplicitTargetType()) + switch (spellEffectInfo.GetImplicitTargetType()) { // add explicit object target or self to the target map case SpellEffectImplicitTargetTypes.Explicit: @@ -1435,7 +1402,7 @@ namespace Game.Spells { Item itemTarget = m_targets.GetItemTarget(); if (itemTarget != null) - AddItemTarget(itemTarget, (uint)(1 << (int)effIndex)); + AddItemTarget(itemTarget, (uint)(1 << (int)spellEffectInfo.EffectIndex)); return; } if (Convert.ToBoolean(targetMask & SpellCastTargetFlags.GameobjectMask)) @@ -1450,14 +1417,14 @@ namespace Game.Spells break; } - CallScriptObjectTargetSelectHandlers(ref target, effIndex, new SpellImplicitTargetInfo()); + CallScriptObjectTargetSelectHandlers(ref target, spellEffectInfo.EffectIndex, new SpellImplicitTargetInfo()); if (target != null) { if (target.ToUnit()) - AddUnitTarget(target.ToUnit(), (uint)(1 << (int)effIndex), false); + AddUnitTarget(target.ToUnit(), 1u << (int)spellEffectInfo.EffectIndex, false); else if (target.IsTypeId(TypeId.GameObject)) - AddGOTarget(target.ToGameObject(), (uint)(1 << (int)effIndex)); + AddGOTarget(target.ToGameObject(), 1u << (int)spellEffectInfo.EffectIndex); } } @@ -1726,12 +1693,9 @@ namespace Game.Spells void AddUnitTarget(Unit target, uint effectMask, bool checkIfValid = true, bool Implicit = true, Position losPosition = null) { - uint validEffectMask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && (effectMask & (1 << (int)effect.EffectIndex)) != 0 && CheckEffectTarget(target, effect, losPosition)) - validEffectMask |= 1u << (int)effect.EffectIndex; - - effectMask &= validEffectMask; + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(target, spellEffectInfo, losPosition)) + effectMask &= ~(1u << (int)spellEffectInfo.EffectIndex); // no effects left if (effectMask == 0) @@ -1742,9 +1706,9 @@ namespace Game.Spells return; // Check for effect immune skip if immuned - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && target.IsImmunedToSpellEffect(m_spellInfo, effect.EffectIndex, m_caster)) - effectMask &= ~(uint)(1 << (int)effect.EffectIndex); + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (target.IsImmunedToSpellEffect(m_spellInfo, spellEffectInfo, m_caster)) + effectMask &= ~(1u << (int)spellEffectInfo.EffectIndex); ObjectGuid targetGUID = target.GetGUID(); @@ -1815,12 +1779,9 @@ namespace Game.Spells void AddGOTarget(GameObject go, uint effectMask) { - uint validEffectMask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && (effectMask & (1 << (int)effect.EffectIndex)) != 0 && CheckEffectTarget(go, effect)) - validEffectMask |= (uint)(1 << (int)effect.EffectIndex); - - effectMask &= validEffectMask; + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(go, spellEffectInfo)) + effectMask &= ~(1u << (int)spellEffectInfo.EffectIndex); // no effects left if (effectMask == 0) @@ -1870,12 +1831,9 @@ namespace Game.Spells void AddItemTarget(Item item, uint effectMask) { - uint validEffectMask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && (effectMask & (1 << (int)effect.EffectIndex)) != 0 && CheckEffectTarget(item, effect)) - validEffectMask |= 1u << (int)effect.EffectIndex; - - effectMask &= validEffectMask; + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(item, spellEffectInfo)) + effectMask &= ~(1u << (int)spellEffectInfo.EffectIndex); // no effects left if (effectMask == 0) @@ -1992,11 +1950,12 @@ namespace Game.Spells // check immunity due to diminishing returns if (Aura.BuildEffectMaskForOwner(m_spellInfo, SpellConst.MaxEffectMask, unit) != 0) { - foreach (SpellEffectInfo auraSpellEffect in m_spellInfo.GetEffects()) - if (auraSpellEffect != null) - hitInfo.AuraBasePoints[auraSpellEffect.EffectIndex] = (m_spellValue.CustomBasePointsMask & (1 << (int)auraSpellEffect.EffectIndex)) != 0 ? - m_spellValue.EffectBasePoints[auraSpellEffect.EffectIndex] : - auraSpellEffect.CalcBaseValue(m_originalCaster, unit, m_castItemEntry, m_castItemLevel); + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + { + hitInfo.AuraBasePoints[spellEffectInfo.EffectIndex] = (m_spellValue.CustomBasePointsMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0 ? + m_spellValue.EffectBasePoints[spellEffectInfo.EffectIndex] : + spellEffectInfo.CalcBaseValue(m_originalCaster, unit, m_castItemEntry, m_castItemLevel); + } // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add hitInfo.DRGroup = m_spellInfo.GetDiminishingReturnsGroupForSpell(); @@ -2016,11 +1975,11 @@ namespace Game.Spells hitInfo.Positive = true; if (origCaster == unit || !origCaster.IsFriendlyTo(unit)) { - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { // mod duration only for effects applying aura! - if (effect != null && (hitInfo.EffectMask & (1 << (int)effect.EffectIndex)) != 0 && - effect.IsUnitOwnedAuraEffect() && !m_spellInfo.IsPositiveEffect(effect.EffectIndex)) + if ((hitInfo.EffectMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0 && + spellEffectInfo.IsUnitOwnedAuraEffect() && !m_spellInfo.IsPositiveEffect(spellEffectInfo.EffectIndex)) { hitInfo.Positive = false; break; @@ -2032,16 +1991,16 @@ namespace Game.Spells // unit is immune to aura if it was diminished to 0 duration if (!hitInfo.Positive && !unit.ApplyDiminishingToDuration(m_spellInfo, ref hitInfo.AuraDuration, origCaster, diminishLevel)) - if (m_spellInfo.GetEffects().All(effInfo => effInfo == null || !effInfo.IsEffect() || effInfo.IsEffect(SpellEffectName.ApplyAura))) + if (m_spellInfo.GetEffects().All(effInfo => !effInfo.IsEffect() || effInfo.IsEffect(SpellEffectName.ApplyAura))) return SpellMissInfo.Immune; } return SpellMissInfo.None; } - public void DoSpellEffectHit(Unit unit, uint effIndex, TargetInfo hitInfo) + public void DoSpellEffectHit(Unit unit, SpellEffectInfo spellEffectInfo, TargetInfo hitInfo) { - uint aura_effmask = Aura.BuildEffectMaskForOwner(m_spellInfo, 1u << (int)effIndex, unit); + uint aura_effmask = Aura.BuildEffectMaskForOwner(m_spellInfo, 1u << (int)spellEffectInfo.EffectIndex, unit); if (aura_effmask != 0) { WorldObject caster = m_caster; @@ -2101,17 +2060,13 @@ namespace Game.Spells { int origDuration = hitInfo.AuraDuration; hitInfo.AuraDuration = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (AuraEffect auraEff in spellAura.GetAuraEffects()) { - if (effect != null) + if (auraEff != null) { - AuraEffect eff = spellAura.GetEffect(effect.EffectIndex); - if (eff != null) - { - int period = eff.GetPeriod(); - if (period != 0) // period is hastened by UNIT_MOD_CAST_SPEED - hitInfo.AuraDuration = Math.Max(Math.Max(origDuration / period, 1) * period, hitInfo.AuraDuration); - } + int period = auraEff.GetPeriod(); + if (period != 0) // period is hastened by UNIT_MOD_CAST_SPEED + hitInfo.AuraDuration = Math.Max(Math.Max(origDuration / period, 1) * period, hitInfo.AuraDuration); } } @@ -2130,7 +2085,7 @@ namespace Game.Spells } } - HandleEffects(unit, null, null, effIndex, SpellEffectHandleMode.HitTarget); + HandleEffects(unit, null, null, spellEffectInfo, SpellEffectHandleMode.HitTarget); } public void DoTriggersOnSpellHit(Unit unit, uint effMask) @@ -2192,9 +2147,9 @@ namespace Game.Spells uint channelTargetEffectMask = m_channelTargetEffectMask; uint channelAuraMask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && effect.Effect == SpellEffectName.ApplyAura) - channelAuraMask |= (1u << (int)effect.EffectIndex); + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect(SpellEffectName.ApplyAura)) + channelAuraMask |= 1u << (int)spellEffectInfo.EffectIndex; channelAuraMask &= channelTargetEffectMask; @@ -2612,9 +2567,9 @@ namespace Game.Spells if (target != null) { uint aura_effmask = 0; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && effect.IsUnitOwnedAuraEffect()) - aura_effmask |= 1u << (int)effect.EffectIndex; + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (spellEffectInfo.IsUnitOwnedAuraEffect()) + aura_effmask |= 1u << (int)spellEffectInfo.EffectIndex; if (aura_effmask != 0) { @@ -2819,10 +2774,12 @@ namespace Game.Spells foreach (TargetInfoBase target in targetContainer) target.PreprocessTarget(this); - for (uint i = 0; i < SpellConst.MaxEffects; ++i) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + { foreach (TargetInfoBase target in targetContainer) - if ((target.EffectMask & (1 << (int)i)) != 0) - target.DoTargetSpellHit(this, i); + if ((target.EffectMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0) + target.DoTargetSpellHit(this, spellEffectInfo); + } foreach (TargetInfoBase target in targetContainer) target.DoDamageAndTriggers(this); @@ -3000,14 +2957,14 @@ namespace Game.Spells HandleThreatSpells(); // handle effects with SPELL_EFFECT_HANDLE_HIT mode - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { // don't do anything for empty effect - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; // call effect handlers to handle destination hit - HandleEffects(null, null, null, effect.EffectIndex, SpellEffectHandleMode.Hit); + HandleEffects(null, null, null, spellEffectInfo, SpellEffectHandleMode.Hit); } // process items @@ -3082,10 +3039,9 @@ namespace Game.Spells // check if the player caster has moved before the spell finished // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect - SpellEffectInfo effect = m_spellInfo.GetEffect(0); if ((m_caster.IsTypeId(TypeId.Player) && m_timer != 0) && m_caster.ToPlayer().IsMoving() && (m_spellInfo.InterruptFlags.HasFlag(SpellInterruptFlags.Movement)) && - ((effect != null && effect.Effect != SpellEffectName.Stuck) || !m_caster.ToPlayer().HasUnitMovementFlag(MovementFlag.FallingFar)) && + (m_spellInfo.HasEffect(SpellEffectName.Stuck) || !m_caster.ToPlayer().HasUnitMovementFlag(MovementFlag.FallingFar)) && !m_caster.ToPlayer().HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) { // don't cancel for melee, autorepeat, triggered and instant spells @@ -3325,9 +3281,9 @@ namespace Game.Spells else { uint item = 0; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.ItemType != 0) - item = effect.ItemType; + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.ItemType != 0) + item = spellEffectInfo.ItemType; ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item); if (proto != null && proto.GetItemLimitCategory() != 0) @@ -3995,9 +3951,9 @@ namespace Game.Spells explicitTargetEffectMask = explicitTarget.EffectMask; } - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) - if (effect != null && effect.Effect == SpellEffectName.ApplyAura && (explicitTargetEffectMask & (1u << (int)effect.EffectIndex)) != 0) - channelAuraMask |= 1u << (int)effect.EffectIndex; + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) + if (spellEffectInfo.Effect == SpellEffectName.ApplyAura && (explicitTargetEffectMask & (1u << (int)spellEffectInfo.EffectIndex)) != 0) + channelAuraMask |= 1u << (int)spellEffectInfo.EffectIndex; foreach (TargetInfo target in m_UniqueTargetInfo) { @@ -4336,29 +4292,21 @@ namespace Game.Spells Log.outDebug(LogFilter.Spells, "Spell {0}, added an additional {1} threat for {2} {3} target(s)", m_spellInfo.Id, threat, IsPositive() ? "assisting" : "harming", m_UniqueTargetInfo.Count); } - public void HandleEffects(Unit pUnitTarget, Item pItemTarget, GameObject pGOTarget, uint i, SpellEffectHandleMode mode) + public void HandleEffects(Unit pUnitTarget, Item pItemTarget, GameObject pGOTarget, SpellEffectInfo spellEffectInfo, SpellEffectHandleMode mode) { effectHandleMode = mode; unitTarget = pUnitTarget; itemTarget = pItemTarget; gameObjTarget = pGOTarget; - destTarget = m_destTargets[i].Position; + destTarget = m_destTargets[spellEffectInfo.EffectIndex].Position; unitCaster = m_originalCaster ? m_originalCaster : m_caster.ToUnit(); - effectInfo = m_spellInfo.GetEffect(i); - if (effectInfo == null) - { - Log.outError(LogFilter.Spells, "Spell: {0} HandleEffects at EffectIndex: {1} missing effect", m_spellInfo.Id, i); - return; - } - SpellEffectName effect = effectInfo.Effect; + damage = CalculateDamage(spellEffectInfo, unitTarget, out _variance); - damage = CalculateDamage(i, unitTarget, out _variance); - - bool preventDefault = CallScriptEffectHandlers(i, mode); + bool preventDefault = CallScriptEffectHandlers(spellEffectInfo.EffectIndex, mode); if (!preventDefault) - Global.SpellMgr.GetSpellEffectHandler(effect).Invoke(this); + Global.SpellMgr.GetSpellEffectHandler(spellEffectInfo.Effect).Invoke(this); } public static Spell ExtractSpellFromEvent(BasicEvent basicEvent) @@ -4532,8 +4480,7 @@ namespace Game.Spells if (unitCaster.IsPlayer() && unitCaster.ToPlayer().IsMoving() && (!unitCaster.IsCharmed() || !unitCaster.GetCharmerGUID().IsCreature()) && !unitCaster.HasAuraTypeWithAffectMask(AuraType.CastWhileWalking, m_spellInfo)) { // skip stuck spell to allow use it in falling case and apply spell limitations at movement - SpellEffectInfo effect = m_spellInfo.GetEffect(0); - if ((!unitCaster.HasUnitMovementFlag(MovementFlag.FallingFar) || (effect != null && effect.Effect != SpellEffectName.Stuck)) && + if ((!unitCaster.HasUnitMovementFlag(MovementFlag.FallingFar) || !m_spellInfo.HasEffect(SpellEffectName.Stuck)) && (IsAutoRepeat() || m_spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.Standing))) return SpellCastResult.Moving; } @@ -4647,9 +4594,9 @@ namespace Game.Spells // check pet presence if (unitCaster != null) { - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect != null && effect.TargetA.GetTarget() == Targets.UnitPet) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitPet) { if (unitCaster.GetGuardianPet() == null) { @@ -4745,13 +4692,10 @@ namespace Game.Spells if (castResult != SpellCastResult.SpellCastOk) return castResult; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - // for effects of spells that have only one target - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.Dummy: { @@ -4775,14 +4719,14 @@ namespace Game.Spells } case SpellEffectName.LearnSpell: { - if (effect.TargetA.GetTarget() != Targets.UnitPet) + if (spellEffectInfo.TargetA.GetTarget() != Targets.UnitPet) break; Pet pet = m_caster.ToPlayer().GetPet(); if (pet == null) return SpellCastResult.NoPet; - SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); + SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, Difficulty.None); if (learn_spellproto == null) return SpellCastResult.NotKnown; @@ -4815,7 +4759,7 @@ namespace Game.Spells if (pet == null || pet.GetOwner() != m_caster) return SpellCastResult.BadTargets; - SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); + SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, Difficulty.None); if (learn_spellproto == null) return SpellCastResult.NotKnown; @@ -4833,7 +4777,7 @@ namespace Game.Spells if (!caster.HasSpell(m_misc.SpellId)) return SpellCastResult.NotKnown; - uint glyphId = (uint)effect.MiscValue; + uint glyphId = (uint)spellEffectInfo.MiscValue; if (glyphId != 0) { GlyphPropertiesRecord glyphProperties = CliDB.GlyphPropertiesStorage.LookupByKey(glyphId); @@ -4978,13 +4922,13 @@ namespace Game.Spells } case SpellEffectName.OpenLock: { - if (effect.TargetA.GetTarget() != Targets.GameobjectTarget && - effect.TargetA.GetTarget() != Targets.GameobjectItemTarget) + if (spellEffectInfo.TargetA.GetTarget() != Targets.GameobjectTarget && + spellEffectInfo.TargetA.GetTarget() != Targets.GameobjectItemTarget) break; if (!m_caster.IsTypeId(TypeId.Player) // only players can open locks, gather etc. // we need a go target in case of TARGET_GAMEOBJECT_TARGET - || (effect.TargetA.GetTarget() == Targets.GameobjectTarget && m_targets.GetGOTarget() == null)) + || (spellEffectInfo.TargetA.GetTarget() == Targets.GameobjectTarget && m_targets.GetGOTarget() == null)) return SpellCastResult.BadTargets; Item pTempItem = null; @@ -4998,7 +4942,7 @@ namespace Game.Spells pTempItem = m_caster.ToPlayer().GetItemByGuid(m_targets.GetItemTargetGUID()); // we need a go target, or an openable item target in case of TARGET_GAMEOBJECT_ITEM_TARGET - if (effect.TargetA.GetTarget() == Targets.GameobjectItemTarget && + if (spellEffectInfo.TargetA.GetTarget() == Targets.GameobjectItemTarget && m_targets.GetGOTarget() == null && (pTempItem == null || pTempItem.GetTemplate().GetLockID() == 0 || !pTempItem.IsLocked())) return SpellCastResult.BadTargets; @@ -5026,7 +4970,7 @@ namespace Game.Spells int skillValue = 0; // check lock compatibility - SpellCastResult res = CanOpenLock(effect, lockId, ref skillId, ref reqSkillValue, ref skillValue); + SpellCastResult res = CanOpenLock(spellEffectInfo, lockId, ref skillId, ref reqSkillValue, ref skillValue); if (res != SpellCastResult.SpellCastOk) return res; break; @@ -5048,7 +4992,7 @@ namespace Game.Spells if (unitCaster == null) break; - var SummonProperties = CliDB.SummonPropertiesStorage.LookupByKey(effect.MiscValueB); + var SummonProperties = CliDB.SummonPropertiesStorage.LookupByKey(spellEffectInfo.MiscValueB); if (SummonProperties == null) break; @@ -5258,10 +5202,10 @@ namespace Game.Spells if (artifact == null) return SpellCastResult.NoArtifactEquipped; - if (effect.Effect == SpellEffectName.GiveArtifactPower) + if (spellEffectInfo.Effect == SpellEffectName.GiveArtifactPower) { ArtifactRecord artifactEntry = CliDB.ArtifactStorage.LookupByKey(artifact.GetTemplate().GetArtifactID()); - if (artifactEntry == null || artifactEntry.ArtifactCategoryID != effect.MiscValue) + if (artifactEntry == null || artifactEntry.ArtifactCategoryID != spellEffectInfo.MiscValue) return SpellCastResult.WrongArtifactEquipped; } break; @@ -5271,12 +5215,9 @@ namespace Game.Spells } } - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.ModPossessPet: { @@ -5302,7 +5243,7 @@ namespace Game.Spells if (!unitCaster1.GetCharmerGUID().IsEmpty()) return SpellCastResult.Charmed; - if (effect.ApplyAuraName == AuraType.ModCharm || effect.ApplyAuraName == AuraType.ModPossess) + if (spellEffectInfo.ApplyAuraName == AuraType.ModCharm || spellEffectInfo.ApplyAuraName == AuraType.ModPossess) { if (!m_spellInfo.HasAttribute(SpellAttr1.DismissPet) && !unitCaster1.GetPetGUID().IsEmpty()) return SpellCastResult.AlreadyHaveSummon; @@ -5326,7 +5267,7 @@ namespace Game.Spells if (target.GetOwner() != null && target.GetOwner().IsTypeId(TypeId.Player)) return SpellCastResult.TargetIsPlayerControlled; - int damage = CalculateDamage(effect.EffectIndex, target); + int damage = CalculateDamage(spellEffectInfo, target); if (damage != 0 && target.GetLevelForTarget(m_caster) > damage) return SpellCastResult.Highlevel; } @@ -5386,7 +5327,7 @@ namespace Game.Spells } case AuraType.PeriodicManaLeech: { - if (effect.IsTargetingArea()) + if (spellEffectInfo.IsTargetingArea()) break; if (m_targets.GetUnitTarget() == null) @@ -5706,15 +5647,12 @@ namespace Game.Spells ObjectGuid targetguid = target.GetGUID(); // check if target already has the same or a more powerful aura - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) + if (!spellEffectInfo.IsAura()) continue; - if (!effect.IsAura()) - continue; - - AuraType auraType = effect.ApplyAuraName; + AuraType auraType = spellEffectInfo.ApplyAuraName; var auras = target.GetAuraEffectsByType(auraType); foreach (var eff in auras) { @@ -5731,7 +5669,7 @@ namespace Game.Spells break; case SpellGroupStackRule.ExclusiveSameEffect: // this one has further checks, but i don't think they're necessary for autocast logic case SpellGroupStackRule.ExclusiveHighest: - if (Math.Abs(effect.BasePoints) <= Math.Abs(eff.GetAmount())) + if (Math.Abs(spellEffectInfo.BasePoints) <= Math.Abs(eff.GetAmount())) return false; break; case SpellGroupStackRule.Default: @@ -5936,13 +5874,13 @@ namespace Game.Spells { // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example SpellCastResult failReason = SpellCastResult.SpellCastOk; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { // skip check, pet not required like checks, and for TARGET_UNIT_PET m_targets.GetUnitTarget() is not the real target but the caster - if (effect == null || effect.TargetA.GetTarget() == Targets.UnitPet) + if (spellEffectInfo.TargetA.GetTarget() == Targets.UnitPet) continue; - if (effect.Effect == SpellEffectName.Heal) + if (spellEffectInfo.Effect == SpellEffectName.Heal) { if (m_targets.GetUnitTarget().IsFullHealth()) { @@ -5957,15 +5895,15 @@ namespace Game.Spells } // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (effect.Effect == SpellEffectName.Energize) + if (spellEffectInfo.Effect == SpellEffectName.Energize) { - if (effect.MiscValue < 0 || effect.MiscValue >= (int)PowerType.Max) + if (spellEffectInfo.MiscValue < 0 || spellEffectInfo.MiscValue >= (int)PowerType.Max) { failReason = SpellCastResult.AlreadyAtFullPower; continue; } - PowerType power = (PowerType)effect.MiscValue; + PowerType power = (PowerType)spellEffectInfo.MiscValue; if (m_targets.GetUnitTarget().GetPower(power) == m_targets.GetUnitTarget().GetMaxPower(power)) { failReason = SpellCastResult.AlreadyAtFullPower; @@ -6093,29 +6031,26 @@ namespace Game.Spells } // special checks for spell effects - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.CreateItem: case SpellEffectName.CreateLoot: { // m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error Unit target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; - if (target.IsPlayer() && !IsTriggered() && effect.ItemType != 0) + if (target.IsPlayer() && !IsTriggered() && spellEffectInfo.ItemType != 0) { List dest = new(); - InventoryResult msg = target.ToPlayer().CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, effect.ItemType, 1); + InventoryResult msg = target.ToPlayer().CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, spellEffectInfo.ItemType, 1); if (msg != InventoryResult.Ok) { - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(effect.ItemType); + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(spellEffectInfo.ItemType); // @todo Needs review if (itemTemplate != null && itemTemplate.GetItemLimitCategory() == 0) { - player.SendEquipError(msg, null, null, effect.ItemType); + player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); return SpellCastResult.DontReport; } else @@ -6123,25 +6058,21 @@ namespace Game.Spells // Conjure Food/Water/Refreshment spells if (!(m_spellInfo.SpellFamilyName == SpellFamilyNames.Mage && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x40000000u))) return SpellCastResult.TooManyOfItem; - else if (!target.ToPlayer().HasItemCount(effect.ItemType)) + else if (!target.ToPlayer().HasItemCount(spellEffectInfo.ItemType)) { - player.SendEquipError(msg, null, null, effect.ItemType); - return SpellCastResult.DontReport; - } - else - { - SpellEffectInfo efi = m_spellInfo.GetEffect(1); - if (efi != null) - player.CastSpell(m_caster, (uint)efi.CalcValue(), new CastSpellExtraArgs(false)); // move this to anywhere + player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); return SpellCastResult.DontReport; } + else if (m_spellInfo.GetEffects().Count > 1) + player.CastSpell(m_caster, (uint)m_spellInfo.GetEffect(1).CalcValue(), new CastSpellExtraArgs(false)); // move this to anywhere + return SpellCastResult.DontReport; } } } break; } case SpellEffectName.EnchantItem: - if (effect.ItemType != 0 && m_targets.GetItemTarget() != null && m_targets.GetItemTarget().IsVellum()) + if (spellEffectInfo.ItemType != 0 && m_targets.GetItemTarget() != null && m_targets.GetItemTarget().IsVellum()) { // cannot enchant vellum for other player if (m_targets.GetItemTarget().GetOwner() != player) @@ -6150,10 +6081,10 @@ namespace Game.Spells if (m_CastItem != null && Convert.ToBoolean(m_CastItem.GetTemplate().GetFlags() & ItemFlags.NoReagentCost)) return SpellCastResult.TotemCategory; List dest = new(); - InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, effect.ItemType, 1); + InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, spellEffectInfo.ItemType, 1); if (msg != InventoryResult.Ok) { - player.SendEquipError(msg, null, null, effect.ItemType); + player.SendEquipError(msg, null, null, spellEffectInfo.ItemType); return SpellCastResult.DontReport; } } @@ -6178,7 +6109,7 @@ namespace Game.Spells } } - var enchantEntry = CliDB.SpellItemEnchantmentStorage.LookupByKey(effect.MiscValue); + var enchantEntry = CliDB.SpellItemEnchantmentStorage.LookupByKey(spellEffectInfo.MiscValue); // do not allow adding usable enchantments to items that have use effect already if (enchantEntry != null) { @@ -6223,7 +6154,7 @@ namespace Game.Spells // Not allow enchant in trade slot for some enchant type if (item.GetOwner() != player) { - int enchant_id = effect.MiscValue; + int enchant_id = spellEffectInfo.MiscValue; var pEnchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchant_id); if (pEnchant == null) return SpellCastResult.Error; @@ -6352,7 +6283,7 @@ namespace Game.Spells } case SpellEffectName.RechargeItem: { - uint itemId = effect.ItemType; + uint itemId = spellEffectInfo.ItemType; ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemId); if (proto == null) @@ -6572,12 +6503,9 @@ namespace Game.Spells WorldObject transport = null; // update effect destinations (in case of moved transport dest target) - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - - SpellDestination dest = m_destTargets[effect.EffectIndex]; + SpellDestination dest = m_destTargets[spellEffectInfo.EffectIndex]; if (dest.TransportGUID.IsEmpty()) continue; @@ -6611,12 +6539,12 @@ namespace Game.Spells return m_caster.GetMap().GetDifficultyID(); } - bool CheckEffectTarget(Unit target, SpellEffectInfo effect, Position losPosition) + bool CheckEffectTarget(Unit target, SpellEffectInfo spellEffectInfo, Position losPosition) { - if (!effect.IsEffect()) + if (spellEffectInfo == null || !spellEffectInfo.IsEffect()) return false; - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.ModPossess: case AuraType.ModCharm: @@ -6628,7 +6556,7 @@ namespace Game.Spells return false; if (!target.GetCharmerGUID().IsEmpty()) return false; - int damage = CalculateDamage(effect.EffectIndex, target); + int damage = CalculateDamage(spellEffectInfo, target); if (damage != 0) if (target.GetLevelForTarget(m_caster) > damage) return false; @@ -6647,7 +6575,7 @@ namespace Game.Spells // @todo shit below shouldn't be here, but it's temporary //Check targets for LOS visibility - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.SkinPlayerCorpse: { @@ -6697,12 +6625,12 @@ namespace Game.Spells return true; } - bool CheckEffectTarget(GameObject target, SpellEffectInfo effect) + bool CheckEffectTarget(GameObject target, SpellEffectInfo spellEffectInfo) { - if (!effect.IsEffect()) + if (spellEffectInfo == null || !spellEffectInfo.IsEffect()) return false; - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.GameObjectDamage: case SpellEffectName.GameobjectRepair: @@ -6717,9 +6645,9 @@ namespace Game.Spells return true; } - bool CheckEffectTarget(Item target, SpellEffectInfo effect) + bool CheckEffectTarget(Item target, SpellEffectInfo spellEffectInfo) { - if (!effect.IsEffect()) + if (spellEffectInfo == null || !spellEffectInfo.IsEffect()) return false; return true; @@ -6759,40 +6687,37 @@ namespace Game.Spells void HandleLaunchPhase() { // handle effects with SPELL_EFFECT_HANDLE_LAUNCH mode - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { // don't do anything for empty effect - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; - HandleEffects(null, null, null, effect.EffectIndex, SpellEffectHandleMode.Launch); + HandleEffects(null, null, null, spellEffectInfo, SpellEffectHandleMode.Launch); } PrepareTargetProcessing(); - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - float multiplier = 1.0f; - if ((m_applyMultiplierMask & (1 << (int)effect.EffectIndex)) != 0) - multiplier = effect.CalcDamageMultiplier(m_originalCaster, this); + if ((m_applyMultiplierMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0) + multiplier = spellEffectInfo.CalcDamageMultiplier(m_originalCaster, this); foreach (TargetInfo target in m_UniqueTargetInfo) { uint mask = target.EffectMask; - if ((mask & (1 << (int)effect.EffectIndex)) == 0) + if ((mask & (1 << (int)spellEffectInfo.EffectIndex)) == 0) continue; - DoEffectOnLaunchTarget(target, multiplier, effect); + DoEffectOnLaunchTarget(target, multiplier, spellEffectInfo); } } FinishTargetProcessing(); } - void DoEffectOnLaunchTarget(TargetInfo targetInfo, float multiplier, SpellEffectInfo effect) + void DoEffectOnLaunchTarget(TargetInfo targetInfo, float multiplier, SpellEffectInfo spellEffectInfo) { Unit unit = null; // In case spell hit target, do all effect on that target @@ -6811,18 +6736,18 @@ namespace Game.Spells m_damage = 0; m_healing = 0; - HandleEffects(unit, null, null, effect.EffectIndex, SpellEffectHandleMode.LaunchTarget); + HandleEffects(unit, null, null, spellEffectInfo, SpellEffectHandleMode.LaunchTarget); if (m_originalCaster != null && m_damage > 0) { - if (effect.IsTargetingArea() || effect.IsAreaAuraEffect() || effect.IsEffect(SpellEffectName.PersistentAreaAura)) + if (spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SpellEffectName.PersistentAreaAura)) { m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_originalCaster.GetGUID()); if (m_originalCaster.IsPlayer()) { // cap damage of player AOE - long targetAmount = GetUnitTargetCountForEffect(effect.EffectIndex); + long targetAmount = GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex); if (targetAmount > 20) m_damage = (int)(m_damage * 20 / targetAmount); } @@ -6830,12 +6755,12 @@ namespace Game.Spells } } - if ((m_applyMultiplierMask & (1 << (int)effect.EffectIndex)) != 0) + if ((m_applyMultiplierMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0) { - m_damage = (int)(m_damage * m_damageMultipliers[effect.EffectIndex]); - m_healing = (int)(m_healing * m_damageMultipliers[effect.EffectIndex]); + m_damage = (int)(m_damage * m_damageMultipliers[spellEffectInfo.EffectIndex]); + m_healing = (int)(m_healing * m_damageMultipliers[spellEffectInfo.EffectIndex]); - m_damageMultipliers[effect.EffectIndex] *= multiplier; + m_damageMultipliers[spellEffectInfo.EffectIndex] *= multiplier; } targetInfo.Damage += m_damage; @@ -7209,9 +7134,9 @@ namespace Game.Spells { bool only_on_caster = (triggeredByAura != null && triggeredByAura.HasAttribute(SpellAttr4.ProcOnlyOnCaster)); // If triggeredByAura has SPELL_ATTR4_PROC_ONLY_ON_CASTER then it can only proc on a casted spell with TARGET_UNIT_CASTER - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect != null && (Convert.ToBoolean(effMask & (1 << (int)effect.EffectIndex)) && (!only_on_caster || (effect.TargetA.GetTarget() == Targets.UnitCaster)))) + if ((effMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0 && (!only_on_caster || (spellEffectInfo.TargetA.GetTarget() == Targets.UnitCaster))) return true; } return false; @@ -7239,7 +7164,7 @@ namespace Game.Spells // this possibly needs fixing int auraBaseAmount = aurEff.GetBaseAmount(); // proc chance is stored in effect amount - int chance = unitCaster.CalculateSpellDamage(null, aurEff.GetSpellInfo(), aurEff.GetEffIndex(), auraBaseAmount); + int chance = unitCaster.CalculateSpellDamage(null, aurEff.GetSpellEffectInfo(), auraBaseAmount); chance *= aurEff.GetBase().GetStackAmount(); // build trigger and add to the list @@ -7325,22 +7250,15 @@ namespace Game.Spells List m_loadedScripts = new(); - int CalculateDamage(uint effIndex, Unit target) + int CalculateDamage(SpellEffectInfo spellEffectInfo, Unit target) { - int? basePoint = null; - if ((m_spellValue.CustomBasePointsMask & (1 << (int)effIndex)) != 0) - basePoint = m_spellValue.EffectBasePoints[effIndex]; - - return m_caster.CalculateSpellDamage(target, m_spellInfo, effIndex, basePoint, m_castItemEntry, m_castItemLevel); + return CalculateDamage(spellEffectInfo, target, out _); } - int CalculateDamage(uint effIndex, Unit target, out float variance) + int CalculateDamage(SpellEffectInfo spellEffectInfo, Unit target, out float variance) { - int? basePoint = null; - if ((m_spellValue.CustomBasePointsMask & (1 << (int)effIndex)) != 0) - basePoint = m_spellValue.EffectBasePoints[effIndex]; - - return m_caster.CalculateSpellDamage(out variance, target, m_spellInfo, effIndex, basePoint, m_castItemEntry, m_castItemLevel); + bool needRecalculateBasePoints = (m_spellValue.CustomBasePointsMask & (1 << (int)spellEffectInfo.EffectIndex)) == 0; + return m_caster.CalculateSpellDamage(out variance, target, spellEffectInfo, needRecalculateBasePoints ? null : m_spellValue.EffectBasePoints[spellEffectInfo.EffectIndex], m_castItemEntry, m_castItemLevel); } public SpellState GetState() @@ -7726,7 +7644,7 @@ namespace Game.Spells public uint EffectMask; public virtual void PreprocessTarget(Spell spell) { } - public virtual void DoTargetSpellHit(Spell spell, uint effIndex) { } + public virtual void DoTargetSpellHit(Spell spell, SpellEffectInfo spellEffectInfo) { } public virtual void DoDamageAndTriggers(Spell spell) { } } @@ -7795,7 +7713,7 @@ namespace Game.Spells Healing = spell.m_healing; } - public override void DoTargetSpellHit(Spell spell, uint effIndex) + public override void DoTargetSpellHit(Spell spell, SpellEffectInfo spellEffectInfo) { Unit unit = spell.GetCaster().GetGUID() == TargetGUID ? spell.GetCaster().ToUnit() : Global.ObjAccessor.GetUnit(spell.GetCaster(), TargetGUID); if (unit == null) @@ -7817,7 +7735,7 @@ namespace Game.Spells return; // No missinfo in that case if (_spellHitTarget) - spell.DoSpellEffectHit(_spellHitTarget, effIndex, this); + spell.DoSpellEffectHit(_spellHitTarget, spellEffectInfo, this); // scripts can modify damage/healing for current target, save them Damage = spell.m_damage; @@ -8064,7 +7982,7 @@ namespace Game.Spells public ObjectGuid TargetGUID; public ulong TimeDelay; - public override void DoTargetSpellHit(Spell spell, uint effIndex) + public override void DoTargetSpellHit(Spell spell, SpellEffectInfo spellEffectInfo) { GameObject go = spell.GetCaster().GetGUID() == TargetGUID ? spell.GetCaster().ToGameObject() : ObjectAccessor.GetGameObject(spell.GetCaster(), TargetGUID); if (go == null) @@ -8072,7 +7990,7 @@ namespace Game.Spells spell.CallScriptBeforeHitHandlers(SpellMissInfo.None); - spell.HandleEffects(null, null, go, effIndex, SpellEffectHandleMode.HitTarget); + spell.HandleEffects(null, null, go, spellEffectInfo, SpellEffectHandleMode.HitTarget); //AI functions if (go.GetAI() != null) @@ -8097,11 +8015,11 @@ namespace Game.Spells { public Item TargetItem; - public override void DoTargetSpellHit(Spell spell, uint effIndex) + public override void DoTargetSpellHit(Spell spell, SpellEffectInfo spellEffectInfo) { spell.CallScriptBeforeHitHandlers(SpellMissInfo.None); - spell.HandleEffects(null, TargetItem, null, effIndex, SpellEffectHandleMode.HitTarget); + spell.HandleEffects(null, TargetItem, null, spellEffectInfo, SpellEffectHandleMode.HitTarget); spell.CallScriptOnHitHandlers(); spell.CallScriptAfterHitHandlers(); @@ -8112,9 +8030,8 @@ namespace Game.Spells { public SpellValue(SpellInfo proto, WorldObject caster) { - foreach (SpellEffectInfo effect in proto.GetEffects()) - if (effect != null) - EffectBasePoints[effect.EffectIndex] = effect.CalcBaseValue(caster, null, 0, -1); + foreach (var spellEffectInfo in proto.GetEffects()) + EffectBasePoints[spellEffectInfo.EffectIndex] = spellEffectInfo.CalcBaseValue(caster, null, 0, -1); CustomBasePointsMask = 0; MaxAffectedTargets = proto.MaxAffectedTargets; diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 7c6358cc6..6b1a9290e 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -37,7 +37,7 @@ namespace Game.Spells { public partial class Spell { - [SpellEffectHandler(SpellEffectName.Null)] + [SpellEffectHandler(SpellEffectName.None)] [SpellEffectHandler(SpellEffectName.Portal)] [SpellEffectHandler(SpellEffectName.BindSight)] [SpellEffectHandler(SpellEffectName.CallPet)] @@ -1155,12 +1155,9 @@ namespace Game.Spells return; // only handle at last effect - for (uint i = effectInfo.EffectIndex + 1; i < SpellConst.MaxEffects; ++i) - { - SpellEffectInfo otherEffect = m_spellInfo.GetEffect(i); - if (otherEffect != null && otherEffect.IsEffect(SpellEffectName.PersistentAreaAura)) + for (uint i = effectInfo.EffectIndex + 1; i < m_spellInfo.GetEffects().Count; ++i) + if (m_spellInfo.GetEffect(i).IsEffect(SpellEffectName.PersistentAreaAura)) return; - } Cypher.Assert(dynObjAura == null); @@ -2482,13 +2479,9 @@ namespace Game.Spells // multiple weapon dmg effect workaround // execute only the last weapon damage // and handle all effects at once - for (var j = effectInfo.EffectIndex + 1; j < SpellConst.MaxEffects; ++j) + for (var j = effectInfo.EffectIndex + 1; j < m_spellInfo.GetEffects().Count; ++j) { - var effect = m_spellInfo.GetEffect(j); - if (effect == null) - continue; - - switch (effect.Effect) + switch (m_spellInfo.GetEffect(j).Effect) { case SpellEffectName.WeaponDamage: case SpellEffectName.WeaponDamageNoSchool: @@ -2518,23 +2511,20 @@ namespace Game.Spells bool normalized = false; float weaponDamagePercentMod = 1.0f; - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.WeaponDamage: case SpellEffectName.WeaponDamageNoSchool: - fixed_bonus += CalculateDamage(effect.EffectIndex, unitTarget); + fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget); break; case SpellEffectName.NormalizedWeaponDmg: - fixed_bonus += CalculateDamage(effect.EffectIndex, unitTarget); + fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget); normalized = true; break; case SpellEffectName.WeaponPercentDamage: - MathFunctions.ApplyPct(ref weaponDamagePercentMod, CalculateDamage(effect.EffectIndex, unitTarget)); + MathFunctions.ApplyPct(ref weaponDamagePercentMod, CalculateDamage(spellEffectInfo, unitTarget)); break; default: break; // not weapon damage effect, just skip @@ -2571,14 +2561,11 @@ namespace Game.Spells uint weaponDamage = unitCaster.CalculateDamage(m_attackType, normalized, addPctMods); // Sequence is important - foreach (SpellEffectInfo effect in m_spellInfo.GetEffects()) + foreach (var spellEffectInfo in m_spellInfo.GetEffects()) { - if (effect == null) - continue; - // We assume that a spell have at most one fixed_bonus // and at most one weaponDamagePercentMod - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.WeaponDamage: case SpellEffectName.WeaponDamageNoSchool: @@ -3092,7 +3079,7 @@ namespace Game.Spells { // @todo a hack, range = 11, should after some time cast, otherwise too far unitCaster.CastSpell(parent, 62496, new CastSpellExtraArgs(true)); - unitTarget.CastSpell(parent, (uint)m_spellInfo.GetEffect(0).CalcValue()); + unitTarget.CastSpell(parent, (uint)damage); // DIFFICULTY_NONE, so effect always valid } } } diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index c31b90325..dd9ebd571 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -41,7 +41,7 @@ namespace Game.Spells if (spellEffect == null) continue; - _effects[spellEffect.EffectIndex] = new SpellEffectInfo(this, spellEffect); + _effects.Add(new SpellEffectInfo(this, spellEffect)); } SpellName = spellName.Name; @@ -246,20 +246,19 @@ namespace Game.Spells } public SpellInfo(SpellNameRecord spellName, Difficulty difficulty, List effects) - { Id = spellName.Id; Difficulty = difficulty; SpellName = spellName.Name; foreach (SpellEffectRecord spellEffect in effects) - _effects[spellEffect.EffectIndex] = new SpellEffectInfo(this, spellEffect); + _effects.Add(new SpellEffectInfo(this, spellEffect)); } public bool HasEffect(SpellEffectName effect) { - foreach (SpellEffectInfo eff in _effects) - if (eff != null && eff.IsEffect(effect)) + foreach (var effectInfo in _effects) + if (effectInfo.IsEffect(effect)) return true; return false; @@ -267,8 +266,8 @@ namespace Game.Spells public bool HasAura(AuraType aura) { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.IsAura(aura)) + foreach (var effectInfo in _effects) + if (effectInfo.IsAura(aura)) return true; return false; @@ -276,8 +275,8 @@ namespace Game.Spells public bool HasAreaAuraEffect() { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.IsAreaAuraEffect()) + foreach (var effectInfo in _effects) + if (effectInfo.IsAreaAuraEffect()) return true; return false; @@ -285,12 +284,9 @@ namespace Game.Spells public bool HasOnlyDamageEffects() { - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null) - continue; - - switch (effect.Effect) + switch (effectInfo.Effect) { case SpellEffectName.WeaponDamage: case SpellEffectName.WeaponDamageNoSchool: @@ -311,11 +307,9 @@ namespace Game.Spells public bool IsExplicitDiscovery() { - SpellEffectInfo effect0 = GetEffect(0); - SpellEffectInfo effect1 = GetEffect(1); - - return ((effect0 != null && (effect0.Effect == SpellEffectName.CreateRandomItem || effect0.Effect == SpellEffectName.CreateLoot)) - && effect1 != null && effect1.Effect == SpellEffectName.ScriptEffect) + return ((GetEffect(0).Effect == SpellEffectName.CreateRandomItem + || GetEffect(0).Effect == SpellEffectName.CreateLoot) + && GetEffect(1).Effect == SpellEffectName.ScriptEffect) || Id == 64323; } @@ -326,19 +320,19 @@ namespace Game.Spells public bool IsQuestTame() { - SpellEffectInfo effect0 = GetEffect(0); - SpellEffectInfo effect1 = GetEffect(1); - return effect0 != null && effect1 != null && effect0.Effect == SpellEffectName.Threat && effect1.Effect == SpellEffectName.ApplyAura - && effect1.ApplyAuraName == AuraType.Dummy; + if (GetEffects().Count < 2) + return false; + + return GetEffect(0).Effect == SpellEffectName.Threat && GetEffect(1).Effect == SpellEffectName.ApplyAura && GetEffect(1).ApplyAuraName == AuraType.Dummy; } public bool IsProfession() { - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect != null && effect.Effect == SpellEffectName.Skill) + if (effectInfo.IsEffect(SpellEffectName.Skill)) { - uint skill = (uint)effect.MiscValue; + uint skill = (uint)effectInfo.MiscValue; if (Global.SpellMgr.IsProfessionSkill(skill)) return true; @@ -349,10 +343,9 @@ namespace Game.Spells public bool IsPrimaryProfession() { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.Effect == SpellEffectName.Skill) - if (Global.SpellMgr.IsPrimaryProfessionSkill((uint)effect.MiscValue)) - return true; + foreach (var effectInfo in _effects) + if (effectInfo.IsEffect(SpellEffectName.Skill) && Global.SpellMgr.IsPrimaryProfessionSkill((uint)effectInfo.MiscValue)) + return true; return false; } @@ -375,8 +368,8 @@ namespace Game.Spells public bool IsAffectingArea() { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.IsEffect() && (effect.IsTargetingArea() || effect.IsEffect(SpellEffectName.PersistentAreaAura) || effect.IsAreaAuraEffect())) + foreach (var effectInfo in _effects) + if (effectInfo.IsEffect() && (effectInfo.IsTargetingArea() || effectInfo.IsEffect(SpellEffectName.PersistentAreaAura) || effectInfo.IsAreaAuraEffect())) return true; return false; @@ -385,8 +378,8 @@ namespace Game.Spells // checks if spell targets are selected from area, doesn't include spell effects in check (like area wide auras for example) public bool IsTargetingArea() { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.IsEffect() && effect.IsTargetingArea()) + foreach (var effectInfo in _effects) + if (effectInfo.IsEffect() && effectInfo.IsTargetingArea()) return true; return false; @@ -405,12 +398,12 @@ namespace Game.Spells if (triggeringSpell.IsChanneled()) { SpellCastTargetFlags mask = 0; - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect != null && (effect.TargetA.GetTarget() != Framework.Constants.Targets.UnitCaster && effect.TargetA.GetTarget() != Framework.Constants.Targets.DestCaster - && effect.TargetB.GetTarget() != Framework.Constants.Targets.UnitCaster && effect.TargetB.GetTarget() != Framework.Constants.Targets.DestCaster)) + if (effectInfo.TargetA.GetTarget() != Framework.Constants.Targets.UnitCaster && effectInfo.TargetA.GetTarget() != Framework.Constants.Targets.DestCaster + && effectInfo.TargetB.GetTarget() != Framework.Constants.Targets.UnitCaster && effectInfo.TargetB.GetTarget() != Framework.Constants.Targets.DestCaster) { - mask |= effect.GetProvidedTargetMask(); + mask |= effectInfo.GetProvidedTargetMask(); } } @@ -441,22 +434,19 @@ namespace Game.Spells return false; // All stance spells. if any better way, change it. - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null) - continue; - switch (SpellFamilyName) { case SpellFamilyNames.Paladin: // Paladin aura Spell - if (effect.Effect == SpellEffectName.ApplyAreaAuraRaid) + if (effectInfo.Effect == SpellEffectName.ApplyAreaAuraRaid) return false; break; case SpellFamilyNames.Druid: // Druid form Spell - if (effect.Effect == SpellEffectName.ApplyAura && - effect.ApplyAuraName == AuraType.ModShapeshift) + if (effectInfo.Effect == SpellEffectName.ApplyAura && + effectInfo.ApplyAuraName == AuraType.ModShapeshift) return false; break; } @@ -504,26 +494,18 @@ namespace Game.Spells if (HasAttribute(SpellAttr2.CanTargetDead) || Targets.HasAnyFlag(SpellCastTargetFlags.CorpseAlly | SpellCastTargetFlags.CorpseEnemy | SpellCastTargetFlags.UnitDead)) return true; - foreach (SpellEffectInfo effect in _effects) - { - if (effect == null) - continue; - - if (effect.TargetA.GetObjectType() == SpellTargetObjectTypes.Corpse || effect.TargetB.GetObjectType() == SpellTargetObjectTypes.Corpse) + foreach (var effectInfo in _effects) + if (effectInfo.TargetA.GetObjectType() == SpellTargetObjectTypes.Corpse || effectInfo.TargetB.GetObjectType() == SpellTargetObjectTypes.Corpse) return true; - } return false; } public bool IsGroupBuff() { - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null) - continue; - - switch (effect.TargetA.GetCheckType()) + switch (effectInfo.TargetA.GetCheckType()) { case SpellTargetCheckTypes.Party: case SpellTargetCheckTypes.Raid: @@ -794,8 +776,7 @@ namespace Game.Spells // talents that learn spells can have stance requirements that need ignore // (this requirement only for client-side stance show in talent description) /* TODO: 6.x fix this in proper way (probably spell flags/attributes?) - if (CliDB.GetTalentSpellCost(Id) > 0 && - (Effects[0].Effect == SpellEffects.LearnSpell || Effects[1].Effect == SpellEffects.LearnSpell || Effects[2].Effect == SpellEffects.LearnSpell)) + if (CliDB.GetTalentSpellCost(Id) > 0 && HasEffect(SpellEffects.LearnSpell)) return SpellCastResult.SpellCastOk; */ @@ -974,16 +955,16 @@ namespace Game.Spells // aura limitations if (player) { - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null || !effect.IsAura()) + if (!effectInfo.IsAura()) continue; - switch (effect.ApplyAuraName) + switch (effectInfo.ApplyAuraName) { case AuraType.ModShapeshift: { - SpellShapeshiftFormRecord spellShapeshiftForm = CliDB.SpellShapeshiftFormStorage.LookupByKey(effect.MiscValue); + SpellShapeshiftFormRecord spellShapeshiftForm = CliDB.SpellShapeshiftFormStorage.LookupByKey(effectInfo.MiscValue); if (spellShapeshiftForm != null) { uint mountType = spellShapeshiftForm.MountTypeID; @@ -995,7 +976,7 @@ namespace Game.Spells } case AuraType.Mounted: { - uint mountType = (uint)effect.MiscValueB; + uint mountType = (uint)effectInfo.MiscValueB; MountRecord mountEntry = Global.DB2Mgr.GetMount(Id); if (mountEntry != null) mountType = mountEntry.MountTypeID; @@ -1224,11 +1205,11 @@ namespace Game.Spells if (vehicle) { VehicleSeatFlags checkMask = 0; - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect != null && effect.ApplyAuraName == AuraType.ModShapeshift) + if (effectInfo.IsAura(AuraType.ModShapeshift)) { - var shapeShiftFromEntry = CliDB.SpellShapeshiftFormStorage.LookupByKey((uint)effect.MiscValue); + var shapeShiftFromEntry = CliDB.SpellShapeshiftFormStorage.LookupByKey((uint)effectInfo.MiscValue); if (shapeShiftFromEntry != null && !shapeShiftFromEntry.Flags.HasAnyFlag(SpellShapeshiftFormFlags.Stance)) checkMask |= VehicleSeatFlags.Uncontrolled; break; @@ -1249,12 +1230,12 @@ namespace Game.Spells // Can only summon uncontrolled minions/guardians when on controlled vehicle if (vehicleSeat.HasFlag(VehicleSeatFlags.CanControl | VehicleSeatFlags.Unk2)) { - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null || effect.Effect != SpellEffectName.Summon) + if (!effectInfo.IsEffect(SpellEffectName.Summon)) continue; - var props = CliDB.SummonPropertiesStorage.LookupByKey(effect.MiscValueB); + var props = CliDB.SummonPropertiesStorage.LookupByKey(effectInfo.MiscValueB); if (props != null && props.Control != SummonCategory.Wild) return SpellCastResult.CantDoThatRightNow; } @@ -1296,9 +1277,9 @@ namespace Game.Spells if (Mechanic != 0) mask |= (uint)(1 << (int)Mechanic); - foreach (SpellEffectInfo effect in _effects) - if (effect != null && effect.IsEffect() && effect.Mechanic != 0) - mask |= 1u << (int)effect.Mechanic; + foreach (var effectInfo in _effects) + if (effectInfo.IsEffect() && effectInfo.Mechanic != 0) + mask |= 1u << (int)effectInfo.Mechanic; return mask; } @@ -1309,9 +1290,8 @@ namespace Game.Spells if (Mechanic != 0) mask |= 1u << (int)Mechanic; - var effect = _effects[effIndex]; - if (effect != null && effect.IsEffect() && effect.Mechanic != 0) - mask |= 1u << (int)effect.Mechanic; + if (GetEffect(effIndex).IsEffect() && GetEffect(effIndex).Mechanic != 0) + mask |= 1u << (int)GetEffect(effIndex).Mechanic; return mask; } @@ -1322,18 +1302,17 @@ namespace Game.Spells if (Mechanic != 0) mask |= (uint)(1 << (int)Mechanic); - foreach (SpellEffectInfo effect in _effects) - if (effect != null && Convert.ToBoolean(effectMask & (1 << (int)effect.EffectIndex)) && effect.Mechanic != 0) - mask |= 1u << (int)effect.Mechanic; + foreach (var effectInfo in _effects) + if ((effectMask & (1 << (int)effectInfo.EffectIndex)) != 0 && effectInfo.Mechanic != 0) + mask |= 1u << (int)effectInfo.Mechanic; return mask; } public Mechanics GetEffectMechanic(uint effIndex) { - SpellEffectInfo effect = GetEffect(effIndex); - if (effect != null && effect.IsEffect() && effect.Mechanic != 0) - return effect.Mechanic; + if (GetEffect(effIndex).IsEffect() && GetEffect(effIndex).Mechanic != 0) + return GetEffect(effIndex).Mechanic; if (Mechanic != 0) return Mechanic; @@ -1391,8 +1370,8 @@ namespace Game.Spells if (Convert.ToBoolean(GetSchoolMask() & SpellSchoolMask.Frost)) { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && (effect.IsAura(AuraType.ModStun) || effect.IsAura(AuraType.ModRoot))) + foreach (var effectInfo in _effects) + if (effectInfo.IsAura(AuraType.ModStun) || effectInfo.IsAura(AuraType.ModRoot) || effectInfo.IsAura(AuraType.ModRoot2)) _auraState = AuraStateType.Frozen; } @@ -1466,12 +1445,12 @@ namespace Game.Spells { bool food = false; bool drink = false; - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect == null || !effect.IsAura()) + if (!effectInfo.IsAura()) continue; - switch (effect.ApplyAuraName) + switch (effectInfo.ApplyAuraName) { // Food case AuraType.ModRegen: @@ -1523,8 +1502,7 @@ namespace Game.Spells if (SpellFamilyFlags[0].HasAnyFlag(0x400u)) _spellSpecific = SpellSpecificType.MageArcaneBrillance; - SpellEffectInfo effect = GetEffect(0); - if (effect != null && SpellFamilyFlags[0].HasAnyFlag(0x1000000u) && effect.IsAura(AuraType.ModConfuse)) + if (SpellFamilyFlags[0].HasAnyFlag(0x1000000u) && GetEffect(0).IsAura(AuraType.ModConfuse)) _spellSpecific = SpellSpecificType.MagePolymorph; break; @@ -1617,11 +1595,11 @@ namespace Game.Spells break; } - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in _effects) { - if (effect != null && effect.Effect == SpellEffectName.ApplyAura) + if (effectInfo.IsEffect(SpellEffectName.ApplyAura)) { - switch (effect.ApplyAuraName) + switch (effectInfo.ApplyAuraName) { case AuraType.ModCharm: case AuraType.ModPossessPet: @@ -2105,7 +2083,7 @@ namespace Game.Spells public void _LoadImmunityInfo() { - var loadImmunityInfoFn = new Action(effectInfo => + foreach (SpellEffectInfo effect in _effects) { uint schoolImmunityMask = 0; uint applyHarmfulAuraImmunityMask = 0; @@ -2113,12 +2091,12 @@ namespace Game.Spells uint dispelImmunity = 0; uint damageImmunityMask = 0; - int miscVal = effectInfo.MiscValue; - int amount = effectInfo.CalcValue(); + int miscVal = effect.MiscValue; + int amount = effect.CalcValue(); - ImmunityInfo immuneInfo = effectInfo.GetImmunityInfo(); + ImmunityInfo immuneInfo = effect.GetImmunityInfo(); - switch (effectInfo.ApplyAuraName) + switch (effect.ApplyAuraName) { case AuraType.MechanicImmunityMask: { @@ -2360,14 +2338,6 @@ namespace Game.Spells immuneInfo.DamageSchoolMask = damageImmunityMask; _allowedMechanicMask |= immuneInfo.MechanicImmuneMask; - }); - - foreach (SpellEffectInfo effect in _effects) - { - if (effect == null) - continue; - - loadImmunityInfoFn(effect); } if (HasAttribute(SpellAttr5.UsableWhileStunned)) @@ -2408,9 +2378,9 @@ namespace Game.Spells } } - public void ApplyAllSpellImmunitiesTo(Unit target, SpellEffectInfo effect, bool apply) + public void ApplyAllSpellImmunitiesTo(Unit target, SpellEffectInfo spellEffectInfo, bool apply) { - ImmunityInfo immuneInfo = effect.GetImmunityInfo(); + ImmunityInfo immuneInfo = spellEffectInfo.GetImmunityInfo(); uint schoolImmunity = immuneInfo.SchoolImmuneMask; if (schoolImmunity != 0) @@ -2488,9 +2458,9 @@ namespace Game.Spells if (auraSpellInfo == null) return false; - foreach (SpellEffectInfo effectInfo in _effects) + foreach (var effectInfo in _effects) { - if (effectInfo == null) + if (!effectInfo.IsEffect()) continue; ImmunityInfo immuneInfo = effectInfo.GetImmunityInfo(); @@ -2514,16 +2484,12 @@ namespace Game.Spells return true; bool immuneToAllEffects = true; - foreach (SpellEffectInfo auraSpellEffectInfo in auraSpellInfo.GetEffects()) + foreach (var auraSpellEffectInfo in auraSpellInfo.GetEffects()) { - if (auraSpellEffectInfo == null) + if (!auraSpellEffectInfo.IsEffect()) continue; - SpellEffectName effectName = auraSpellEffectInfo.Effect; - if (effectName == 0) - continue; - - if (!immuneInfo.SpellEffectImmune.Contains(effectName)) + if (!immuneInfo.SpellEffectImmune.Contains(auraSpellEffectInfo.Effect)) { immuneToAllEffects = false; break; @@ -2580,19 +2546,16 @@ namespace Game.Spells if (aurEff.GetSpellInfo().HasAttribute(SpellAttr0.UnaffectedByInvulnerability)) return false; - foreach (SpellEffectInfo effectInfo in _effects) + foreach (var effectInfo in GetEffects()) { - if (effectInfo == null) - continue; - - if (effectInfo.Effect != SpellEffectName.ApplyAura) + if (!effectInfo.IsEffect(SpellEffectName.ApplyAura)) continue; uint miscValue = (uint)effectInfo.MiscValue; switch (effectInfo.ApplyAuraName) { case AuraType.StateImmunity: - if (miscValue != (uint)aurEff.GetSpellEffectInfo().ApplyAuraName) + if (miscValue != (uint)aurEff.GetAuraType()) continue; break; case AuraType.SchoolImmunity: @@ -2702,35 +2665,35 @@ namespace Game.Spells uint totalTicks = 0; int DotDuration = GetDuration(); - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in GetEffects()) { - if (effect != null && effect.Effect == SpellEffectName.ApplyAura) + if (!effectInfo.IsEffect(SpellEffectName.ApplyAura)) + continue; + + switch (effectInfo.ApplyAuraName) { - switch (effect.ApplyAuraName) - { - case AuraType.PeriodicDamage: - case AuraType.PeriodicDamagePercent: - case AuraType.PeriodicHeal: - case AuraType.ObsModHealth: - case AuraType.ObsModPower: - case AuraType.Unk48: - case AuraType.PowerBurn: - case AuraType.PeriodicLeech: - case AuraType.PeriodicManaLeech: - case AuraType.PeriodicEnergize: - case AuraType.PeriodicDummy: - case AuraType.PeriodicTriggerSpell: - case AuraType.PeriodicTriggerSpellWithValue: - case AuraType.PeriodicHealthFunnel: - // skip infinite periodics - if (effect.ApplyAuraPeriod > 0 && DotDuration > 0) - { - totalTicks = (uint)DotDuration / effect.ApplyAuraPeriod; - if (HasAttribute(SpellAttr5.StartPeriodicAtApply)) - ++totalTicks; - } - break; - } + case AuraType.PeriodicDamage: + case AuraType.PeriodicDamagePercent: + case AuraType.PeriodicHeal: + case AuraType.ObsModHealth: + case AuraType.ObsModPower: + case AuraType.Unk48: + case AuraType.PowerBurn: + case AuraType.PeriodicLeech: + case AuraType.PeriodicManaLeech: + case AuraType.PeriodicEnergize: + case AuraType.PeriodicDummy: + case AuraType.PeriodicTriggerSpell: + case AuraType.PeriodicTriggerSpellWithValue: + case AuraType.PeriodicHealthFunnel: + // skip infinite periodics + if (effectInfo.ApplyAuraPeriod > 0 && DotDuration > 0) + { + totalTicks = (uint)DotDuration / effectInfo.ApplyAuraPeriod; + if (HasAttribute(SpellAttr5.StartPeriodicAtApply)) + ++totalTicks; + } + break; } } @@ -3160,13 +3123,13 @@ namespace Game.Spells return this; bool needRankSelection = false; - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in GetEffects()) { - if (effect != null && IsPositiveEffect(effect.EffectIndex) && - (effect.Effect == SpellEffectName.ApplyAura || - effect.Effect == SpellEffectName.ApplyAreaAuraParty || - effect.Effect == SpellEffectName.ApplyAreaAuraRaid) && - effect.Scaling.Coefficient != 0) + if (IsPositiveEffect(effectInfo.EffectIndex) && + (effectInfo.IsEffect(SpellEffectName.ApplyAura) || + effectInfo.IsEffect(SpellEffectName.ApplyAreaAuraParty) || + effectInfo.IsEffect(SpellEffectName.ApplyAreaAuraRaid)) && + effectInfo.Scaling.Coefficient != 0) { needRankSelection = true; break; @@ -3245,20 +3208,20 @@ namespace Game.Spells bool dstSet = false; SpellCastTargetFlags targetMask = Targets; // prepare target mask using effect target entries - foreach (SpellEffectInfo effect in _effects) + foreach (var effectInfo in GetEffects()) { - if (effect == null || !effect.IsEffect()) + if (!effectInfo.IsEffect()) continue; - targetMask |= effect.TargetA.GetExplicitTargetMask(ref srcSet, ref dstSet); - targetMask |= effect.TargetB.GetExplicitTargetMask(ref srcSet, ref dstSet); + targetMask |= effectInfo.TargetA.GetExplicitTargetMask(ref srcSet, ref dstSet); + targetMask |= effectInfo.TargetB.GetExplicitTargetMask(ref srcSet, ref dstSet); // add explicit target flags based on spell effects which have SpellEffectImplicitTargetTypes.Explicit and no valid target provided - if (effect.GetImplicitTargetType() != SpellEffectImplicitTargetTypes.Explicit) + if (effectInfo.GetImplicitTargetType() != SpellEffectImplicitTargetTypes.Explicit) continue; // extend explicit target mask only if valid targets for effect could not be provided by target types - SpellCastTargetFlags effectTargetMask = effect.GetMissingTargetMask(srcSet, dstSet, targetMask); + SpellCastTargetFlags effectTargetMask = effectInfo.GetMissingTargetMask(srcSet, dstSet, targetMask); // don't add explicit object/dest flags when spell has no max range if (GetMaxRange(true) == 0.0f && GetMaxRange(false) == 0.0f) @@ -3270,24 +3233,22 @@ namespace Game.Spells ExplicitTargetMask = (uint)targetMask; } - public bool _isPositiveTarget(SpellInfo spellInfo, uint effIndex) + public bool _isPositiveTarget(SpellEffectInfo effect) { - SpellEffectInfo effect = spellInfo.GetEffect(effIndex); - if (effect == null || !effect.IsEffect()) + if (!effect.IsEffect()) return true; - return (effect.TargetA.GetCheckType() != SpellTargetCheckTypes.Enemy && - effect.TargetB.GetCheckType() != SpellTargetCheckTypes.Enemy); + return effect.TargetA.GetCheckType() != SpellTargetCheckTypes.Enemy && + effect.TargetB.GetCheckType() != SpellTargetCheckTypes.Enemy; } - bool _isPositiveEffectImpl(SpellInfo spellInfo, uint effIndex, List> visited) + bool _isPositiveEffectImpl(SpellInfo spellInfo, SpellEffectInfo effect, List> visited) { - SpellEffectInfo effect = spellInfo.GetEffect(effIndex); - if (effect == null || !effect.IsEffect()) + if ( !effect.IsEffect()) return true; // attribute may be already set in DB - if (!spellInfo.IsPositiveEffect(effIndex)) + if (!spellInfo.IsPositiveEffect(effect.EffectIndex)) return false; // passive auras like talents are all positive @@ -3298,7 +3259,7 @@ namespace Game.Spells if (spellInfo.HasAttribute(SpellAttr0.Negative1)) return false; - visited.Add(Tuple.Create(spellInfo, effIndex)); + visited.Add(Tuple.Create(spellInfo, effect.EffectIndex)); int bp = effect.CalcValue(); switch (spellInfo.SpellFamilyName) @@ -3352,16 +3313,13 @@ namespace Game.Spells if (spellInfo.HasAttribute(SpellAttr1.DontRefreshDurationOnRecast)) { // check for targets, there seems to be an assortment of dummy triggering spells that should be negative - foreach (SpellEffectInfo otherEffect in spellInfo.GetEffects()) - if (otherEffect != null && !_isPositiveTarget(spellInfo, otherEffect.EffectIndex)) + foreach (var otherEffect in spellInfo.GetEffects()) + if (!_isPositiveTarget(otherEffect)) return false; } - foreach (SpellEffectInfo otherEffect in spellInfo.GetEffects()) + foreach (var otherEffect in spellInfo.GetEffects()) { - if (otherEffect == null) - continue; - switch (otherEffect.Effect) { case SpellEffectName.Heal: @@ -3371,7 +3329,7 @@ namespace Game.Spells case SpellEffectName.EnergizePct: return true; case SpellEffectName.Instakill: - if (otherEffect.EffectIndex != effIndex && // for spells like 38044: instakill effect is negative but auras on target must count as buff + if (otherEffect.EffectIndex != effect.EffectIndex && // for spells like 38044: instakill effect is negative but auras on target must count as buff otherEffect.TargetA.GetTarget() == effect.TargetA.GetTarget() && otherEffect.TargetB.GetTarget() == effect.TargetB.GetTarget()) return false; @@ -3430,7 +3388,7 @@ namespace Game.Spells case SpellEffectName.AttackMe: case SpellEffectName.PowerBurn: // check targets - if (!_isPositiveTarget(spellInfo, effIndex)) + if (!_isPositiveTarget(effect)) return false; break; case SpellEffectName.Dispel: @@ -3446,11 +3404,11 @@ namespace Game.Spells } // also check targets - if (!_isPositiveTarget(spellInfo, effIndex)) + if (!_isPositiveTarget(effect)) return false; break; case SpellEffectName.DispelMechanic: - if (!_isPositiveTarget(spellInfo, effIndex)) + if (!_isPositiveTarget(effect)) { // non-positive mechanic dispel on negative target switch ((Mechanics)effect.MiscValue) @@ -3468,7 +3426,7 @@ namespace Game.Spells case SpellEffectName.Threat: case SpellEffectName.ModifyThreatPercent: // check targets AND basepoints - if (!_isPositiveTarget(spellInfo, effIndex) && bp > 0) + if (!_isPositiveTarget(effect) && bp > 0) return false; break; default: @@ -3513,7 +3471,7 @@ namespace Game.Spells case AuraType.ModAttackPower: case AuraType.ModRangedAttackPower: case AuraType.ModDamagePercentDone: - if (!_isPositiveTarget(spellInfo, effIndex) && bp < 0) + if (!_isPositiveTarget(effect) && bp < 0) return false; break; case AuraType.ModDamageTaken: // dependent from basepoint sign (positive . negative) @@ -3527,24 +3485,21 @@ namespace Game.Spells return false; break; case AuraType.ModDamagePercentTaken: // check targets and basepoints (ex Recklessness) - if (!_isPositiveTarget(spellInfo, effIndex) && bp > 0) + if (!_isPositiveTarget(effect) && bp > 0) return false; break; case AuraType.AddTargetTrigger: return true; case AuraType.PeriodicTriggerSpellWithValue: case AuraType.PeriodicTriggerSpell: - if (!_isPositiveTarget(spellInfo, effIndex)) + if (!_isPositiveTarget(effect)) { SpellInfo spellTriggeredProto = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, spellInfo.Difficulty); if (spellTriggeredProto != null) { // negative targets of main spell return early - foreach (SpellEffectInfo spellTriggeredEffect in spellTriggeredProto.GetEffects()) + foreach (var spellTriggeredEffect in spellTriggeredProto.GetEffects()) { - if (spellTriggeredEffect == null) - continue; - // already seen this if (visited.Contains(Tuple.Create(spellTriggeredProto, spellTriggeredEffect.EffectIndex))) continue; @@ -3554,7 +3509,7 @@ namespace Game.Spells // if non-positive trigger cast targeted to positive target this main cast is non-positive // this will place this spell auras as debuffs - if (_isPositiveTarget(spellTriggeredProto, spellTriggeredEffect.EffectIndex) && !_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect.EffectIndex, visited)) + if (_isPositiveTarget(spellTriggeredEffect) && !_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect, visited)) return false; } } @@ -3585,7 +3540,7 @@ namespace Game.Spells case AuraType.ModAttackerRangedCritChance: case AuraType.ModAttackerSpellAndWeaponCritChance: // have positive and negative spells, check target - if (!_isPositiveTarget(spellInfo, effIndex)) + if (!_isPositiveTarget(effect)) return false; break; case AuraType.ModConfuse: @@ -3677,11 +3632,8 @@ namespace Game.Spells { // spells with at least one negative effect are considered negative // some self-applied spells have negative effects but in self casting case negative check ignored. - foreach (SpellEffectInfo spellTriggeredEffect in spellTriggeredProto.GetEffects()) + foreach (var spellTriggeredEffect in spellTriggeredProto.GetEffects()) { - if (spellTriggeredEffect == null) - continue; - // already seen this if (visited.Contains(Tuple.Create(spellTriggeredProto, spellTriggeredEffect.EffectIndex))) continue; @@ -3689,7 +3641,7 @@ namespace Game.Spells if (!spellTriggeredEffect.IsEffect()) continue; - if (!_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect.EffectIndex, visited)) + if (!_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect, visited)) return false; } } @@ -3703,18 +3655,18 @@ namespace Game.Spells { List> visited = new(); - for (byte i = 0; i < SpellConst.MaxEffects; ++i) - if (!_isPositiveEffectImpl(this, i, visited)) - NegativeEffects[i] = true; + foreach (SpellEffectInfo effect in GetEffects()) + if (!_isPositiveEffectImpl(this, effect, visited)) + NegativeEffects[(int)effect.EffectIndex] = true; // additional checks after effects marked - foreach (SpellEffectInfo effect in GetEffects()) + foreach (var spellEffectInfo in GetEffects()) { - if (effect == null || !effect.IsEffect() || !IsPositiveEffect(effect.EffectIndex)) + if (!spellEffectInfo.IsEffect() || !IsPositiveEffect(spellEffectInfo.EffectIndex)) continue; - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { // has other non positive effect? // then it should be marked negative despite of targets (ex 8510, 8511, 8893, 10267) @@ -3725,9 +3677,14 @@ namespace Game.Spells case AuraType.Transform: case AuraType.ModAttackspeed: case AuraType.ModDecreaseSpeed: - if (!IsPositive()) - NegativeEffects[(int)effect.EffectIndex] = true; + { + for (uint j = spellEffectInfo.EffectIndex + 1; j < GetEffects().Count; ++j) + if (!IsPositiveEffect(j) + && spellEffectInfo.TargetA.GetTarget() == GetEffect(j).TargetA.GetTarget() + && spellEffectInfo.TargetB.GetTarget() == GetEffect(j).TargetB.GetTarget()) + NegativeEffects[(int)spellEffectInfo.EffectIndex] = true; break; + } default: break; } @@ -3737,21 +3694,17 @@ namespace Game.Spells public void _UnloadImplicitTargetConditionLists() { // find the same instances of ConditionList and delete them. - for (int i = 0; i < _effects.Length; ++i) + foreach (var effectInfo in _effects) { - SpellEffectInfo effect = _effects[i]; - if (effect != null) - { - var cur = effect.ImplicitTargetConditions; - if (cur == null) - continue; + var cur = effectInfo.ImplicitTargetConditions; + if (cur == null) + continue; - for (var j = i; j < _effects.Length; ++j) - { - SpellEffectInfo eff = _effects[j]; - if (eff != null && eff.ImplicitTargetConditions == cur) - eff.ImplicitTargetConditions = null; - } + for (int j = (int)effectInfo.EffectIndex; j < _effects.Count; ++j) + { + SpellEffectInfo eff = _effects[j]; + if (eff.ImplicitTargetConditions == cur) + eff.ImplicitTargetConditions = null; } } } @@ -3804,14 +3757,14 @@ namespace Game.Spells return CategoryId; } - public SpellEffectInfo[] GetEffects() { return _effects; } + public List GetEffects() { return _effects; } - public SpellEffectInfo GetEffect(uint index) { return _effects[index]; } + public SpellEffectInfo GetEffect(uint index) { return _effects[(int)index]; } public bool HasTargetType(Targets target) { - foreach (SpellEffectInfo effect in _effects) - if (effect != null && (effect.TargetA.GetTarget() == target || effect.TargetB.GetTarget() == target)) + foreach (var effectInfo in _effects) + if (effectInfo.TargetA.GetTarget() == target || effectInfo.TargetB.GetTarget() == target) return true; return false; @@ -3945,7 +3898,7 @@ namespace Game.Spells public uint ExplicitTargetMask { get; set; } public SpellChainNode ChainEntry { get; set; } - SpellEffectInfo[] _effects = new SpellEffectInfo[SpellConst.MaxEffects]; + List _effects = new(); List _visuals = new(); SpellSpecificType _spellSpecific; AuraStateType _auraState; diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index d9d35dd69..0bb0d42e5 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -100,12 +100,9 @@ namespace Game.Entities bool needCheckReagents = false; // check effects - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case 0: continue; @@ -113,55 +110,55 @@ namespace Game.Entities // craft spell for crafting non-existed item (break client recipes list show) case SpellEffectName.CreateItem: case SpellEffectName.CreateLoot: + { + if (spellEffectInfo.ItemType == 0) { - if (effect.ItemType == 0) - { - // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime) - if (!spellInfo.IsLootCrafting()) - { - if (msg) - { - if (player) - player.SendSysMessage("Craft spell {0} not have create item entry.", spellInfo.Id); - else - Log.outError(LogFilter.Spells, "Craft spell {0} not have create item entry.", spellInfo.Id); - } - return false; - } - - } - // also possible IsLootCrafting case but fake item must exist anyway - else if (Global.ObjectMgr.GetItemTemplate(effect.ItemType) == null) + // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime) + if (!spellInfo.IsLootCrafting()) { if (msg) { if (player) - player.SendSysMessage("Craft spell {0} create not-exist in DB item (Entry: {1}) and then...", spellInfo.Id, effect.ItemType); + player.SendSysMessage("Craft spell {0} not have create item entry.", spellInfo.Id); else - Log.outError(LogFilter.Spells, "Craft spell {0} create not-exist in DB item (Entry: {1}) and then...", spellInfo.Id, effect.ItemType); + Log.outError(LogFilter.Spells, "Craft spell {0} not have create item entry.", spellInfo.Id); } return false; } - needCheckReagents = true; - break; } - case SpellEffectName.LearnSpell: + // also possible IsLootCrafting case but fake item must exist anyway + else if (Global.ObjectMgr.GetItemTemplate(spellEffectInfo.ItemType) == null) { - SpellInfo spellInfo2 = GetSpellInfo(effect.TriggerSpell, Difficulty.None); - if (!IsSpellValid(spellInfo2, player, msg)) + if (msg) { - if (msg) - { - if (player != null) - player.SendSysMessage("Spell {0} learn to broken spell {1}, and then...", spellInfo.Id, effect.TriggerSpell); - else - Log.outError(LogFilter.Spells, "Spell {0} learn to invalid spell {1}, and then...", spellInfo.Id, effect.TriggerSpell); - } - return false; + if (player) + player.SendSysMessage("Craft spell {0} create not-exist in DB item (Entry: {1}) and then...", spellInfo.Id, spellEffectInfo.ItemType); + else + Log.outError(LogFilter.Spells, "Craft spell {0} create not-exist in DB item (Entry: {1}) and then...", spellInfo.Id, spellEffectInfo.ItemType); } - break; + return false; } + + needCheckReagents = true; + break; + } + case SpellEffectName.LearnSpell: + { + SpellInfo spellInfo2 = GetSpellInfo(spellEffectInfo.TriggerSpell, Difficulty.None); + if (!IsSpellValid(spellInfo2, player, msg)) + { + if (msg) + { + if (player != null) + player.SendSysMessage("Spell {0} learn to broken spell {1}, and then...", spellInfo.Id, spellEffectInfo.TriggerSpell); + else + Log.outError(LogFilter.Spells, "Spell {0} learn to invalid spell {1}, and then...", spellInfo.Id, spellEffectInfo.TriggerSpell); + } + return false; + } + break; + } } } @@ -837,17 +834,14 @@ namespace Game.Entities if (entry.Difficulty != Difficulty.None) continue; - foreach (SpellEffectInfo effect in entry.GetEffects()) + foreach (var spellEffectInfo in entry.GetEffects()) { - if (effect == null) - continue; - SpellLearnSkillNode dbc_node = new(); - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.Skill: - dbc_node.skill = (SkillType)effect.MiscValue; - dbc_node.step = (ushort)effect.CalcValue(); + dbc_node.skill = (SkillType)spellEffectInfo.MiscValue; + dbc_node.step = (ushort)spellEffectInfo.CalcValue(); if (dbc_node.skill != SkillType.Riding) dbc_node.value = 1; else @@ -925,13 +919,13 @@ namespace Game.Entities { if (entry.Difficulty != Difficulty.None) continue; - - foreach (SpellEffectInfo effect in entry.GetEffects()) + + foreach (var spellEffectInfo in entry.GetEffects()) { - if (effect != null && effect.Effect == SpellEffectName.LearnSpell) + if (spellEffectInfo.Effect == SpellEffectName.LearnSpell) { var dbc_node = new SpellLearnSpellNode(); - dbc_node.Spell = effect.TriggerSpell; + dbc_node.Spell = spellEffectInfo.TriggerSpell; dbc_node.Active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) dbc_node.OverridesSpell = 0; @@ -942,7 +936,7 @@ namespace Game.Entities // talent or passive spells or skill-step spells auto-cast and not need dependent learning, // pet teaching spells must not be dependent learning (cast) // other required explicit dependent learning - dbc_node.AutoLearned = effect.TargetA.GetTarget() == Targets.UnitPet || entry.HasAttribute(SpellCustomAttributes.IsTalent) || entry.IsPassive() || entry.HasEffect(SpellEffectName.SkillStep); + dbc_node.AutoLearned = spellEffectInfo.TargetA.GetTarget() == Targets.UnitPet || entry.HasAttribute(SpellCustomAttributes.IsTalent) || entry.IsPassive() || entry.HasEffect(SpellEffectName.SkillStep); var db_node_bounds = GetSpellLearnSpellMapBounds(entry.Id); @@ -1061,20 +1055,19 @@ namespace Game.Entities continue; } - SpellEffectInfo effect = spellInfo.GetEffect(effIndex); - if (effect == null) + if (effIndex >= spellInfo.GetEffects().Count) { Log.outError(LogFilter.Sql, "Spell (Id: {0}, effIndex: {1}) listed in `spell_target_position` does not have an effect at index {2}.", spellId, effIndex, effIndex); continue; } // target facing is in degrees for 6484 & 9268... (blizz sucks) - if (effect.PositionFacing > 2 * Math.PI) - st.target_Orientation = effect.PositionFacing * (float)Math.PI / 180; + if (spellInfo.GetEffect(effIndex).PositionFacing > 2 * Math.PI) + st.target_Orientation = spellInfo.GetEffect(effIndex).PositionFacing * (float)Math.PI / 180; else - st.target_Orientation = effect.PositionFacing; + st.target_Orientation = spellInfo.GetEffect(effIndex).PositionFacing; - if (effect.TargetA.GetTarget() == Targets.DestDb || effect.TargetB.GetTarget() == Targets.DestDb) + if (spellInfo.GetEffect(effIndex).TargetA.GetTarget() == Targets.DestDb || spellInfo.GetEffect(effIndex).TargetB.GetTarget() == Targets.DestDb) { var key = new KeyValuePair(spellId, effIndex); mSpellTargetPositions[key] = st; @@ -1236,12 +1229,12 @@ namespace Game.Entities foreach (uint spellId in spellIds) { SpellInfo spellInfo = GetSpellInfo(spellId, Difficulty.None); - foreach (SpellEffectInfo effectInfo in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effectInfo == null || !effectInfo.IsAura()) + if (!spellEffectInfo.IsAura()) continue; - AuraType auraName = effectInfo.ApplyAuraName; + AuraType auraName = spellEffectInfo.ApplyAuraName; if (SubGroups.Contains(auraName)) { // count as first aura @@ -1268,7 +1261,7 @@ namespace Game.Entities { auraTypes.AddRange(SubGroups); break; - } + } if (auraTypes.Empty()) auraTypes.Add(auraType); @@ -1416,19 +1409,19 @@ namespace Game.Entities Log.outError(LogFilter.Sql, "`spell_proc` table entry for spellId {0} has wrong `HitMask` set: {1}", spellInfo.Id, procEntry.HitMask); if (procEntry.HitMask != 0 && !(Convert.ToBoolean(procEntry.ProcFlags & ProcFlags.TakenHitMask) || (Convert.ToBoolean(procEntry.ProcFlags & ProcFlags.DoneHitMask) && (procEntry.SpellPhaseMask == 0 || Convert.ToBoolean(procEntry.SpellPhaseMask & (ProcFlagsSpellPhase.Hit | ProcFlagsSpellPhase.Finish)))))) Log.outError(LogFilter.Sql, "`spell_proc` table entry for spellId {0} has `HitMask` value defined, but it won't be used for defined `ProcFlags` and `SpellPhaseMask` values", spellInfo.Id); - for (uint i = 0; i < SpellConst.MaxEffects; ++i) - if ((procEntry.DisableEffectsMask & (1u << (int)i)) != 0 && (!spellInfo.HasEffect((SpellEffectName)i) || !spellInfo.GetEffect(i).IsAura())) - Log.outError(LogFilter.Sql, $"The `spell_proc` table entry for spellId {spellInfo.Id} has DisableEffectsMask with effect {i}, but effect {i} is not an aura effect"); + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if ((procEntry.DisableEffectsMask & (1u << (int)spellEffectInfo.EffectIndex)) != 0 && !spellEffectInfo.IsAura()) + Log.outError(LogFilter.Sql, $"The `spell_proc` table entry for spellId {spellInfo.Id} has DisableEffectsMask with effect {spellEffectInfo.EffectIndex}, but effect {spellEffectInfo.EffectIndex} is not an aura effect"); if (procEntry.AttributesMask.HasFlag(ProcAttributes.ReqSpellmod)) { bool found = false; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || !effect.IsAura()) + if (!spellEffectInfo.IsAura()) continue; - if (effect.ApplyAuraName == AuraType.AddPctModifier || effect.ApplyAuraName == AuraType.AddFlatModifier) + if (spellEffectInfo.ApplyAuraName == AuraType.AddPctModifier || spellEffectInfo.ApplyAuraName == AuraType.AddFlatModifier) { found = true; break; @@ -1472,19 +1465,19 @@ namespace Game.Entities bool addTriggerFlag = false; ProcFlagsSpellType procSpellTypeMask = ProcFlagsSpellType.None; uint nonProcMask = 0; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || !effect.IsEffect()) + if (!spellEffectInfo.IsEffect()) continue; - AuraType auraName = effect.ApplyAuraName; + AuraType auraName = spellEffectInfo.ApplyAuraName; if (auraName == 0) continue; if (!IsTriggerAura(auraName)) { // explicitly disable non proccing auras to avoid losing charges on self proc - nonProcMask |= 1u << (int)effect.EffectIndex; + nonProcMask |= 1u << (int)spellEffectInfo.EffectIndex; continue; } @@ -1510,9 +1503,9 @@ namespace Game.Entities if (procSpellTypeMask == 0) { - foreach (SpellEffectInfo effectInfo in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effectInfo != null && effectInfo.IsAura()) + if (spellEffectInfo.IsAura()) { Log.outError(LogFilter.Sql, $"Spell Id {spellInfo.Id} has DBC ProcFlags {spellInfo.ProcFlags}, but it's of non-proc aura type, it probably needs an entry in `spell_proc` table to be handled correctly."); break; @@ -1526,9 +1519,9 @@ namespace Game.Entities procEntry.SchoolMask = 0; procEntry.ProcFlags = spellInfo.ProcFlags; procEntry.SpellFamilyName = 0; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - if (effect != null && effect.IsEffect() && IsTriggerAura(effect.ApplyAuraName)) - procEntry.SpellFamilyMask |= effect.SpellClassMask; + foreach (var spellEffectInfo in spellInfo.GetEffects()) + if (spellEffectInfo.IsEffect() && IsTriggerAura(spellEffectInfo.ApplyAuraName)) + procEntry.SpellFamilyMask |= spellEffectInfo.SpellClassMask; if (procEntry.SpellFamilyMask) procEntry.SpellFamilyName = spellInfo.SpellFamilyName; @@ -1537,12 +1530,12 @@ namespace Game.Entities procEntry.SpellPhaseMask = ProcFlagsSpellPhase.Hit; procEntry.HitMask = ProcFlagsHit.None; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null || !effect.IsAura()) + if (!spellEffectInfo.IsAura()) continue; - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { // Reflect auras should only proc off reflects case AuraType.ReflectSpells: @@ -1663,14 +1656,13 @@ namespace Game.Entities Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_pet_auras` does not exist", spell); continue; } - SpellEffectInfo effect = spellInfo.GetEffect(eff); - if (effect == null) + if (eff >= spellInfo.GetEffects().Count) { Log.outError(LogFilter.Spells, "Spell {0} listed in `spell_pet_auras` does not have effect at index {1}", spell, eff); continue; } - if (effect.Effect != SpellEffectName.Dummy && (effect.Effect != SpellEffectName.ApplyAura || effect.ApplyAuraName != AuraType.Dummy)) + if (spellInfo.GetEffect(eff).Effect != SpellEffectName.Dummy && (spellInfo.GetEffect(eff).Effect != SpellEffectName.ApplyAura || spellInfo.GetEffect(eff).ApplyAuraName != AuraType.Dummy)) { Log.outError(LogFilter.Spells, "Spell {0} listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); continue; @@ -1683,7 +1675,7 @@ namespace Game.Entities continue; } - PetAura pa = new(pet, aura, effect.TargetA.GetTarget() == Targets.UnitPet, effect.CalcValue()); + PetAura pa = new(pet, aura, spellInfo.GetEffect(eff).TargetA.GetTarget() == Targets.UnitPet, spellInfo.GetEffect(eff).CalcValue()); mSpellPetAuraMap[(spell << 8) + eff] = pa; } ++count; @@ -1704,11 +1696,11 @@ namespace Game.Entities if (!spellInfo.HasAttribute(SpellAttr2.PreserveEnchantInArena) || !spellInfo.HasAttribute(SpellAttr0.NotShapeshift)) continue; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect != null && effect.Effect == SpellEffectName.EnchantItemTemporary) + if (spellEffectInfo.Effect == SpellEffectName.EnchantItemTemporary) { - int enchId = effect.MiscValue; + int enchId = spellEffectInfo.MiscValue; var ench = CliDB.SpellItemEnchantmentStorage.LookupByKey((uint)enchId); if (ench == null) continue; @@ -1792,10 +1784,10 @@ namespace Game.Entities if (effect >= 0) { - foreach (SpellEffectInfo eff in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (eff != null && eff.CalcValue() == Math.Abs(effect)) - Log.outError(LogFilter.Sql, $"The spell {Math.Abs(trigger)} Effect: {Math.Abs(effect)} listed in `spell_linked_spell` has same bp{eff.EffectIndex} like effect (possible hack)"); + if (spellEffectInfo.CalcValue() == Math.Abs(effect)) + Log.outError(LogFilter.Sql, $"The spell {Math.Abs(trigger)} Effect: {Math.Abs(effect)} listed in `spell_linked_spell` has same bp{spellEffectInfo.EffectIndex} like effect (possible hack)"); } } @@ -1868,7 +1860,7 @@ namespace Game.Entities } public void LoadPetDefaultSpells() - { + { uint oldMSTime = Time.GetMSTime(); mPetDefaultSpellsMap.Clear(); @@ -1882,11 +1874,11 @@ namespace Game.Entities { if (spellEntry.Difficulty != Difficulty.None) { - foreach (SpellEffectInfo effect in spellEntry.GetEffects()) + foreach (var spellEffectInfo in spellEntry.GetEffects()) { - if (effect != null && (effect.Effect == SpellEffectName.Summon || effect.Effect == SpellEffectName.SummonPet)) + if (spellEffectInfo.Effect == SpellEffectName.Summon || spellEffectInfo.Effect == SpellEffectName.SummonPet) { - int creature_id = effect.MiscValue; + int creature_id = spellEffectInfo.MiscValue; CreatureTemplate cInfo = Global.ObjectMgr.GetCreatureTemplate((uint)creature_id); if (cInfo == null) continue; @@ -2501,7 +2493,7 @@ namespace Game.Entities continue; } - mServersideSpellNames.Add(new (spellId, spellsResult.Read(61))); + mServersideSpellNames.Add(new(spellId, spellsResult.Read(61))); SpellInfo spellInfo = new(mServersideSpellNames.Last().Name, difficulty, spellEffects[(spellId, difficulty)]); spellInfo.CategoryId = spellsResult.Read(2); @@ -2631,12 +2623,9 @@ namespace Game.Entities foreach (var spellInfo in mSpellInfoMap.Values) { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.ModPossess: case AuraType.ModConfuse: @@ -2660,7 +2649,7 @@ namespace Game.Entities break; } - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.OpenStable: // No point in saving this, since the stable dialog can't be open on aura load anyway. // Auras that require both caster & target to be in world cannot be saved @@ -2677,7 +2666,7 @@ namespace Game.Entities break; } - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.SchoolDamage: case SpellEffectName.HealthLeech: @@ -2694,7 +2683,7 @@ namespace Game.Entities break; } - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.SchoolDamage: case SpellEffectName.WeaponDamage: @@ -2728,32 +2717,32 @@ namespace Game.Entities case SpellEffectName.EnchantItemTemporary: case SpellEffectName.EnchantItemPrismatic: case SpellEffectName.EnchantHeldItem: + { + // only enchanting profession enchantments procs can stack + if (IsPartOfSkillLine(SkillType.Enchanting, spellInfo.Id)) { - // only enchanting profession enchantments procs can stack - if (IsPartOfSkillLine(SkillType.Enchanting, spellInfo.Id)) + uint enchantId = (uint)spellEffectInfo.MiscValue; + var enchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchantId); + for (var s = 0; s < ItemConst.MaxItemEnchantmentEffects; ++s) { - uint enchantId = (uint)effect.MiscValue; - var enchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchantId); - for (var s = 0; s < ItemConst.MaxItemEnchantmentEffects; ++s) + if (enchant.Effect[s] != ItemEnchantmentType.CombatSpell) + continue; + + foreach (SpellInfo procInfo in _GetSpellInfo(enchant.EffectArg[s])) { - if (enchant.Effect[s] != ItemEnchantmentType.CombatSpell) + + // if proced directly from enchantment, not via proc aura + // NOTE: Enchant Weapon - Blade Ward also has proc aura spell and is proced directly + // however its not expected to stack so this check is good + if (procInfo.HasAura(AuraType.ProcTriggerSpell)) continue; - foreach (SpellInfo procInfo in _GetSpellInfo(enchant.EffectArg[s])) - { - - // if proced directly from enchantment, not via proc aura - // NOTE: Enchant Weapon - Blade Ward also has proc aura spell and is proced directly - // however its not expected to stack so this check is good - if (procInfo.HasAura(AuraType.ProcTriggerSpell)) - continue; - - procInfo.AttributesCu |= SpellCustomAttributes.EnchantProc; - } + procInfo.AttributesCu |= SpellCustomAttributes.EnchantProc; } } - break; } + break; + } } } @@ -2761,14 +2750,11 @@ namespace Game.Entities if (!spellInfo.HasAttribute(SpellAttr3.IgnoreHitResult)) { bool setFlag = false; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - if (effect.IsEffect()) + if (spellEffectInfo.IsEffect()) { - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.SchoolDamage: case SpellEffectName.WeaponDamage: @@ -2789,42 +2775,42 @@ namespace Game.Entities case SpellEffectName.ApplyAuraOnPet: case SpellEffectName.ApplyAreaAuraSummons: case SpellEffectName.ApplyAreaAuraPartyNonrandom: - { - 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; + { + if (spellEffectInfo.ApplyAuraName == AuraType.PeriodicDamage || + spellEffectInfo.ApplyAuraName == AuraType.PeriodicDamagePercent || + spellEffectInfo.ApplyAuraName == AuraType.PeriodicDummy || + spellEffectInfo.ApplyAuraName == AuraType.PeriodicLeech || + spellEffectInfo.ApplyAuraName == AuraType.PeriodicHealthFunnel || + spellEffectInfo.ApplyAuraName == AuraType.PeriodicDummy) break; - } + + goto default; + } + default: + { + // No value and not interrupt cast or crowd control without SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY flag + if (spellEffectInfo.CalcValue() == 0 && !((spellEffectInfo.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) @@ -2855,7 +2841,7 @@ namespace Game.Entities { case SpellFamilyNames.Warrior: // Shout / Piercing Howl - if (spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x20000u)/* || spellInfo->SpellFamilyFlags[1] & 0x20*/) + if (spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x20000u)/* || spellInfo.SpellFamilyFlags[1] & 0x20*/) spellInfo.AttributesCu |= SpellCustomAttributes.AuraCC; break; case SpellFamilyNames.Druid: @@ -2882,18 +2868,15 @@ namespace Game.Entities { bool allNonBinary = true; bool overrideAttr = false; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - if (effect.IsAura() && effect.TriggerSpell != 0) + if (spellEffectInfo.IsAura() && spellEffectInfo.TriggerSpell != 0) { - switch (effect.ApplyAuraName) + switch (spellEffectInfo.ApplyAuraName) { case AuraType.PeriodicTriggerSpell: case AuraType.PeriodicTriggerSpellWithValue: - SpellInfo triggerSpell = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); + SpellInfo triggerSpell = Global.SpellMgr.GetSpellInfo(spellEffectInfo.TriggerSpell, Difficulty.None); if (triggerSpell != null) { overrideAttr = true; @@ -2919,23 +2902,1266 @@ namespace Game.Entities Log.outInfo(LogFilter.ServerLoading, "Loaded spell custom attributes in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime)); } - + + void ApplySpellFix(int[] spellIds, Action fix) + { + foreach (uint spellId in spellIds) + { + var range = _GetSpellInfo(spellId); + if (range == null) + { + Log.outError(LogFilter.ServerLoading, $"Spell info correction specified for non-existing spell {spellId}"); + continue; + } + + foreach (SpellInfo spellInfo in range) + fix(spellInfo); + } + } + + void ApplySpellEffectFix(SpellInfo spellInfo, uint effectIndex, Action fix) + { + if (spellInfo.GetEffects().Count <= effectIndex) + { + Log.outError(LogFilter.ServerLoading, $"Spell effect info correction specified for non-existing effect {effectIndex} of spell {spellInfo.Id}"); + return; + } + + fix(spellInfo.GetEffect(effectIndex)); + } + public void LoadSpellInfoCorrections() { uint oldMSTime = Time.GetMSTime(); + // Some spells have no amplitude set + { + ApplySpellFix(new[] { + 6727, // Poison Mushroom + 7331, // Healing Aura (TEST) (Rank 1) + /* + 30400, // Nether Beam - Perseverance + Blizzlike to have it disabled? DBC says: + "This is currently turned off to increase performance. Enable this to make it fire more frequently." + */ + 34589, // Dangerous Water + 52562, // Arthas Zombie Catcher + 57550, // Tirion Aggro + 65755 + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.ApplyAuraPeriod = 1 * Time.InMilliseconds; + }); + }); + + ApplySpellFix(new[] { + 24707, // Food + 26263, // Dim Sum + 29055 // Refreshing Red Apple + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.ApplyAuraPeriod = 1 * Time.InMilliseconds;; + }); + }); + + // Karazhan - Chess NPC AI, action timer + ApplySpellFix(new[] { 37504 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.ApplyAuraPeriod = 5 * Time.InMilliseconds;; + }); + }); + + // Vomit + ApplySpellFix(new[] { 43327 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.ApplyAuraPeriod = 1 * Time.InMilliseconds;; + }); + }); + } + + // specific code for cases with no trigger spell provided in field + { + // Brood Affliction: Bronze + ApplySpellFix(new[] { 23170 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 23171; + }); + }); + + // Feed Captured Animal + ApplySpellFix(new[] { 29917 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 29916; + }); + }); + + // Remote Toy + ApplySpellFix(new[] { 37027 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 37029; + }); + }); + + // Eye of Grillok + ApplySpellFix(new[] { 38495 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 38530; + }); + }); + + // Tear of Azzinoth Summon Channel - it's not really supposed to do anything, and this only prevents the console spam + ApplySpellFix(new[] { 39857 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 39856; + }); + }); + + // Personalized Weather + ApplySpellFix(new[] { 46736 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 46737; + spellEffectInfo.ApplyAuraName = AuraType.PeriodicTriggerSpell; + }); + }); + } + + // Allows those to crit + ApplySpellFix(new[] { + 379, // Earth Shield + 71607, // Item - Bauble of True Blood 10m + 71646, // Item - Bauble of True Blood 25m + 71610, // Item - Althor's Abacus trigger 10m + 71641 // Item - Althor's Abacus trigger 25m + }, spellInfo => + { + // We need more spells to find a general way (if there is any) + spellInfo.DmgClass = SpellDmgClass.Magic; + }); + + ApplySpellFix(new[] { + 63026, // Summon Aspirant Test NPC (HACK: Target shouldn't be changed) + 63137 // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination) + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestDb); + }); + }); + + // Summon Skeletons + ApplySpellFix(new[] { 52611, 52612 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.MiscValueB = 64; + }); + }); + + ApplySpellFix(new[] { + 40244, // Simon Game Visual + 40245, // Simon Game Visual + 40246, // Simon Game Visual + 40247, // Simon Game Visual + 42835 // Spout, remove damage effect, only anim is needed + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.None; + }); + }); + + ApplySpellFix(new[] { + 63665, // Charge (Argent Tournament emote on riders) + 31298, // Sleep (needs target selection script) + 51904, // Summon Ghouls On Scarlet Crusade (this should use conditions table, script for this spell needs to be fixed) + 68933, // Wrath of Air Totem rank 2 (Aura) + 29200 // Purify Helboar Meat + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitCaster); + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(); + }); + }); + + ApplySpellFix(new[] { + 56690, // Thrust Spear + 60586, // Mighty Spear Thrust + 60776, // Claw Swipe + 60881, // Fatal Strike + 60864 // Jaws of Death + }, spellInfo => + { + spellInfo.AttributesEx4 |= SpellAttr4.FixedDamage; + }); + + // Howl of Azgalor + ApplySpellFix(new[] { 31344 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards100); // 100yards instead of 50000?! + }); + }); + + ApplySpellFix(new[] { + 42818, // Headless Horseman - Wisp Flight Port + 42821 // Headless Horseman - Wisp Flight Missile + }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(6); // 100 yards + }); + + // They Must Burn Bomb Aura (self) + ApplySpellFix(new[] { 36350 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 36325; // They Must Burn Bomb Drop (DND) + }); + }); + + // Execute + ApplySpellFix(new[] { 5308 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.CantTriggerProc; + }); + + ApplySpellFix(new[] { + 31347, // Doom + 36327, // Shoot Arcane Explosion Arrow + 39365, // Thundering Storm + 41071, // Raise Dead (HACK) + 42442, // Vengeance Landing Cannonfire + 42611, // Shoot + 44978, // Wild Magic + 45001, // Wild Magic + 45002, // Wild Magic + 45004, // Wild Magic + 45006, // Wild Magic + 45010, // Wild Magic + 45761, // Shoot Gun + 45863, // Cosmetic - Incinerate to Random Target + 48246, // Ball of Flame + 41635, // Prayer of Mending + 44869, // Spectral Blast + 45027, // Revitalize + 45976, // Muru Portal Channel + 52124, // Sky Darkener Assault + 52479, // Gift of the Harvester + 61588, // Blazing Harpoon + 55479, // Force Obedience + 28560, // Summon Blizzard (Sapphiron) + 53096, // Quetz'lun's Judgment + 70743, // AoD Special + 70614, // AoD Special - Vegard + 4020, // Safirdrang's Chill + 52438, // Summon Skittering Swarmer (Force Cast) + 52449, // Summon Skittering Infector (Force Cast) + 53609, // Summon Anub'ar Assassin (Force Cast) + 53457, // Summon Impale Trigger (AoE) + 45907, // Torch Target Picker + 52953, // Torch + 58121 // Torch + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + }); + + ApplySpellFix(new[] { + 36384 // Skartax Purple Beam + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 2; + }); + + ApplySpellFix(new[] { + 28542, // Life Drain - Sapphiron + 29213, // Curse of the Plaguebringer - Noth + 29576, // Multi-Shot + 37790, // Spread Shot + 39992, // Needle Spine + 40816, // Saber Lash + 41303, // Soul Drain + 41376, // Spite + 45248, // Shadow Blades + 46771, // Flame Sear + 66588 // Flaming Spear + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 3; + }); + + ApplySpellFix(new[] { + 38310, // Multi-Shot + 53385 // Divine Storm (Damage) + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 4; + }); + + ApplySpellFix(new[] { + 42005, // Bloodboil + 38296, // Spitfire Totem + 37676, // Insidious Whisper + 46008, // Negative Energy + 45641, // Fire Bloom + 55665, // Life Drain - Sapphiron (H) + 28796 // Poison Bolt Volly - Faerlina + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 5; + }); + + // Curse of the Plaguebringer - Noth (H) + ApplySpellFix(new[] { 54835 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 8; + }); + + ApplySpellFix(new[] { + 40827, // Sinful Beam + 40859, // Sinister Beam + 40860, // Vile Beam + 40861, // Wicked Beam + 54098 // Poison Bolt Volly - Faerlina (H) + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 10; + }); + + // Unholy Frenzy + ApplySpellFix(new[] { 50312 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 15; + }); + + // Murmur's Touch + ApplySpellFix(new[] { 33711, 38794 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TriggerSpell = 33760; + }); + }); + + // Fingers of Frost + ApplySpellFix(new[] { 44544 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.SpellClassMask[0] |= 0x20000; + }); + }); + + ApplySpellFix(new[] { + 52212, // Death and Decay + 41485, // Deadly Poison - Black Temple + 41487 // Envenom - Black Temple + }, spellInfo => + { + spellInfo.AttributesEx6 |= SpellAttr6.CanTargetInvisible; + }); + + // Oscillation Field + ApplySpellFix(new[] { 37408 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; + }); + + // Crafty's Ultra-Advanced Proto-Typical Shortening Blaster + ApplySpellFix(new[] { 51912 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.ApplyAuraPeriod = 3000; + }); + }); + + // Nether Portal - Perseverence + ApplySpellFix(new[] { 30421 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 2, spellEffectInfo => + { + spellEffectInfo.BasePoints += 30000; + }); + }); + + // Parasitic Shadowfiend Passive + ApplySpellFix(new[] { 41913 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.ApplyAuraName = AuraType.Dummy; // proc debuff, and summon infinite fiends + }); + }); + + ApplySpellFix(new[] { + 27892, // To Anchor 1 + 27928, // To Anchor 1 + 27935, // To Anchor 1 + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); + }); + }); + + // Wrath of the Plaguebringer + ApplySpellFix(new[] { 29214, 54836 }, spellInfo => + { + // target allys instead of enemies, target A is src_caster, spells with effect like that have ally target + // this is the only known exception, probably just wrong data + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.UnitSrcAreaAlly); + }); + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.UnitSrcAreaAlly); + }); + }); + + // Vampiric Embrace + ApplySpellFix(new[] { 15290 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.NoInitialAggro; + }); + + // Earthbind Totem (instant pulse) + ApplySpellFix(new[] { 6474 }, spellInfo => + { + spellInfo.AttributesEx5 |= SpellAttr5.StartPeriodicAtApply; + }); + + ApplySpellFix(new[] { + 70728, // Exploit Weakness (needs target selection script) + 70840 // Devious Minds (needs target selection script) + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitCaster); + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.UnitPet); + }); + }); + + // Ride Carpet + ApplySpellFix(new[] { 45602 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.BasePoints = 0; // force seat 0, vehicle doesn't have the required seat flags for "no seat specified (-1)" + }); + }); + + // Easter Lay Noblegarden Egg Aura - Interrupt flags copied from aura which this aura is linked with + ApplySpellFix(new[] { 61719 }, spellInfo => + { + spellInfo.AuraInterruptFlags = SpellAuraInterruptFlags.HostileActionReceived | SpellAuraInterruptFlags.Damage; + }); + + ApplySpellFix(new[] { + 71838, // Drain Life - Bryntroll Normal + 71839 // Drain Life - Bryntroll Heroic + }, spellInfo => + { + spellInfo.AttributesEx2 |= SpellAttr2.CantCrit; + }); + + ApplySpellFix(new[] { + 56606, // Ride Jokkum + 61791 // Ride Vehicle (Yogg-Saron) + }, spellInfo => + { + /// @todo: remove this when basepoints of all Ride Vehicle auras are calculated correctly + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.BasePoints = 1; + }); + }); + + // Black Magic + ApplySpellFix(new[] { 59630 }, spellInfo => + { + spellInfo.Attributes |= SpellAttr0.Passive; + }); + + // Paralyze + ApplySpellFix(new[] { 48278 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; + }); + + ApplySpellFix(new[] { + 51798, // Brewfest - Relay Race - Intro - Quest Complete + 47134 // Quest Complete + }, spellInfo => + { + //! HACK: This spell break quest complete for alliance and on retail not used + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.None; + }); + }); + + // Siege Cannon (Tol Barad) + ApplySpellFix(new[] { 85123 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitSrcAreaEntry); + }); + }); + + // Gathering Storms + ApplySpellFix(new[] { 198300 }, spellInfo => + { + spellInfo.ProcCharges = 1; // override proc charges, has 0 (unlimited) in db2 + }); + + ApplySpellFix(new[] { + 42490, // Energized! + 42492, // Cast Energized + 43115 // Plague Vial + }, spellInfo => + { + spellInfo.AttributesEx |= SpellAttr1.NoThreat; + }); + + // Test Ribbon Pole Channel + ApplySpellFix(new[] { 29726 }, spellInfo => + { + spellInfo.ChannelInterruptFlags &= ~SpellAuraInterruptFlags.Action; + }); + + // Sic'em + ApplySpellFix(new[] { 42767 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitNearbyEntry); + }); + }); + + // Burn Body + ApplySpellFix(new[] { 42793 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 2, spellEffectInfo => + { + spellEffectInfo.MiscValue = 24008; // Fallen Combatant + }); + }); + + // Gift of the Naaru (priest and monk variants) + ApplySpellFix(new[] { 59544, 121093 }, spellInfo => + { + spellInfo.SpellFamilyFlags[2] = 0x80000000; + }); + + ApplySpellFix(new[] { + 50661, // Weakened Resolve + 68979, // Unleashed Souls + 48714, // Compelled + 7853, // The Art of Being a Water Terror: Force Cast on Player + }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd + }); + + // Summon Corpse Scarabs + ApplySpellFix(new[] { 28864, 29105 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); + }); + }); + + // + // VIOLET HOLD SPELLS + // + // Water Globule (Ichoron) + ApplySpellFix(new[] { 54258, 54264, 54265, 54266, 54267 }, spellInfo => + { + // in 3.3.5 there is only one radius in dbc which is 0 yards in this case + // use max radius from 4.3.4 + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards25); + }); + }); + // ENDOF VIOLET HOLD + + // + // ULDUAR SPELLS + // + // Pursued (Flame Leviathan) + ApplySpellFix(new[] { 62374 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards50000); // 50000yd + }); + }); + + // Focused Eyebeam Summon Trigger (Kologarn) + ApplySpellFix(new[] { 63342 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + }); + + ApplySpellFix(new[] { + 65584, // Growth of Nature (Freya) + 64381 // Strength of the Pack (Auriaya) + }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; + }); + + ApplySpellFix(new[] { + 63018, // Searing Light (XT-002) + 65121, // Searing Light (25m) (XT-002) + 63024, // Gravity Bomb (XT-002) + 64234 // Gravity Bomb (25m) (XT-002) + }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + }); + + ApplySpellFix(new[] { + 64386, // Terrifying Screech (Auriaya) + 64389, // Sentinel Blast (Auriaya) + 64678 // Sentinel Blast (Auriaya) + }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(28); // 5 seconds, wrong DBC data? + }); + + // Potent Pheromones (Freya) + ApplySpellFix(new[] { 64321 }, spellInfo => + { + // spell should dispel area aura, but doesn't have the attribute + // may be db data bug, or blizz may keep reapplying area auras every update with checking immunity + // that will be clear if we get more spells with problem like this + spellInfo.AttributesEx |= SpellAttr1.DispelAurasOnImmunity; + }); + + // Blizzard (Thorim) + ApplySpellFix(new[] { 62576, 62602 }, spellInfo => + { + // DBC data is wrong for 0, it's a different dynobject target than 1 + // Both effects should be shared by the same DynObject + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestCasterLeft); + }); + }); + + // Spinning Up (Mimiron) + ApplySpellFix(new[] { 63414 }, spellInfo => + { + spellInfo.ChannelInterruptFlags = SpellAuraInterruptFlags.None; + spellInfo.ChannelInterruptFlags2 = SpellAuraInterruptFlags2.None; + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.UnitCaster); + }); + }); + + // Rocket Strike (Mimiron) + ApplySpellFix(new[] { 63036 }, spellInfo => + { + spellInfo.Speed = 0; + }); + + // Magnetic Field (Mimiron) + ApplySpellFix(new[] { 64668 }, spellInfo => + { + spellInfo.Mechanic = Mechanics.None; + }); + + // Empowering Shadows (Yogg-Saron) + ApplySpellFix(new[] { 64468, 64486 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 3; // same for both modes? + }); + + // Cosmic Smash (Algalon the Observer) + ApplySpellFix(new[] { 62301 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + }); + + // Cosmic Smash (Algalon the Observer) + ApplySpellFix(new[] { 64598 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 3; + }); + + // Cosmic Smash (Algalon the Observer) + ApplySpellFix(new[] { 62293 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.DestCaster); + }); + }); + + // Cosmic Smash (Algalon the Observer) + ApplySpellFix(new[] { 62311, 64596 }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(6); // 100yd + }); + + ApplySpellFix(new[] { + 64014, // Expedition Base Camp Teleport + 64024, // Conservatory Teleport + 64025, // Halls of Invention Teleport + 64028, // Colossal Forge Teleport + 64029, // Shattered Walkway Teleport + 64030, // Antechamber Teleport + 64031, // Scrapyard Teleport + 64032, // Formation Grounds Teleport + 65042 // Prison of Yogg-Saron Teleport + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestDb); + }); + }); + // ENDOF ULDUAR SPELLS + + // + // TRIAL OF THE CRUSADER SPELLS + // + // Infernal Eruption + ApplySpellFix(new[] { 66258 }, spellInfo => + { + // increase duration from 15 to 18 seconds because caster is already + // unsummoned when spell missile hits the ground so nothing happen in result + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(85); + }); + // ENDOF TRIAL OF THE CRUSADER SPELLS + + // + // ICECROWN CITADEL SPELLS + // + ApplySpellFix(new[] { + 70781, // Light's Hammer Teleport + 70856, // Oratory of the Damned Teleport + 70857, // Rampart of Skulls Teleport + 70858, // Deathbringer's Rise Teleport + 70859, // Upper Spire Teleport + 70860, // Frozen Throne Teleport + 70861 // Sindragosa's Lair Teleport + }, spellInfo => + { + // THESE SPELLS ARE WORKING CORRECTLY EVEN WITHOUT THIS HACK + // THE ONLY REASON ITS HERE IS THAT CURRENT GRID SYSTEM + // DOES NOT ALLOW FAR OBJECT SELECTION (dist > 333) + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestDb); + }); + }); + + // Coldflame (Lord Marrowgar) + ApplySpellFix(new[] { 69146 }, spellInfo => + { + spellInfo.AttributesEx4 &= ~SpellAttr4.IgnoreResistances; + }); + + // Shadow's Fate + ApplySpellFix(new[] { 71169 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; + }); + + // Lock Players and Tap Chest + ApplySpellFix(new[] { 72347 }, spellInfo => + { + spellInfo.AttributesEx3 &= ~SpellAttr3.NoInitialAggro; + }); + + // Resistant Skin (Deathbringer Saurfang adds) + ApplySpellFix(new[] { 72723 }, spellInfo => + { + // this spell initially granted Shadow damage immunity, however it was removed but the data was left in client + ApplySpellEffectFix(spellInfo, 2, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.None; + }); + }); + + // Coldflame Jets (Traps after Saurfang) + ApplySpellFix(new[] { 70460 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(1); // 10 seconds + }); + + ApplySpellFix(new[] { + 71412, // Green Ooze Summon (Professor Putricide) + 71415 // Orange Ooze Summon (Professor Putricide) + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitTargetAny); + }); + }); + + // Awaken Plagued Zombies + ApplySpellFix(new[] { 71159 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(21); + }); + + // Volatile Ooze Beam Protection (Professor Putricide) + ApplySpellFix(new[] { 70530 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.ApplyAura; // for an unknown reason this was SPELL_EFFECT_APPLY_AREA_AURA_RAID + }); + }); + + // Mutated Strength (Professor Putricide) + ApplySpellFix(new[] { 71604 }, spellInfo => + { + // THIS IS HERE BECAUSE COOLDOWN ON CREATURE PROCS WERE NOT IMPLEMENTED WHEN THE SCRIPT WAS WRITTEN + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.None; + }); + }); + + // Unbound Plague (Professor Putricide) (needs target selection script) + ApplySpellFix(new[] { 70911 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.UnitTargetEnemy); + }); + }); + + // Empowered Flare (Blood Prince Council) + ApplySpellFix(new[] { 71708 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; + }); + + // Swarming Shadows + ApplySpellFix(new[] { 71266 }, spellInfo => + { + spellInfo.RequiredAreasID = 0; // originally, these require area 4522, which is... outside of Icecrown Citadel + }); + + // Corruption + ApplySpellFix(new[] { 70602 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; + }); + + // Column of Frost (visual marker) + ApplySpellFix(new[] { 70715 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(32); // 6 seconds (missing) + }); + + // Mana Void (periodic aura) + ApplySpellFix(new[] { 71085 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(9); // 30 seconds (missing) + }); + + // Summon Suppressor (needs target selection script) + ApplySpellFix(new[] { 70936 }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(157); // 90yd + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitTargetAny); + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(); + }); + }); + + // Sindragosa's Fury + ApplySpellFix(new[] { 70598 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestDest); + }); + }); + + // Frost Bomb + ApplySpellFix(new[] { 69846 }, spellInfo => + { + spellInfo.Speed = 0.0f; // This spell's summon happens instantly + }); + + // Chilled to the Bone + ApplySpellFix(new[] { 70106 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; + spellInfo.AttributesEx6 |= SpellAttr6.IgnoreCasterDamageModifiers; + }); + + // Ice Lock + ApplySpellFix(new[] { 71614 }, spellInfo => + { + spellInfo.Mechanic = Mechanics.Stun; + }); + + // Defile + ApplySpellFix(new[] { 72762 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(559); // 53 seconds + }); + + // Defile + ApplySpellFix(new[] { 72743 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(22); // 45 seconds + }); + + // Defile + ApplySpellFix(new[] { 72754 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); // 200yd + }); + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); // 200yd + }); + }); + + // Val'kyr Target Search + ApplySpellFix(new[] { 69030 }, spellInfo => + { + spellInfo.Attributes |= SpellAttr0.UnaffectedByInvulnerability; + }); + + // Raging Spirit Visual + ApplySpellFix(new[] { 69198 }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd + }); + + // Harvest Soul + ApplySpellFix(new[] { 73655 }, spellInfo => + { + spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; + }); + + // Summon Shadow Trap + ApplySpellFix(new[] { 73540 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(3); // 60 seconds + }); + + // Shadow Trap (visual) + ApplySpellFix(new[] { 73530 }, spellInfo => + { + spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(27); // 3 seconds + }); + + // Summon Spirit Bomb + ApplySpellFix(new[] { 74302 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 2; + }); + + // Summon Spirit Bomb + ApplySpellFix(new[] { 73579 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards25); // 25yd + }); + }); + + // Raise Dead + ApplySpellFix(new[] { 72376 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 3; + }); + + // Jump + ApplySpellFix(new[] { 71809 }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(5); // 40yd + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); // 10yd + spellEffectInfo.MiscValue = 190; + }); + }); + + // Broken Frostmourne + ApplySpellFix(new[] { 72405 }, spellInfo => + { + spellInfo.AttributesEx |= SpellAttr1.NoThreat; + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards20); // 20yd + }); + }); + // ENDOF ICECROWN CITADEL SPELLS + + // + // RUBY SANCTUM SPELLS + // + // Soul Consumption + ApplySpellFix(new[] { 74799 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 1, spellEffectInfo => + { + spellEffectInfo.RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards12); + }); + }); + + // Twilight Mending + ApplySpellFix(new[] { 75509 }, spellInfo => + { + spellInfo.AttributesEx6 |= SpellAttr6.CanTargetInvisible; + spellInfo.AttributesEx2 |= SpellAttr2.CanTargetNotInLos; + }); + + // Awaken Flames + ApplySpellFix(new[] { 75888 }, spellInfo => + { + spellInfo.AttributesEx |= SpellAttr1.CantTargetSelf; + }); + // ENDOF RUBY SANCTUM SPELLS + + // + // EYE OF ETERNITY SPELLS + // + ApplySpellFix(new[] { + 57473, // Arcane Storm bonus explicit visual spell + 57431, // Summon Static Field + 56091, // Flame Spike (Wyrmrest Skytalon) + 56092, // Engulf in Flames (Wyrmrest Skytalon) + 57090, // Revivify (Wyrmrest Skytalon) + 57143 // Life Burst (Wyrmrest Skytalon) + }, spellInfo => + { + // All spells work even without these changes. The LOS attribute is due to problem + // from collision between maps & gos with active destroyed state. + spellInfo.AttributesEx2 |= SpellAttr2.CanTargetNotInLos; + }); + + // Arcane Barrage (cast by players and NONMELEEDAMAGELOG with caster Scion of Eternity (original caster)). + ApplySpellFix(new[] { 63934 }, spellInfo => + { + // This would never crit on retail and it has attribute for SPELL_ATTR3_NO_DONE_BONUS because is handled from player, + // until someone figures how to make scions not critting without hack and without making them main casters this should stay here. + spellInfo.AttributesEx2 |= SpellAttr2.CantCrit; + }); + // ENDOF EYE OF ETERNITY SPELLS + + ApplySpellFix(new[] { + 40055, // Introspection + 40165, // Introspection + 40166, // Introspection + 40167, // Introspection + }, spellInfo => + { + spellInfo.Attributes |= SpellAttr0.Negative1; + }); + + // + // STONECORE SPELLS + // + ApplySpellFix(new[] { + 95284, // Teleport (from entrance to Slabhide) + 95285 // Teleport (from Slabhide to entrance) + }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetB = new SpellImplicitTargetInfo(Targets.DestDb); + }); + }); + // ENDOF STONECORE SPELLS + + // + // HALLS OF ORIGINATION SPELLS + // + ApplySpellFix(new[] { + 76606, // Disable Beacon Beams L + 76608 // Disable Beacon Beams R + }, spellInfo => + { + // Little hack, Increase the radius so it can hit the Cave In Stalkers in the platform. + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.MaxRadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards45); + }); + }); + + // ENDOF HALLS OF ORIGINATION SPELLS + + // Threatening Gaze + ApplySpellFix(new[] { 24314 }, spellInfo => + { + spellInfo.AuraInterruptFlags |= SpellAuraInterruptFlags.Action | SpellAuraInterruptFlags.Moving | SpellAuraInterruptFlags.Anim; + }); + + // Travel Form (dummy) - cannot be cast indoors. + ApplySpellFix(new[] { 783 }, spellInfo => + { + spellInfo.Attributes |= SpellAttr0.OutdoorsOnly; + }); + + // Tree of Life (Passive) + ApplySpellFix(new[] { 5420 }, spellInfo => + { + spellInfo.Stances = 1ul << ((int)ShapeShiftForm.TreeOfLife - 1); + }); + + // Feral Charge (Cat Form) + ApplySpellFix(new[] { 49376 }, spellInfo => + { + spellInfo.AttributesEx3 &= ~SpellAttr3.CantTriggerProc; + }); + + // Gaze of Occu'thar + ApplySpellFix(new[] { 96942 }, spellInfo => + { + spellInfo.AttributesEx &= ~SpellAttr1.Channeled1; + }); + + // Evolution + ApplySpellFix(new[] { 75610 }, spellInfo => + { + spellInfo.MaxAffectedTargets = 1; + }); + + // Evolution + ApplySpellFix(new[] { 75697 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.UnitSrcAreaEntry); + }); + }); + + // + // ISLE OF CONQUEST SPELLS + // + // Teleport + ApplySpellFix(new[] { 66551 }, spellInfo => + { + spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd + }); + // ENDOF ISLE OF CONQUEST SPELLS + + // Aura of Fear + ApplySpellFix(new[] { 40453 }, spellInfo => + { + // Bad DBC data? Copying 25820 here due to spell description + // either is a periodic with chance on tick, or a proc + + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.ApplyAuraName = AuraType.ProcTriggerSpell; + spellEffectInfo.ApplyAuraPeriod = 0; + }); + spellInfo.ProcChance = 10; + }); + + // + // FIRELANDS SPELLS + // + // Torment Searcher + ApplySpellFix(new[] { 99253 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.MaxRadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards15); + }); + }); + + // Torment Damage + ApplySpellFix(new[] { 99256 }, spellInfo => + { + spellInfo.Attributes |= SpellAttr0.Negative1; + }); + + // Blaze of Glory + ApplySpellFix(new[] { 99252 }, spellInfo => + { + spellInfo.AuraInterruptFlags |= SpellAuraInterruptFlags.LeaveWorld; + }); + // ENDOF FIRELANDS SPELLS + + // + // ANTORUS THE BURNING THRONE SPELLS + // + + // Decimation + ApplySpellFix(new[] { 244449 }, spellInfo => + { + // For some reason there is a instakill effect that serves absolutely no purpose. + // Until we figure out what it's actually used for we disable it. + ApplySpellEffectFix(spellInfo, 2, spellEffectInfo => + { + spellEffectInfo.Effect = SpellEffectName.None; + }); + }); + + // ENDOF ANTORUS THE BURNING THRONE SPELLS + + // Summon Master Li Fei + ApplySpellFix(new[] { 102445 }, spellInfo => + { + ApplySpellEffectFix(spellInfo, 0, spellEffectInfo => + { + spellEffectInfo.TargetA = new SpellImplicitTargetInfo(Targets.DestDb); + }); + }); + foreach (var spellInfo in mSpellInfoMap.Values) { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + // Fix range for trajectory triggered spell + foreach (var spellEffectInfo in spellInfo.GetEffects()) { - if (effect == null) - continue; - - if (effect.IsEffect() && (effect.TargetA.GetTarget() == Targets.DestTraj || effect.TargetB.GetTarget() == Targets.DestTraj)) + if (spellEffectInfo.IsEffect() && (spellEffectInfo.TargetA.GetTarget() == Targets.DestTraj || spellEffectInfo.TargetB.GetTarget() == Targets.DestTraj)) { // Get triggered spell if any - SpellInfo spellInfoTrigger = GetSpellInfo(effect.TriggerSpell, Difficulty.None); - if (spellInfoTrigger != null) + foreach (SpellInfo spellInfoTrigger in _GetSpellInfo(spellEffectInfo.TriggerSpell)) { float maxRangeMain = spellInfo.GetMaxRange(); float maxRangeTrigger = spellInfoTrigger.GetMaxRange(); @@ -2946,7 +4172,7 @@ namespace Game.Entities } } - switch (effect.Effect) + switch (spellEffectInfo.Effect) { case SpellEffectName.Charge: case SpellEffectName.ChargeDest: @@ -2958,15 +4184,15 @@ namespace Game.Entities break; } - if (effect.TargetA.GetSelectionCategory() == SpellTargetSelectionCategories.Cone || effect.TargetB.GetSelectionCategory() == SpellTargetSelectionCategories.Cone) + if (spellEffectInfo.TargetA.GetSelectionCategory() == SpellTargetSelectionCategories.Cone || spellEffectInfo.TargetB.GetSelectionCategory() == SpellTargetSelectionCategories.Cone) if (MathFunctions.fuzzyEq(spellInfo.ConeAngle, 0.0f)) spellInfo.ConeAngle = 90.0f; // Area auras may not target area (they're self cast) - if (effect.IsAreaAuraEffect() && effect.IsTargetingArea()) + if (spellEffectInfo.IsAreaAuraEffect() && spellEffectInfo.IsTargetingArea()) { - effect.TargetA = new(Targets.UnitCaster); - effect.TargetB = new(); + spellEffectInfo.TargetA = new(Targets.UnitCaster); + spellEffectInfo.TargetB = new(); } } @@ -2981,654 +4207,6 @@ namespace Game.Entities if (spellInfo.ActiveIconFileDataId == 135754) // flight spellInfo.Attributes |= SpellAttr0.Passive; - switch (spellInfo.Id) - { - case 6727: // Poison Mushroom - case 7331: // Healing Aura (TEST) (Rank 1) - /* - 30400, // Nether Beam - Perseverance - Blizzlike to have it disabled? DBC says: - "This is currently turned off to increase performance. Enable this to make it fire more frequently." - */ - case 34589: // Dangerous Water - case 52562: // Arthas Zombie Catcher - case 57550: // Tirion Aggro - case 65755: - spellInfo.GetEffect(0).ApplyAuraPeriod = 1 * Time.InMilliseconds; - break; - case 24707: // Food - case 26263: // Dim Sum - case 29055: // Refreshing Red Apple - case 37504: // Karazhan - Chess NPC AI, action timer - // first effect has correct amplitude - spellInfo.GetEffect(1).ApplyAuraPeriod = spellInfo.GetEffect(0).ApplyAuraPeriod; - break; - // Vomit - case 43327: - spellInfo.GetEffect(1).ApplyAuraPeriod = 1 * Time.InMilliseconds; - break; - // specific code for cases with no trigger spell provided in field - case 23170: // Brood Affliction: Bronze - spellInfo.GetEffect(0).TriggerSpell = 23171; - break; - case 29917: // Feed Captured Animal - spellInfo.GetEffect(0).TriggerSpell = 29916; - break; - case 37027: // Remote Toy - spellInfo.GetEffect(0).TriggerSpell = 37029; - break; - case 38495: // Eye of Grillok - spellInfo.GetEffect(0).TriggerSpell = 38530; - break; - case 39857: // Tear of Azzinoth Summon Channel - it's not really supposed to do anything, and this only prevents the console spam - spellInfo.GetEffect(0).TriggerSpell = 39856; - break; - case 46736:// Personalized Weather - spellInfo.GetEffect(0).TriggerSpell = 46737; - spellInfo.GetEffect(0).ApplyAuraName = AuraType.PeriodicTriggerSpell; - break; - case 379: // Earth Shield - case 71607: // Item - Bauble of True Blood 10m - case 71646: // Item - Bauble of True Blood 25m - case 71610: // Item - Althor's Abacus trigger 10m - case 71641: // Item - Althor's Abacus trigger 25m - // We need more spells to find a general way (if there is any) - spellInfo.DmgClass = SpellDmgClass.Magic; - break; - case 63026: // Summon Aspirant Test NPC (HACK: Target shouldn't be changed) - case 63137: // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination) - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); - break; - case 52611: // Summon Skeletons - case 52612: // Summon Skeletons - spellInfo.GetEffect(0).MiscValueB = 64; - break; - case 40244: // Simon Game Visual - case 40245: // Simon Game Visual - case 40246: // Simon Game Visual - case 40247: // Simon Game Visual - case 42835: // Spout, remove damage effect, only anim is needed - spellInfo.GetEffect(0).Effect = 0; - break; - case 63665: // Charge (Argent Tournament emote on riders) - case 31298: // Sleep (needs target selection script) - case 51904: // Summon Ghouls On Scarlet Crusade (this should use conditions table, script for this spell needs to be fixed) - case 68933: // Wrath of Air Totem rank 2 (Aura) - case 29200: // Purify Helboar Meat - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitCaster); - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(); - break; - case 56690: // Thrust Spear - case 60586: // Mighty Spear Thrust - case 60776: // Claw Swipe - case 60881: // Fatal Strike - case 60864: // Jaws of Death - spellInfo.AttributesEx4 |= SpellAttr4.FixedDamage; - break; - case 31344: // Howl of Azgalor - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards100); // 100yards instead of 50000?! - break; - case 42818: // Headless Horseman - Wisp Flight Port - case 42821: // Headless Horseman - Wisp Flight Missile - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(6); // 100 yards - break; - case 36350: //They Must Burn Bomb Aura (self) - spellInfo.GetEffect(0).TriggerSpell = 36325; // They Must Burn Bomb Drop (DND) - break; - case 5308: // Execute - spellInfo.AttributesEx3 |= SpellAttr3.CantTriggerProc; - break; - case 31347: // Doom - case 36327: // Shoot Arcane Explosion Arrow - case 39365: // Thundering Storm - case 41071: // Raise Dead (HACK) - case 42442: // Vengeance Landing Cannonfire - case 42611: // Shoot - case 44978: // Wild Magic - case 45001: // Wild Magic - case 45002: // Wild Magic - case 45004: // Wild Magic - case 45006: // Wild Magic - case 45010: // Wild Magic - case 45761: // Shoot Gun - case 45863: // Cosmetic - Incinerate to Random Target - case 48246: // Ball of Flame - case 41635: // Prayer of Mending - case 44869: // Spectral Blast - case 45027: // Revitalize - case 45976: // Muru Portal Channel - case 52124: // Sky Darkener Assault - case 52479: // Gift of the Harvester - case 61588: // Blazing Harpoon - case 55479: // Force Obedience - case 28560: // Summon Blizzard (Sapphiron) - case 53096: // Quetz'lun's Judgment - case 70743: // AoD Special - case 70614: // AoD Special - Vegard - case 4020: // Safirdrang's Chill - case 52438: // Summon Skittering Swarmer (Force Cast) - case 52449: // Summon Skittering Infector (Force Cast) - case 53609: // Summon Anub'ar Assassin (Force Cast) - case 53457: // Summon Impale Trigger (AoE) - case 45907: // Torch Target Picker - case 52953: // Torch - case 58121: // Torch - spellInfo.MaxAffectedTargets = 1; - break; - case 36384: // Skartax Purple Beam - spellInfo.MaxAffectedTargets = 2; - break; - case 28542: // Life Drain - Sapphiron - case 29213: // Curse of the Plaguebringer - Noth - case 29576: // Multi-Shot - case 37790: // Spread Shot - case 39992: // Needle Spine - case 40816: // Saber Lash - case 41303: // Soul Drain - case 41376: // Spite - case 45248: // Shadow Blades - case 46771: // Flame Sear - case 66588: // Flaming Spear - spellInfo.MaxAffectedTargets = 3; - break; - case 38310: // Multi-Shot - case 53385: // Divine Storm (Damage) - spellInfo.MaxAffectedTargets = 4; - break; - case 42005: // Bloodboil - case 38296: // Spitfire Totem - case 37676: // Insidious Whisper - case 46008: // Negative Energy - case 45641: // Fire Bloom - case 55665: // Life Drain - Sapphiron (H) - case 28796: // Poison Bolt Volly - Faerlina - spellInfo.MaxAffectedTargets = 5; - break; - case 54835: // Curse of the Plaguebringer - Noth (H) - spellInfo.MaxAffectedTargets = 8; - break; - case 40827: // Sinful Beam - case 40859: // Sinister Beam - case 40860: // Vile Beam - case 40861: // Wicked Beam - case 54098: // Poison Bolt Volly - Faerlina (H) - spellInfo.MaxAffectedTargets = 10; - break; - case 50312: // Unholy Frenzy - spellInfo.MaxAffectedTargets = 15; - break; - case 33711: // Murmur's Touch - case 38794: - spellInfo.MaxAffectedTargets = 1; - spellInfo.GetEffect(0).TriggerSpell = 33760; - break; - case 44544: // Fingers of Frost - spellInfo.GetEffect(0).SpellClassMask[0] |= 0x20000; - break; - case 52212: // Death and Decay - case 41485: // Deadly Poison - Black Temple - case 41487: // Envenom - Black Temple - spellInfo.AttributesEx6 |= SpellAttr6.CanTargetInvisible; - break; - case 37408: // Oscillation Field - spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; - break; - case 51912: // Crafty's Ultra-Advanced Proto-Typical Shortening Blaster - spellInfo.GetEffect(0).ApplyAuraPeriod = 3000; - break; - case 30421: // Nether Portal - Perseverence - spellInfo.GetEffect(2).BasePoints += 30000; - break; - case 41913: // Parasitic Shadowfiend Passive - spellInfo.GetEffect(0).ApplyAuraName = AuraType.Dummy; // proc debuff, and summon infinite fiends - break; - case 27892: // To Anchor 1 - case 27928: // To Anchor 1 - case 27935: // To Anchor 1 - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); - break; - // target allys instead of enemies, target A is src_caster, spells with effect like that have ally target - // this is the only known exception, probably just wrong data - case 29214: // Wrath of the Plaguebringer - case 54836: // Wrath of the Plaguebringer - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.UnitSrcAreaAlly); - spellInfo.GetEffect(1).TargetB = new SpellImplicitTargetInfo(Targets.UnitSrcAreaAlly); - break; - case 15290: // Vampiric Embrace - spellInfo.AttributesEx3 |= SpellAttr3.NoInitialAggro; - break; - case 6474: // Earthbind Totem (instant pulse) - spellInfo.AttributesEx5 |= SpellAttr5.StartPeriodicAtApply; - break; - case 70728: // Exploit Weakness (needs target selection script) - case 70840: // Devious Minds (needs target selection script) - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitCaster); - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.UnitPet); - break; - case 45602: // Ride Carpet - spellInfo.GetEffect(0).BasePoints = 0;// force seat 0, vehicle doesn't have the required seat flags for "no seat specified (-1)" - break; - case 61719: // Easter Lay Noblegarden Egg Aura - Interrupt flags copied from aura which this aura is linked with - spellInfo.AuraInterruptFlags = SpellAuraInterruptFlags.HostileActionReceived | SpellAuraInterruptFlags.Damage; - break; - case 71838: // Drain Life - Bryntroll Normal - case 71839: // Drain Life - Bryntroll Heroic - spellInfo.AttributesEx2 |= SpellAttr2.CantCrit; - break; - case 56606: // Ride Jokkum - case 61791: // Ride Vehicle (Yogg-Saron) - // @todo: remove this when basepoints of all Ride Vehicle auras are calculated correctly - spellInfo.GetEffect(0).BasePoints = 1; - break; - case 59630: // Black Magic - spellInfo.Attributes |= SpellAttr0.Passive; - break; - case 48278: // Paralyze - spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; - break; - case 51798: // Brewfest - Relay Race - Intro - Quest Complete - case 47134: // Quest Complete - //! HACK: This spell break quest complete for alliance and on retail not used °_O - spellInfo.GetEffect(0).Effect = 0; - break; - case 85123: // Siege Cannon (Tol Barad) - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitSrcAreaEntry); - break; - case 198300: // Gathering Storms - spellInfo.ProcCharges = 1; // override proc charges, has 0 (unlimited) in db2 - break; - case 42490: // Energized! - case 42492: // Cast Energized - case 43115: // Plague Vial - spellInfo.AttributesEx |= SpellAttr1.NoThreat; - break; - case 29726: // Test Ribbon Pole Channel - spellInfo.ChannelInterruptFlags &= ~SpellAuraInterruptFlags.Action;//AURA_INTERRUPT_FLAG_CAST - break; - case 42767: // Sic'em - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitNearbyEntry); - break; - case 42793: // Burn Body - spellInfo.GetEffect(2).MiscValue = 24008; // Fallen Combatant - break; - case 59544:// Gift of the Naaru (priest and monk variants) - case 121093: - spellInfo.SpellFamilyFlags[2] = 0x80000000; - break; - case 50661:// Weakened Resolve - case 68979:// Unleashed Souls - case 48714:// Compelled - case 7853: // The Art of Being a Water Terror: Force Cast on Player - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd - break; - case 28864: - case 29105: - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); - break; - // VIOLET HOLD SPELLS - // - case 54258: // Water Globule (Ichoron) - case 54264: // Water Globule (Ichoron) - case 54265: // Water Globule (Ichoron) - case 54266: // Water Globule (Ichoron) - case 54267: // Water Globule (Ichoron) - // in 3.3.5 there is only one radius in dbc which is 0 yards in this case - // use max radius from 4.3.4 - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards25); - break; - // ENDOF VIOLET HOLD - // - // ULDUAR SPELLS - // - case 62374: // Pursued (Flame Leviathan) - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards50000); // 50000yd - break; - case 63342: // Focused Eyebeam Summon Trigger (Kologarn) - spellInfo.MaxAffectedTargets = 1; - break; - case 65584: // Growth of Nature (Freya) - case 64381: // Strength of the Pack (Auriaya) - spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; - break; - case 63018: // Searing Light (XT-002) - case 65121: // Searing Light (25m) (XT-002) - case 63024: // Gravity Bomb (XT-002) - case 64234: // Gravity Bomb (25m) (XT-002) - spellInfo.MaxAffectedTargets = 1; - break; - case 64386: // Terrifying Screech (Auriaya) - case 64389: // Sentinel Blast (Auriaya) - case 64678: // Sentinel Blast (Auriaya) - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(28); // 5 seconds, wrong DBC data? - break; - case 64321: // Potent Pheromones (Freya) - // spell should dispel area aura, but doesn't have the attribute - // may be db data bug, or blizz may keep reapplying area auras every update with checking immunity - // that will be clear if we get more spells with problem like this - spellInfo.AttributesEx |= SpellAttr1.DispelAurasOnImmunity; - break; - case 63414: // Spinning Up (Mimiron) - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.UnitCaster); - spellInfo.ChannelInterruptFlags = SpellAuraInterruptFlags.None; - spellInfo.ChannelInterruptFlags2 = SpellAuraInterruptFlags2.None; - break; - case 63036: // Rocket Strike (Mimiron) - spellInfo.Speed = 0; - break; - case 64668: // Magnetic Field (Mimiron) - spellInfo.Mechanic = Mechanics.None; - break; - case 64468: // Empowering Shadows (Yogg-Saron) - case 64486: // Empowering Shadows (Yogg-Saron) - spellInfo.MaxAffectedTargets = 3; // same for both modes? - break; - case 62301: // Cosmic Smash (Algalon the Observer) - spellInfo.MaxAffectedTargets = 1; - break; - case 64598: // Cosmic Smash (Algalon the Observer) - spellInfo.MaxAffectedTargets = 3; - break; - case 62293: // Cosmic Smash (Algalon the Observer) - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.DestCaster); - break; - case 62311: // Cosmic Smash (Algalon the Observer) - case 64596: // Cosmic Smash (Algalon the Observer) - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(6); // 100yd - break; - case 64014: // Expedition Base Camp Teleport - case 64024: // Conservatory Teleport - case 64025: // Halls of Invention Teleport - case 64028: // Colossal Forge Teleport - case 64029: // Shattered Walkway Teleport - case 64030: // Antechamber Teleport - case 64031: // Scrapyard Teleport - case 64032: // Formation Grounds Teleport - case 65042: // Prison of Yogg-Saron Teleport - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); - break; - // ENDOF ULDUAR SPELLS - // - // TRIAL OF THE CRUSADER SPELLS - // - case 66258: // Infernal Eruption - // increase duration from 15 to 18 seconds because caster is already - // unsummoned when spell missile hits the ground so nothing happen in result - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(85); - break; - // ENDOF TRIAL OF THE CRUSADER SPELLS - // - // ICECROWN CITADEL SPELLS - // - // THESE SPELLS ARE WORKING CORRECTLY EVEN WITHOUT THIS HACK - // THE ONLY REASON ITS HERE IS THAT CURRENT GRID SYSTEM - // DOES NOT ALLOW FAR OBJECT SELECTION (dist > 333) - case 70781: // Light's Hammer Teleport - case 70856: // Oratory of the Damned Teleport - case 70857: // Rampart of Skulls Teleport - case 70858: // Deathbringer's Rise Teleport - case 70859: // Upper Spire Teleport - case 70860: // Frozen Throne Teleport - case 70861: // Sindragosa's Lair Teleport - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); - break; - // Coldflame (Lord Marrowgar) - case 69146: - spellInfo.AttributesEx4 &= ~SpellAttr4.IgnoreResistances; - break; - case 71169: // Shadow's Fate - spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; - break; - case 72347: // Lock Players and Tap Chest - spellInfo.AttributesEx3 &= ~SpellAttr3.NoInitialAggro; - break; - case 72723: // Resistant Skin (Deathbringer Saurfang adds) - // this spell initially granted Shadow damage immunity, however it was removed but the data was left in client - spellInfo.GetEffect(2).Effect = 0; - break; - case 70460: // Coldflame Jets (Traps after Saurfang) - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(1); // 10 seconds - break; - case 71412: // Green Ooze Summon (Professor Putricide) - case 71415: // Orange Ooze Summon (Professor Putricide) - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitTargetAny); - break; - case 71159: // Awaken Plagued Zombies - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(21); - break; - case 70530: // Volatile Ooze Beam Protection (Professor Putricide) - spellInfo.GetEffect(0).Effect = SpellEffectName.ApplyAura; // for an unknown reason this was SPELL_EFFECT_APPLY_AREA_AURA_RAID - break; - // THIS IS HERE BECAUSE COOLDOWN ON CREATURE PROCS IS NOT IMPLEMENTED - case 71604: // Mutated Strength (Professor Putricide) - spellInfo.GetEffect(1).Effect = 0; - break; - case 70911: // Unbound Plague (Professor Putricide) (needs target selection script) - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.UnitTargetEnemy); - break; - case 71708: // Empowered Flare (Blood Prince Council) - spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; - break; - case 71266: // Swarming Shadows - spellInfo.RequiredAreasID = 0; // originally, these require area 4522, which is... outside of Icecrown Citadel - break; - case 70602: // Corruption - spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters; - break; - case 70715: // Column of Frost (visual marker) - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(32); // 6 seconds (missing) - break; - case 71085: // Mana Void (periodic aura) - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(9); // 30 seconds (missing) - break; - case 70936: // Summon Suppressor (needs target selection script) - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitTargetAny); - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(); - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(157); // 90yd - break; - case 70598: // Sindragosa's Fury - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDest); - break; - case 69846: // Frost Bomb - spellInfo.Speed = 0.0f; // This spell's summon happens instantly - break; - case 70106: // Chilled to the Bone - spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; - spellInfo.AttributesEx6 |= SpellAttr6.IgnoreCasterDamageModifiers; - break; - case 71614: // Ice Lock - spellInfo.Mechanic = Mechanics.Stun; - break; - case 72762: // Defile - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(559); // 53 seconds - break; - case 72743: // Defile - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(22); // 45 seconds - break; - case 72754: // Defile - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); // 200yd - spellInfo.GetEffect(1).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards200); // 200yd - break; - case 69030: // Val'kyr Target Search - spellInfo.Attributes |= SpellAttr0.UnaffectedByInvulnerability; - break; - case 69198: // Raging Spirit Visual - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd - break; - case 73655: // Harvest Soul - spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; - break; - case 73540: // Summon Shadow Trap - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(23); // 90 seconds - break; - case 73530: // Shadow Trap (visual) - spellInfo.DurationEntry = CliDB.SpellDurationStorage.LookupByKey(28); // 5 seconds - break; - case 74302: // Summon Spirit Bomb - spellInfo.MaxAffectedTargets = 2; - break; - case 73579: // Summon Spirit Bomb - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards25); // 25yd - break; - case 72376: // Raise Dead - spellInfo.MaxAffectedTargets = 3; - break; - case 71809: // Jump - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(5); // 40yd - spellInfo.GetEffect(0).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards10); // 10yd - spellInfo.GetEffect(0).MiscValue = 190; - break; - case 72405: // Broken Frostmourne - spellInfo.GetEffect(1).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards20); // 20yd - spellInfo.AttributesEx |= SpellAttr1.NoThreat; - break; - // ENDOF ICECROWN CITADEL SPELLS - // - // RUBY SANCTUM SPELLS - // - case 74799: // Soul Consumption - spellInfo.GetEffect(1).RadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards12); - break; - case 75509: // Twilight Mending - spellInfo.AttributesEx6 |= SpellAttr6.CanTargetInvisible; - spellInfo.AttributesEx2 |= SpellAttr2.CanTargetNotInLos; - break; - case 75888: // Awaken Flames - spellInfo.AttributesEx |= SpellAttr1.CantTargetSelf; - break; - // ENDOF RUBY SANCTUM SPELLS - // - // EYE OF ETERNITY SPELLS - // All spells below work even without these changes. The LOS attribute is due to problem - // from collision between maps & gos with active destroyed state. - case 57473: // Arcane Storm bonus explicit visual spell - case 57431: // Summon Static Field - case 56091: // Flame Spike (Wyrmrest Skytalon) - case 56092: // Engulf in Flames (Wyrmrest Skytalon) - case 57090: // Revivify (Wyrmrest Skytalon) - case 57143: // Life Burst (Wyrmrest Skytalon) - spellInfo.AttributesEx2 |= SpellAttr2.CanTargetNotInLos; - break; - // This would never crit on retail and it has attribute for SPELL_ATTR3_NO_DONE_BONUS because is handled from player, - // until someone figures how to make scions not critting without hack and without making them main casters this should stay here. - case 63934: // Arcane Barrage (cast by players and NONMELEEDAMAGELOG with caster Scion of Eternity (original caster)). - spellInfo.AttributesEx2 |= SpellAttr2.CantCrit; - break; - // ENDOF EYE OF ETERNITY SPELLS - // - case 40055: // Introspection - case 40165: // Introspection - case 40166: // Introspection - case 40167: // Introspection - spellInfo.Attributes |= SpellAttr0.Negative1; - break; - // Stonecore spells - case 95284: // Teleport (from entrance to Slabhide) - case 95285: // Teleport (from Slabhide to entrance) - spellInfo.GetEffect(0).TargetB = new SpellImplicitTargetInfo(Targets.DestDb); - break; - // Halls Of Origination spells - // Temple Guardian Anhuur - case 76606: // Disable Beacon Beams L - case 76608: // Disable Beacon Beams R - // Little hack, Increase the radius so it can hit the Cave In Stalkers in the platform. - spellInfo.GetEffect(0).MaxRadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards45); - break; - case 24314: // Threatening Gaze - spellInfo.AuraInterruptFlags |= SpellAuraInterruptFlags.Action | SpellAuraInterruptFlags.Moving | SpellAuraInterruptFlags.Anim; - break; - case 783: // Travel Form (dummy) - cannot be cast indoors. - spellInfo.Attributes |= SpellAttr0.OutdoorsOnly; - break; - case 5420: // Tree of Life (Passive) - spellInfo.Stances = 1 << ((int)ShapeShiftForm.TreeOfLife - 1); - break; - case 49376: // Feral Charge (Cat Form) - spellInfo.AttributesEx3 &= ~SpellAttr3.CantTriggerProc; - break; - case 96942: // Gaze of Occu'thar - spellInfo.AttributesEx &= ~SpellAttr1.Channeled1; - break; - case 75610: // Evolution - spellInfo.MaxAffectedTargets = 1; - break; - case 75697: // Evolution - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.UnitSrcAreaEntry); - break; - // ISLE OF CONQUEST SPELLS - // - case 66551: // Teleport - spellInfo.RangeEntry = CliDB.SpellRangeStorage.LookupByKey(13); // 50000yd - break; - // ENDOF ISLE OF CONQUEST SPELLS - // - // Aura of Fear - case 40453: - // Bad DBC data? Copying 25820 here due to spell description - // either is a periodic with chance on tick, or a proc - spellInfo.GetEffect(0).ApplyAuraName = AuraType.ProcTriggerSpell; - spellInfo.GetEffect(0).ApplyAuraPeriod = 0; - spellInfo.ProcChance = 10; - break; - // FIRELANDS SPELLS - // Torment Searcher - case 99253: - spellInfo.GetEffect(0).MaxRadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards15); - break; - // Torment Damage - case 99256: - spellInfo.Attributes |= SpellAttr0.Negative1; - break; - // Blaze of Glory - case 99252: - spellInfo.AuraInterruptFlags |= SpellAuraInterruptFlags.LeaveWorld; - break; - // ENDOF FIRELANDS SPELLS - - // ANTORUS THE BURNING THRONE SPELLS - // Decimation - case 244449: - // For some reason there is a instakill effect that serves absolutely no purpose. - // Until we figure out what it's actually used for we disable it. - spellInfo.GetEffect(2).Effect = 0; - break; - // ENDOF ANTORUS THE BURNING THRONE SPELLS - case 102445: // Summon Master Li Fei - spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); - break; - } - } - - foreach (var spellInfo in mSpellInfoMap.Values) - { - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) - { - if (effect == null) - continue; - switch (effect.Effect) - { - case SpellEffectName.Charge: - case SpellEffectName.ChargeDest: - case SpellEffectName.Jump: - case SpellEffectName.JumpDest: - case SpellEffectName.LeapBack: - if (spellInfo.Speed == 0 && spellInfo.SpellFamilyName == 0) - spellInfo.Speed = MotionMaster.SPEED_CHARGE; - break; - } - - if (effect.TargetA.GetSelectionCategory() == SpellTargetSelectionCategories.Cone || effect.TargetB.GetSelectionCategory() == SpellTargetSelectionCategories.Cone) - if (MathFunctions.fuzzyEq(spellInfo.ConeAngle, 0.0f)) - spellInfo.ConeAngle = 90.0f; - } - - // disable proc for magnet auras, they're handled differently - if (spellInfo.HasAura(AuraType.SpellMagnet)) - spellInfo.ProcFlags = 0; - - if (spellInfo.ActiveIconFileDataId == 135754) // flight - spellInfo.Attributes |= SpellAttr0.Passive; - if (spellInfo.IsSingleTarget()) spellInfo.MaxAffectedTargets = 1; } @@ -3884,7 +4462,7 @@ namespace Game.Entities if (!SpellEffectsHandlers.ContainsKey(eff)) { Log.outError(LogFilter.Spells, "No defined handler for SpellEffect {0}", eff); - return SpellEffectsHandlers[SpellEffectName.Null]; + return SpellEffectsHandlers[SpellEffectName.None]; } return SpellEffectsHandlers[eff]; @@ -3976,7 +4554,7 @@ namespace Game.Entities { return mBattlePets.LookupByKey(spellId); } - + #region Fields Dictionary mSpellChains = new(); MultiMap mSpellsReqSpell = new(); @@ -4139,9 +4717,9 @@ namespace Game.Entities if (player == null || (((1 << (int)player.GetQuestStatus(questStart)) & questStartStatus) == 0)) return false; - if (questEnd != 0) // not in expected forbidden quest state - if (player == null || (((1 << (int)player.GetQuestStatus(questEnd)) & questEndStatus) == 0)) - return false; + if (questEnd != 0) // not in expected forbidden quest state + if (player == null || (((1 << (int)player.GetQuestStatus(questEnd)) & questEndStatus) == 0)) + return false; if (auraSpell != 0) // not have expected aura if (player == null || (auraSpell > 0 && !player.HasAura((uint)auraSpell)) || (auraSpell < 0 && player.HasAura((uint)-auraSpell))) @@ -4158,56 +4736,56 @@ namespace Game.Entities switch (spellId) { case 91604: // No fly Zone - Wintergrasp - { - if (!player) - return false; + { + if (!player) + return false; - BattleField Bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); - if (Bf == null || Bf.CanFlyIn() || (!player.HasAuraType(AuraType.ModIncreaseMountedFlightSpeed) && !player.HasAuraType(AuraType.Fly))) - return false; - break; - } + BattleField Bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); + if (Bf == null || Bf.CanFlyIn() || (!player.HasAuraType(AuraType.ModIncreaseMountedFlightSpeed) && !player.HasAuraType(AuraType.Fly))) + return false; + break; + } case 56618: // Horde Controls Factory Phase Shift case 56617: // Alliance Controls Factory Phase Shift - { - if (!player) - return false; + { + if (!player) + return false; - BattleField bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); + BattleField bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); - if (bf == null || bf.GetTypeId() != (int)BattleFieldTypes.WinterGrasp) - return false; + if (bf == null || bf.GetTypeId() != (int)BattleFieldTypes.WinterGrasp) + return false; - // team that controls the workshop in the specified area - uint team = bf.GetData(newArea); + // team that controls the workshop in the specified area + uint team = bf.GetData(newArea); - if (team == TeamId.Horde) - return spellId == 56618; - else if (team == TeamId.Alliance) - return spellId == 56617; - break; - } + if (team == TeamId.Horde) + return spellId == 56618; + else if (team == TeamId.Alliance) + return spellId == 56617; + break; + } case 57940: // Essence of Wintergrasp - Northrend case 58045: // Essence of Wintergrasp - Wintergrasp - { - if (!player) - return false; - - BattleField battlefieldWG = Global.BattleFieldMgr.GetBattlefieldByBattleId(1); - if (battlefieldWG != null) - return battlefieldWG.IsEnabled() && (player.GetTeamId() == battlefieldWG.GetDefenderTeam()) && !battlefieldWG.IsWarTime(); - break; - } + { + if (!player) + return false; + + BattleField battlefieldWG = Global.BattleFieldMgr.GetBattlefieldByBattleId(1); + if (battlefieldWG != null) + return battlefieldWG.IsEnabled() && (player.GetTeamId() == battlefieldWG.GetDefenderTeam()) && !battlefieldWG.IsWarTime(); + break; + } case 74411: // Battleground- Dampening - { - if (!player) - return false; - - BattleField bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); - if (bf != null) - return bf.IsWarTime(); - break; - } + { + if (!player) + return false; + + BattleField bf = Global.BattleFieldMgr.GetBattlefieldToZoneId(player.GetZoneId()); + if (bf != null) + return bf.IsWarTime(); + break; + } } return true; } diff --git a/Source/Scripts/Spells/DeathKnight.cs b/Source/Scripts/Spells/DeathKnight.cs index 3e07d3107..47d2175a0 100644 --- a/Source/Scripts/Spells/DeathKnight.cs +++ b/Source/Scripts/Spells/DeathKnight.cs @@ -127,12 +127,12 @@ namespace Scripts.Spells.DeathKnight public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.RunicPowerEnergize, SpellIds.VolatileShielding); + return ValidateSpellInfo(SpellIds.RunicPowerEnergize, SpellIds.VolatileShielding) && spellInfo.GetEffects().Count > 1; } public override bool Load() { - absorbPct = GetSpellInfo().GetEffect(1).CalcValue(GetCaster()); + absorbPct = GetEffectInfo(1).CalcValue(GetCaster()); maxHealth = GetCaster().GetMaxHealth(); absorbedAmount = 0; return true; @@ -427,7 +427,7 @@ namespace Scripts.Spells.DeathKnight public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.DeathStrikeEnabler, SpellIds.DeathStrikeHeal, SpellIds.BloodShieldMastery, SpellIds.BloodShieldAbsorb, SpellIds.RecentlyUsedDeathStrike, SpellIds.Frost, SpellIds.DeathStrikeOffhand) - && spellInfo.GetEffect(1) != null && spellInfo.GetEffect(2) != null; + && spellInfo.GetEffects().Count > 2; } void HandleDummy(uint effIndex) @@ -437,12 +437,10 @@ namespace Scripts.Spells.DeathKnight AuraEffect enabler = caster.GetAuraEffect(SpellIds.DeathStrikeEnabler, 0, GetCaster().GetGUID()); if (enabler != null) { - SpellInfo spellInfo = GetSpellInfo(); - // Heals you for 25% of all damage taken in the last 5 sec, - int heal = MathFunctions.CalculatePct(enabler.CalculateAmount(GetCaster()), spellInfo.GetEffect(1).CalcValue(GetCaster())); + int heal = MathFunctions.CalculatePct(enabler.CalculateAmount(GetCaster()), GetEffectInfo(1).CalcValue(GetCaster())); // minimum 7.0% of maximum health. - int pctOfMaxHealth = MathFunctions.CalculatePct(spellInfo.GetEffect(2).CalcValue(GetCaster()), caster.GetMaxHealth()); + int pctOfMaxHealth = MathFunctions.CalculatePct(GetEffectInfo(2).CalcValue(GetCaster()), caster.GetMaxHealth()); heal = Math.Max(heal, pctOfMaxHealth); caster.CastSpell(caster, SpellIds.DeathStrikeHeal, new CastSpellExtraArgs(TriggerCastFlags.FullMask).AddSpellMod(SpellValueMod.BasePoint0, heal)); @@ -530,7 +528,7 @@ namespace Scripts.Spells.DeathKnight { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.CorpseExplosionTriggered); + return ValidateSpellInfo(SpellIds.CorpseExplosionTriggered) && spellInfo.GetEffects().Count > 2; } void HandleDamage(uint effIndex) diff --git a/Source/Scripts/Spells/Druid.cs b/Source/Scripts/Spells/Druid.cs index 19ce5f39c..1a694c555 100644 --- a/Source/Scripts/Spells/Druid.cs +++ b/Source/Scripts/Spells/Druid.cs @@ -410,7 +410,7 @@ namespace Scripts.Spells.Druid public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.IncarnationKingOfTheJungle) - && Global.SpellMgr.GetSpellInfo(SpellIds.IncarnationKingOfTheJungle, Difficulty.None).GetEffect(1) != null; + && Global.SpellMgr.GetSpellInfo(SpellIds.IncarnationKingOfTheJungle, Difficulty.None).GetEffects().Count > 1; } void HandleHitTargetBurn(uint effIndex) @@ -1570,8 +1570,7 @@ namespace Scripts.Spells.Druid public override bool Validate(SpellInfo spellInfo) { - SpellEffectInfo effectInfo = spellInfo.GetEffect(2); - if (effectInfo == null || effectInfo.IsEffect() || effectInfo.CalcValue() <= 0) + if (spellInfo.GetEffects().Count <= 2 || spellInfo.GetEffect(2).IsEffect() || spellInfo.GetEffect(2).CalcValue() <= 0) return false; return true; } @@ -1587,7 +1586,7 @@ namespace Scripts.Spells.Druid return true; }); - int maxTargets = GetSpellInfo().GetEffect(2).CalcValue(GetCaster()); + int maxTargets = GetEffectInfo(2).CalcValue(GetCaster()); if (targets.Count > maxTargets) { @@ -1626,7 +1625,7 @@ namespace Scripts.Spells.Druid return; // calculate from base damage, not from aurEff.GetAmount() (already modified) - float damage = caster.CalculateSpellDamage(GetUnitOwner(), GetSpellInfo(), aurEff.GetEffIndex()); + float damage = caster.CalculateSpellDamage(GetUnitOwner(), aurEff.GetSpellEffectInfo()); // Wild Growth = first tick gains a 6% bonus, reduced by 2% each tick float reduction = 2.0f; diff --git a/Source/Scripts/Spells/Generic.cs b/Source/Scripts/Spells/Generic.cs index 92795fd2b..41dde9c67 100644 --- a/Source/Scripts/Spells/Generic.cs +++ b/Source/Scripts/Spells/Generic.cs @@ -529,7 +529,7 @@ namespace Scripts.Spells.Generic public override bool Validate(SpellInfo spellInfo) { - if (!spellInfo.GetEffect(0).IsAura() || spellInfo.GetEffect(0).ApplyAuraName != AuraType.ModPowerRegen) + if (spellInfo.GetEffects().Empty() || !spellInfo.GetEffect(0).IsAura(AuraType.ModPowerRegen)) { Log.outError(LogFilter.Spells, "Aura {GetId()} structure has been changed - first aura is no longer SPELL_AURA_MOD_POWER_REGEN"); return false; @@ -625,7 +625,7 @@ namespace Scripts.Spells.Generic { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(spellInfo.GetEffect(0).TriggerSpell); + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo(spellInfo.GetEffect(0).TriggerSpell); } void PeriodicTick(AuraEffect aurEff) @@ -634,7 +634,7 @@ namespace Scripts.Spells.Generic if (!RandomHelper.randChance(GetSpellInfo().ProcChance)) return; - GetTarget().CastSpell((Unit)null, GetSpellInfo().GetEffect(aurEff.GetEffIndex()).TriggerSpell, true); + GetTarget().CastSpell(null, aurEff.GetSpellEffectInfo().TriggerSpell, true); } public override void Register() @@ -896,19 +896,19 @@ namespace Scripts.Spells.Generic { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo((uint)spellInfo.GetEffect(2).CalcValue()); + return spellInfo.GetEffects().Count > 2 && ValidateSpellInfo((uint)spellInfo.GetEffect(2).CalcValue()); } void HandleApply(AuraEffect aurEff, AuraEffectHandleModes mode) { Unit caster = GetCaster(); if (caster) - caster.CastSpell(GetTarget(), (uint)GetSpellInfo().GetEffect(2).CalcValue()); + caster.CastSpell(GetTarget(), (uint)GetEffectInfo(2).CalcValue()); } void HandleRemove(AuraEffect aurEff, AuraEffectHandleModes mode) { - GetTarget().RemoveAurasDueToSpell((uint)GetSpellInfo().GetEffect(2).CalcValue(), GetCasterGUID()); + GetTarget().RemoveAurasDueToSpell((uint)GetEffectInfo(2).CalcValue(), GetCasterGUID()); } public override void Register() @@ -1565,19 +1565,20 @@ namespace Scripts.Spells.Generic [Script] class spell_gen_gift_of_naaru : AuraScript { + public override bool Validate(SpellInfo spellInfo) + { + return spellInfo.GetEffects().Count > 1; + } + void CalculateAmount(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated) { if (!GetCaster() || aurEff.GetTotalTicks() == 0) return; - SpellEffectInfo eff1 = GetSpellInfo().GetEffect(1); - if (eff1 != null) - { - float healPct = eff1.CalcValue() / 100.0f; - float heal = healPct * GetCaster().GetMaxHealth(); - int healTick = (int)Math.Floor(heal / aurEff.GetTotalTicks()); - amount += healTick; - } + float healPct = GetEffectInfo(1).CalcValue() / 100.0f; + float heal = healPct * GetCaster().GetMaxHealth(); + int healTick = (int)Math.Floor(heal / aurEff.GetTotalTicks()); + amount += healTick; } public override void Register() @@ -1785,7 +1786,7 @@ namespace Scripts.Spells.Generic if (spell.HasEffect(SpellEffectName.ScriptEffect)) OnEffectHitTarget.Add(new EffectHandler(HandleScriptEffect, SpellConst.EffectFirstFound, SpellEffectName.ScriptEffect)); - if (spell.GetEffect(0).Effect == SpellEffectName.Charge) + if (spell.GetEffect(0).IsEffect(SpellEffectName.Charge)) OnEffectHitTarget.Add(new EffectHandler(HandleChargeEffect, 0, SpellEffectName.Charge)); } } @@ -1816,7 +1817,7 @@ namespace Scripts.Spells.Generic { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(spellInfo.GetEffect(0).TriggerSpell); + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo(spellInfo.GetEffect(0).TriggerSpell); } void PeriodicTick(AuraEffect aurEff) @@ -1825,7 +1826,7 @@ namespace Scripts.Spells.Generic CastSpellExtraArgs args = new(aurEff); args.AddSpellMod(SpellValueMod.MaxTargets, (int)aurEff.GetTickNumber() / 10 + 1); - GetTarget().CastSpell((Unit)null, GetSpellInfo().GetEffect(aurEff.GetEffIndex()).TriggerSpell, args); + GetTarget().CastSpell((Unit)null, aurEff.GetSpellEffectInfo().TriggerSpell, args); } public override void Register() @@ -1980,6 +1981,11 @@ namespace Scripts.Spells.Generic [Script] class spell_gen_oracle_wolvar_reputation : SpellScript { + public override bool Validate(SpellInfo spellInfo) + { + return spellInfo.GetEffects().Count > 1; + } + public override bool Load() { return GetCaster().IsTypeId(TypeId.Player); @@ -1988,7 +1994,7 @@ namespace Scripts.Spells.Generic void HandleDummy(uint effIndex) { Player player = GetCaster().ToPlayer(); - uint factionId = (uint)GetEffectInfo(effIndex).CalcValue(); + uint factionId = (uint)GetEffectInfo().CalcValue(); int repChange = GetEffectInfo(1).CalcValue(); FactionRecord factionEntry = CliDB.FactionStorage.LookupByKey(factionId); @@ -2291,11 +2297,16 @@ namespace Scripts.Spells.Generic [Script] // 62418 Impale class spell_gen_remove_on_health_pct : AuraScript { + public override bool Validate(SpellInfo spellInfo) + { + return spellInfo.GetEffects().Count > 1; + } + void PeriodicTick(AuraEffect aurEff) { // they apply damage so no need to check for ticks here - if (GetTarget().HealthAbovePct(GetSpellInfo().GetEffect(1).CalcValue())) + if (GetTarget().HealthAbovePct(GetEffectInfo(1).CalcValue())) { Remove(AuraRemoveMode.EnemySpell); PreventDefaultAction(); @@ -2318,7 +2329,7 @@ namespace Scripts.Spells.Generic void PeriodicTick(AuraEffect aurEff) { // if it has only periodic effect, allow 1 tick - bool onlyEffect = GetSpellInfo().GetEffects().Length == 1; + bool onlyEffect = GetSpellInfo().GetEffects().Count == 1; if (onlyEffect && aurEff.GetTickNumber() <= 1) return; @@ -3042,9 +3053,12 @@ namespace Scripts.Spells.Generic { public override bool Validate(SpellInfo spellInfo) { - SpellEffectInfo effect = spellInfo.GetEffect(0); - if (effect == null || effect.CalcValue() < 1) + if (spellInfo.GetEffects().Empty()) return false; + + if (spellInfo.GetEffect(0).CalcValue() < 1) + return false; + return true; } @@ -3236,7 +3250,7 @@ namespace Scripts.Spells.Generic public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo((uint)RequiredMixologySpells.Mixology); + return ValidateSpellInfo((uint)RequiredMixologySpells.Mixology) && !spellInfo.GetEffects().Empty(); } public override bool Load() @@ -3252,7 +3266,7 @@ namespace Scripts.Spells.Generic void CalculateAmount(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated) { - if (GetCaster().HasAura((uint)RequiredMixologySpells.Mixology) && GetCaster().HasSpell(GetSpellInfo().GetEffect(0).TriggerSpell)) + if (GetCaster().HasAura((uint)RequiredMixologySpells.Mixology) && GetCaster().HasSpell(GetEffectInfo(0).TriggerSpell)) { switch ((RequiredMixologySpells)GetId()) { @@ -3596,14 +3610,14 @@ namespace Scripts.Spells.Generic [Script] // 99947 - Face Rage class spell_gen_face_rage : AuraScript { - public override bool Validate(SpellInfo spell) + public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.FaceRage); + return ValidateSpellInfo(SpellIds.FaceRage) && spellInfo.GetEffects().Count > 2; } void OnRemove(AuraEffect effect, AuraEffectHandleModes mode) { - GetTarget().RemoveAurasDueToSpell(GetSpellInfo().GetEffect(2).TriggerSpell); + GetTarget().RemoveAurasDueToSpell(GetEffectInfo(2).TriggerSpell); } public override void Register() diff --git a/Source/Scripts/Spells/Hunter.cs b/Source/Scripts/Spells/Hunter.cs index 961709d07..fb9339d57 100644 --- a/Source/Scripts/Spells/Hunter.cs +++ b/Source/Scripts/Spells/Hunter.cs @@ -134,7 +134,7 @@ namespace Scripts.Spells.Hunter { public override bool Validate(SpellInfo spellInfo) { - return spellInfo.GetEffect(0) != null && ValidateSpellInfo(SpellIds.MastersCallTriggered, (uint)spellInfo.GetEffect(0).CalcValue()); + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo(SpellIds.MastersCallTriggered, (uint)spellInfo.GetEffect(0).CalcValue()); } public override bool Load() diff --git a/Source/Scripts/Spells/Items.cs b/Source/Scripts/Spells/Items.cs index 514b501dc..1445e143b 100644 --- a/Source/Scripts/Spells/Items.cs +++ b/Source/Scripts/Spells/Items.cs @@ -380,11 +380,6 @@ namespace Scripts.Spells.Items public const uint JomGabbar = 29602; public const uint BattleTrance = 45040; public const uint WorldQuellerFocus = 90900; - public const uint AzureWaterStrider = 118089; - public const uint CrimsonWaterStrider = 127271; - public const uint OrangeWaterStrider = 127272; - public const uint JadeWaterStrider = 127274; - public const uint GoldenWaterStrider = 127278; public const uint BrutalKinship1 = 144671; public const uint BrutalKinship2 = 145738; } @@ -1302,7 +1297,7 @@ namespace Scripts.Spells.Items { public override bool Validate(SpellInfo spellInfo) { - return spellInfo.GetEffect(0) != null; + return !spellInfo.GetEffects().Empty(); } bool CheckProc(ProcEventInfo eventInfo) @@ -3346,7 +3341,7 @@ namespace Scripts.Spells.Items { public override bool Validate(SpellInfo spellInfo) { - return spellInfo.GetEffect(1) != null; + return spellInfo.GetEffects().Count > 1; } public override bool Load() @@ -3358,7 +3353,7 @@ namespace Scripts.Spells.Items { Item artifact = GetOwner().ToPlayer().GetItemByGuid(GetAura().GetCastItemGUID()); if (artifact) - amount = (int)(GetSpellInfo().GetEffect(1).BasePoints * artifact.GetTotalPurchasedArtifactPowers() / 100); + amount = (int)(GetEffectInfo(1).BasePoints * artifact.GetTotalPurchasedArtifactPowers() / 100); } public override void Register() @@ -3372,7 +3367,7 @@ namespace Scripts.Spells.Items { public override bool Validate(SpellInfo spellInfo) { - return spellInfo.GetEffect(1) != null; + return spellInfo.GetEffects().Count > 1; } public override bool Load() @@ -3476,9 +3471,9 @@ namespace Scripts.Spells.Items [Script] // 127278 - Golden Water Strider class spell_item_water_strider : AuraScript { - public override bool Validate(SpellInfo spell) + public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.AzureWaterStrider, SpellIds.CrimsonWaterStrider, SpellIds.OrangeWaterStrider, SpellIds.JadeWaterStrider, SpellIds.GoldenWaterStrider); + return spellInfo.GetEffects().Count > 1; } void OnRemove(AuraEffect effect, AuraEffectHandleModes mode) diff --git a/Source/Scripts/Spells/Mage.cs b/Source/Scripts/Spells/Mage.cs index a9836d317..d1fc33ea5 100644 --- a/Source/Scripts/Spells/Mage.cs +++ b/Source/Scripts/Spells/Mage.cs @@ -151,7 +151,7 @@ namespace Scripts.Spells.Mage public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.ArcaneBarrageR3, SpellIds.ArcaneBarrageEnergize) && spellInfo.GetEffect(1) != null; + return ValidateSpellInfo(SpellIds.ArcaneBarrageR3, SpellIds.ArcaneBarrageEnergize) && spellInfo.GetEffects().Count > 1; } void ConsumeArcaneCharges() @@ -214,8 +214,10 @@ namespace Scripts.Spells.Mage if (!ValidateSpellInfo(SpellIds.ArcaneMage, SpellIds.Reverberate)) return false; - SpellEffectInfo damageEffect = spellInfo.GetEffect(1); - return damageEffect != null && damageEffect.IsEffect(SpellEffectName.SchoolDamage); + if (spellInfo.GetEffects().Count <= 1) + return false; + + return spellInfo.GetEffect(1).IsEffect(SpellEffectName.SchoolDamage); } void CheckRequiredAuraForBaselineEnergize(uint effIndex) @@ -319,7 +321,7 @@ namespace Scripts.Spells.Mage { public override bool Validate(SpellInfo spellInfo) { - return spellInfo.GetEffect(2) != null && ValidateSpellInfo(SpellIds.CauterizeDot, SpellIds.Cauterized, spellInfo.GetEffect(2).TriggerSpell); + return spellInfo.GetEffects().Count > 2 && ValidateSpellInfo(SpellIds.CauterizeDot, SpellIds.Cauterized, spellInfo.GetEffect(2).TriggerSpell); } void HandleAbsorb(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount) @@ -335,7 +337,7 @@ namespace Scripts.Spells.Mage } GetTarget().SetHealth(GetTarget().CountPctFromMaxHealth(effectInfo.GetAmount())); - GetTarget().CastSpell(GetTarget(), GetSpellInfo().GetEffect(2).TriggerSpell, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); + GetTarget().CastSpell(GetTarget(), GetEffectInfo(2).TriggerSpell, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); GetTarget().CastSpell(GetTarget(), SpellIds.CauterizeDot, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); GetTarget().CastSpell(GetTarget(), SpellIds.Cauterized, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); } @@ -517,15 +519,11 @@ namespace Scripts.Spells.Mage { // Thermal Void Aura thermalVoid = caster.GetAura(SpellIds.ThermalVoid); - if (thermalVoid != null) + if (!thermalVoid.GetSpellInfo().GetEffects().Empty()) { - SpellEffectInfo thermalVoidEffect = thermalVoid.GetSpellInfo().GetEffect(0); - if (thermalVoidEffect != null) - { - Aura icyVeins = caster.GetAura(SpellIds.IcyVeins); - if (icyVeins != null) - icyVeins.SetDuration(icyVeins.GetDuration() + thermalVoidEffect.CalcValue(caster) * Time.InMilliseconds); - } + Aura icyVeins = caster.GetAura(SpellIds.IcyVeins); + if (icyVeins != null) + icyVeins.SetDuration(icyVeins.GetDuration() + thermalVoid.GetSpellInfo().GetEffect(0).CalcValue(caster) * Time.InMilliseconds); } // Chain Reaction @@ -750,7 +748,7 @@ namespace Scripts.Spells.Mage { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.RingOfFrostSummon, SpellIds.RingOfFrostFreeze); + return ValidateSpellInfo(SpellIds.RingOfFrostSummon, SpellIds.RingOfFrostFreeze) && !Global.SpellMgr.GetSpellInfo(SpellIds.RingOfFrostSummon, GetCastDifficulty()).GetEffects().Empty(); } void HandleEffectPeriodic(AuraEffect aurEff) @@ -806,7 +804,7 @@ namespace Scripts.Spells.Mage { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.RingOfFrostSummon, SpellIds.RingOfFrostFreeze); + return ValidateSpellInfo(SpellIds.RingOfFrostSummon, SpellIds.RingOfFrostFreeze) && !Global.SpellMgr.GetSpellInfo(SpellIds.RingOfFrostSummon, GetCastDifficulty()).GetEffects().Empty(); } void FilterTargets(List targets) diff --git a/Source/Scripts/Spells/Monk.cs b/Source/Scripts/Spells/Monk.cs index dc68a517e..67521e5b7 100644 --- a/Source/Scripts/Spells/Monk.cs +++ b/Source/Scripts/Spells/Monk.cs @@ -20,6 +20,7 @@ using Game.Entities; using Game.Scripting; using Game.Spells; using System; +using System.Collections.Generic; namespace Scripts.Spells.Monk { @@ -304,14 +305,15 @@ namespace Scripts.Spells.Monk { float _period; + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.StaggerDamageAura) + && !Global.SpellMgr.GetSpellInfo(SpellIds.StaggerDamageAura, Difficulty.None).GetEffects().Empty(); + } + public override bool Load() { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SpellIds.StaggerDamageAura, GetCastDifficulty()); - SpellEffectInfo effInfo = spellInfo?.GetEffect(0); - if (effInfo == null) - return false; - - _period = (float)effInfo.ApplyAuraPeriod; + _period = (float)Global.SpellMgr.GetSpellInfo(SpellIds.StaggerDamageAura, GetCastDifficulty()).GetEffect(0).ApplyAuraPeriod; return true; } diff --git a/Source/Scripts/Spells/Priest.cs b/Source/Scripts/Spells/Priest.cs index 66b5d0e32..729c6f8d5 100644 --- a/Source/Scripts/Spells/Priest.cs +++ b/Source/Scripts/Spells/Priest.cs @@ -115,7 +115,7 @@ namespace Scripts.Spells.Priest public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.AtonementHeal); + return ValidateSpellInfo(SpellIds.AtonementHeal) && spellInfo.GetEffects().Count > 1; } bool CheckProc(ProcEventInfo eventInfo) @@ -133,7 +133,7 @@ namespace Scripts.Spells.Priest Unit target = Global.ObjAccessor.GetUnit(GetTarget(), targetGuid); if (target) { - if (target.GetExactDist(GetTarget()) < GetSpellInfo().GetEffect(1).CalcValue()) + if (target.GetExactDist(GetTarget()) < GetEffectInfo(1).CalcValue()) GetTarget().CastSpell(target, SpellIds.AtonementHeal, args); return false; @@ -242,12 +242,12 @@ namespace Scripts.Spells.Priest public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.GuardianSpiritHeal); + return ValidateSpellInfo(SpellIds.GuardianSpiritHeal) && spellInfo.GetEffects().Count > 1; } public override bool Load() { - healPct = (uint)GetSpellInfo().GetEffect(1).CalcValue(); + healPct = (uint)GetEffectInfo(1).CalcValue(); return true; } @@ -285,10 +285,9 @@ namespace Scripts.Spells.Priest public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.Heal, SpellIds.FlashHeal, SpellIds.PrayerOfHealing, SpellIds.Renew, SpellIds.Smite, SpellIds.HolyWordChastise, SpellIds.HolyWordSanctify, SpellIds.HolyWordSerenity) - && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordSerenity, Difficulty.None).GetEffect(1) != null - && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordSanctify, Difficulty.None).GetEffect(2) != null - && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordSanctify, Difficulty.None).GetEffect(3) != null - && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordChastise, Difficulty.None).GetEffect(1) != null; + && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordSerenity, Difficulty.None).GetEffects().Count > 1 + && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordSanctify, Difficulty.None).GetEffects().Count > 3 + && Global.SpellMgr.GetSpellInfo(SpellIds.HolyWordChastise, Difficulty.None).GetEffects().Count > 1; } void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo) @@ -480,16 +479,12 @@ namespace Scripts.Spells.Priest { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.Atonement, SpellIds.AtonementTriggered, SpellIds.Trinity); + return ValidateSpellInfo(SpellIds.Atonement, SpellIds.AtonementTriggered, SpellIds.Trinity) && spellInfo.GetEffects().Count > 3; } void OnTargetSelect(List targets) { - SpellEffectInfo eff2 = GetEffectInfo(2); - if (eff2 == null) - return; - - uint maxTargets = (uint)(eff2.CalcValue(GetCaster()) + 1); // adding 1 for explicit target unit + uint maxTargets = (uint)(GetEffectInfo(2).CalcValue(GetCaster()) + 1); // adding 1 for explicit target unit if (targets.Count > maxTargets) { Unit explTarget = GetExplTargetUnit(); @@ -516,11 +511,7 @@ namespace Scripts.Spells.Priest if (caster.HasAura(SpellIds.Trinity)) return; - SpellEffectInfo effect3 = GetEffectInfo(3); - if (effect3 == null) - return; - - int durationPct = effect3.CalcValue(caster); + int durationPct = GetEffectInfo(3).CalcValue(caster); if (caster.HasAura(SpellIds.Atonement)) caster.CastSpell(GetHitUnit(), SpellIds.AtonementTriggered, new CastSpellExtraArgs(SpellValueMod.DurationPct, durationPct).SetTriggerFlags(TriggerCastFlags.FullMask)); } @@ -622,7 +613,7 @@ namespace Scripts.Spells.Priest public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.PrayerOfMendingHeal, SpellIds.PrayerOfMendingAura) - && Global.SpellMgr.GetSpellInfo(SpellIds.PrayerOfMendingHeal, Difficulty.None).GetEffect(0) != null; + && !Global.SpellMgr.GetSpellInfo(SpellIds.PrayerOfMendingHeal, Difficulty.None).GetEffects().Empty(); } public override bool Load() diff --git a/Source/Scripts/Spells/Quest.cs b/Source/Scripts/Spells/Quest.cs index 5055a4417..f190ab0b3 100644 --- a/Source/Scripts/Spells/Quest.cs +++ b/Source/Scripts/Spells/Quest.cs @@ -673,14 +673,19 @@ namespace Scripts.Spells.Quest [Script] class spell_q12683_take_sputum_sample : SpellScript { + public override bool Validate(SpellInfo spellInfo) + { + return spellInfo.GetEffects().Count > 1; + } + void HandleDummy(uint effIndex) { - uint reqAuraId = (uint)GetSpellInfo().GetEffect(1).CalcValue(); + uint reqAuraId = (uint)GetEffectInfo(1).CalcValue(); Unit caster = GetCaster(); if (caster.HasAuraEffect(reqAuraId, 0)) { - uint spellId = (uint)GetSpellInfo().GetEffect(0).CalcValue(); + uint spellId = (uint)GetEffectInfo(0).CalcValue(); caster.CastSpell(caster, spellId, true); } } @@ -1451,7 +1456,7 @@ namespace Scripts.Spells.Quest { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo((uint)spellInfo.GetEffect(0).CalcValue()); + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo((uint)spellInfo.GetEffect(0).CalcValue()); } void HandleEffectDummy(uint effIndex) @@ -1612,7 +1617,7 @@ namespace Scripts.Spells.Quest { void ModDest(ref SpellDestination dest) { - float dist = GetSpellInfo().GetEffect(0).CalcRadius(GetCaster()); + float dist = GetEffectInfo(0).CalcRadius(GetCaster()); float angle = RandomHelper.FRand(0.75f, 1.25f) * MathFunctions.PI; Position pos = GetCaster().GetNearPosition(dist, angle); diff --git a/Source/Scripts/Spells/Warlock.cs b/Source/Scripts/Spells/Warlock.cs index 0b87d865d..2a5a2ca39 100644 --- a/Source/Scripts/Spells/Warlock.cs +++ b/Source/Scripts/Spells/Warlock.cs @@ -199,26 +199,22 @@ namespace Scripts.Spells.Warlock { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.GlyphOfDemonTraining, SpellIds.DevourMagicHeal); + return ValidateSpellInfo(SpellIds.GlyphOfDemonTraining, SpellIds.DevourMagicHeal) && spellInfo.GetEffects().Count > 1; } void OnSuccessfulDispel(uint effIndex) { - SpellEffectInfo effect = GetSpellInfo().GetEffect(1); - if (effect != null) - { - Unit caster = GetCaster(); - CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); - args.AddSpellMod(SpellValueMod.BasePoint0, effect.CalcValue(caster)); + Unit caster = GetCaster(); + CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); + args.AddSpellMod(SpellValueMod.BasePoint0, GetEffectInfo(1).CalcValue(caster)); - caster.CastSpell(caster, SpellIds.DevourMagicHeal, args); + caster.CastSpell(caster, SpellIds.DevourMagicHeal, args); - // Glyph of Felhunter - Unit owner = caster.GetOwner(); - if (owner) - if (owner.GetAura(SpellIds.GlyphOfDemonTraining) != null) - owner.CastSpell(owner, SpellIds.DevourMagicHeal, args); - } + // Glyph of Felhunter + Unit owner = caster.GetOwner(); + if (owner) + if (owner.GetAura(SpellIds.GlyphOfDemonTraining) != null) + owner.CastSpell(owner, SpellIds.DevourMagicHeal, args); } public override void Register() @@ -370,7 +366,7 @@ namespace Scripts.Spells.Warlock if (caster == null) return; - amount = caster.SpellBaseDamageBonusDone(GetSpellInfo().GetSchoolMask()) * GetSpellInfo().GetEffect(0).CalcValue(caster) / 100; + amount = caster.SpellBaseDamageBonusDone(GetSpellInfo().GetSchoolMask()) * GetEffectInfo(0).CalcValue(caster) / 100; } void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo) diff --git a/Source/Scripts/Spells/Warrior.cs b/Source/Scripts/Spells/Warrior.cs index 2717461ab..4cac3c2be 100644 --- a/Source/Scripts/Spells/Warrior.cs +++ b/Source/Scripts/Spells/Warrior.cs @@ -302,7 +302,7 @@ namespace Scripts.Spells.Warrior { public override bool Validate(SpellInfo spellInfo) { - return ValidateSpellInfo(SpellIds.Stoicism); + return ValidateSpellInfo(SpellIds.Stoicism) && spellInfo.GetEffects().Count > 1; } void HandleProc(ProcEventInfo eventInfo) @@ -310,7 +310,7 @@ namespace Scripts.Spells.Warrior PreventDefaultAction(); Unit target = eventInfo.GetActionTarget(); - int bp0 = (int)MathFunctions.CalculatePct(target.GetMaxHealth(), GetSpellInfo().GetEffect(1).CalcValue()); + int bp0 = (int)MathFunctions.CalculatePct(target.GetMaxHealth(), GetEffectInfo(1).CalcValue()); CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); args.AddSpellMod(SpellValueMod.BasePoint0, bp0); target.CastSpell((Unit)null, SpellIds.Stoicism, args); @@ -378,7 +378,7 @@ namespace Scripts.Spells.Warrior if (!ValidateSpellInfo(SpellIds.Shockwave, SpellIds.ShockwaveStun)) return false; - return spellInfo.GetEffect(0) != null && spellInfo.GetEffect(3) != null; + return spellInfo.GetEffects().Count > 3; } public override bool Load() @@ -395,8 +395,8 @@ namespace Scripts.Spells.Warrior // Cooldown reduced by 20 sec if it strikes at least 3 targets. void HandleAfterCast() { - if (_targetCount >= (uint)GetSpellInfo().GetEffect(0).CalcValue()) - GetCaster().ToPlayer().GetSpellHistory().ModifyCooldown(GetSpellInfo().Id, TimeSpan.FromSeconds(-GetSpellInfo().GetEffect(3).CalcValue())); + if (_targetCount >= (uint)GetEffectInfo(0).CalcValue()) + GetCaster().ToPlayer().GetSpellHistory().ModifyCooldown(GetSpellInfo().Id, TimeSpan.FromSeconds(-GetEffectInfo(3).CalcValue())); } public override void Register()