From e3994d9636f1db658fc8006f920260ee858cff2a Mon Sep 17 00:00:00 2001 From: hondacrx Date: Mon, 29 Mar 2021 13:09:27 -0400 Subject: [PATCH] Core/Spells: Defined and implemented new spell interrupt flags Port From (https://github.com/TrinityCore/TrinityCore/commit/07a47947e2e189649e48a093b66c8c3de8872ac0) --- .../Framework/Constants/Spells/SpellConst.cs | 18 +++-- Source/Game/Entities/Unit/Unit.Combat.cs | 71 +++++++++++++------ Source/Game/Spells/Spell.cs | 3 +- Source/Game/Spells/SpellEffects.cs | 4 +- Source/Game/Spells/SpellInfo.cs | 12 ++++ 5 files changed, 76 insertions(+), 32 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index 410928e02..e708dd07b 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -100,12 +100,18 @@ namespace Framework.Constants public enum SpellInterruptFlags { - Movement = 0x01, // why need this for instant? - PushBack = 0x02, // push back - Unk3 = 0x04, // any info? - Interrupt = 0x08, // interrupt - AbortOnDmg = 0x10 // _complete_ interrupt on direct damage - //SPELL_INTERRUPT_UNK = 0x20 // unk, 564 of 727 spells having this spell start with "Glyph" + None = 0, + Movement = 0x01, + DamagePushbackPlayerOnly = 0x02, + Stun = 0x04, // useless, even spells without it get interrupted + Combat = 0x08, + DamageCancelsPlayerOnly = 0x10, + MeleeCombat = 0x20, // NYI + Immunity = 0x40, // NYI + DamageAbsorb = 0x80, + ZeroDamageCancels = 0x100, + DamagePushback = 0x200, + DamageCancels = 0x400 } public enum SpellAuraInterruptFlags : uint diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index d47f5fb62..4db2b115e 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -1011,21 +1011,14 @@ namespace Game.Entities else victim.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Damage, 0); - // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots) if (damage == 0 && damagetype != DamageEffectType.DOT && cleanDamage != null && cleanDamage.absorbed_damage != 0) { - if (victim != this && victim.IsTypeId(TypeId.Player)) + if (victim != this && victim.IsPlayer()) { Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic); - if (spell) - { - if (spell.GetState() == SpellState.Preparing) - { - SpellInterruptFlags interruptFlags = spell.m_spellInfo.InterruptFlags; - if (interruptFlags.HasAnyFlag(SpellInterruptFlags.AbortOnDmg)) - victim.InterruptNonMeleeSpells(false); - } - } + if (spell != null) + if (spell.GetState() == SpellState.Preparing && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageAbsorb)) + victim.InterruptNonMeleeSpells(false); } } @@ -1179,10 +1172,9 @@ namespace Game.Entities } } - if (damagetype != DamageEffectType.NoDamage && damage != 0) + if (damagetype != DamageEffectType.NoDamage) { - if (victim != this && victim.IsTypeId(TypeId.Player) && // does not support creature push_back - (spellProto == null || !(spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage)))) + if (victim != this && (spellProto == null || !spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage))) { if (damagetype != DamageEffectType.DOT) { @@ -1191,20 +1183,52 @@ namespace Game.Entities { if (spell.GetState() == SpellState.Preparing) { - var interruptFlags = spell.m_spellInfo.InterruptFlags; - if (interruptFlags.HasAnyFlag(SpellInterruptFlags.AbortOnDmg)) + bool isCastInterrupted() + { + if (damage == 0) + return spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.ZeroDamageCancels); + + if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) + return true; + + if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamageCancels)) + return true; + + return false; + }; + + bool isCastDelayed() + { + if (damage == 0) + return false; + + if (victim.IsPlayer() && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushbackPlayerOnly)) + return true; + + if (spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.DamagePushback)) + return true; + + return false; + } + + if (isCastInterrupted()) victim.InterruptNonMeleeSpells(false); - else if (interruptFlags.HasAnyFlag(SpellInterruptFlags.PushBack)) + else if (isCastDelayed()) spell.Delayed(); } } + + if (damage != 0 && victim.IsPlayer()) + { + Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled); + if (spell1 != null) + if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration)) + spell1.DelayedChannel(); + } } - Spell spell1 = victim.GetCurrentSpell(CurrentSpellTypes.Channeled); - if (spell1 != null) - if (spell1.GetState() == SpellState.Casting && spell1.m_spellInfo.HasChannelInterruptFlag(SpellAuraInterruptFlags.DamageChannelDuration) && damagetype != DamageEffectType.DOT) - spell1.DelayedChannel(); } } + // last damage from duel opponent if (duel_hasEnded) { @@ -1443,6 +1467,11 @@ namespace Game.Entities aurApp.GetBase().CallScriptEnterLeaveCombatHandlers(aurApp, true); } + Spell spell = GetCurrentSpell(CurrentSpellTypes.Generic); + if (spell != null) + if (spell.GetState() == SpellState.Preparing && spell.m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Combat)) + InterruptNonMeleeSpells(false); + RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.EnteringCombat); ProcSkillsAndAuras(enemy, ProcFlags.EnterCombat, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null); } diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 73c642e56..5b6214122 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -6688,8 +6688,7 @@ namespace Game.Spells bool IsAutoActionResetSpell() { - // @todo changed SPELL_INTERRUPT_FLAG_AUTOATTACK . SPELL_INTERRUPT_FLAG_INTERRUPT to fix compile - is this check correct at all? - if (IsTriggered() || !m_spellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Interrupt)) + if (IsTriggered()) return false; if (m_casttime == 0 && m_spellInfo.HasAttribute(SpellAttr6.NotResetSwingIfInstant)) diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index b8f1f55b3..b593d1a93 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -2696,9 +2696,7 @@ namespace Game.Spells // check if we can interrupt spell if ((spell.GetState() == SpellState.Casting || (spell.GetState() == SpellState.Preparing && spell.GetCastTime() > 0.0f)) - && (curSpellInfo.PreventionType.HasAnyFlag(SpellPreventionType.Silence)) - && ((i == CurrentSpellTypes.Generic && curSpellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Interrupt)) - || (i == CurrentSpellTypes.Channeled))) + && curSpellInfo.CanBeInterrupted(m_caster, unitTarget)) { if (m_originalCaster != null) { diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index bcd6c38af..b94c06293 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -3609,6 +3609,18 @@ namespace Game.Spells public bool HasAttribute(SpellAttr14 attribute) { return Convert.ToBoolean(AttributesEx14 & attribute); } public bool HasAttribute(SpellCustomAttributes attribute) { return Convert.ToBoolean(AttributesCu & attribute); } + public bool CanBeInterrupted(Unit interruptCaster, Unit interruptTarget) + { + return HasAttribute(SpellAttr7.CanAlwaysBeInterrupted) + || HasChannelInterruptFlag(SpellAuraInterruptFlags.Damage | SpellAuraInterruptFlags.EnteringCombat) + || (interruptTarget.IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) + || InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancels) + || interruptCaster.HasAuraTypeWithMiscvalue(AuraType.AllowInterruptSpell, (int)Id) + || ((interruptTarget.GetMechanicImmunityMask() & (1 << (int)Mechanics.Interrupt)) == 0 + && !interruptTarget.HasAuraTypeWithAffectMask(AuraType.PreventInterrupt, this) + && PreventionType.HasAnyFlag(SpellPreventionType.Silence)); + } + public bool HasAnyAuraInterruptFlag() { return AuraInterruptFlags != SpellAuraInterruptFlags.None || AuraInterruptFlags2 != SpellAuraInterruptFlags2.None; } public bool HasAuraInterruptFlag(SpellAuraInterruptFlags flag) { return AuraInterruptFlags.HasAnyFlag(flag); } public bool HasAuraInterruptFlag(SpellAuraInterruptFlags2 flag) { return AuraInterruptFlags2.HasAnyFlag(flag); }