diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 1b2a4c325c..7e5493a776 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1418,7 +1418,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel m_spells[spellId] = newspell; // Do not go further if this Attribute exists - if (spellInfo->HasAttribute(SPELL_ATTR4_UNK15) && !IsWarlockMinion()) + if (spellInfo->HasAttribute(SPELL_ATTR4_HIDDEN_IN_SPELLBOOK) && !IsWarlockMinion()) return true; if (spellInfo->IsPassive() && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)))) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 1879747a7d..8148e49a7b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -21119,7 +21119,7 @@ void Player::PetSpellInitialize() continue; // Do not send this spells, they are used indirectly - if (sSpellMgr->GetSpellInfo(itr->first)->HasAttribute(SPELL_ATTR4_UNK15)) + if (sSpellMgr->GetSpellInfo(itr->first)->HasAttribute(SPELL_ATTR4_HIDDEN_IN_SPELLBOOK)) continue; data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active)); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 86bc2ecc17..131ded7cf8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -830,17 +830,6 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam else victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage, 0); - // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots) - if (!damage && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage) - if (victim != this && victim->GetTypeId() == TYPEID_PLAYER) - if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) - if (spell->getState() == SPELL_STATE_PREPARING) - { - uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; - if ((interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) != 0) - victim->InterruptNonMeleeSpells(false); - } - // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation AuraEffectList vCopyDamageCopy(victim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT)); @@ -1007,28 +996,48 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam } } - if (damagetype != NODAMAGE && damage) + if (damagetype != NODAMAGE) { - if (victim != this && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back - (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) + if (victim != this + && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) { if (damagetype != DOT) + { if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + { if (spell->getState() == SPELL_STATE_PREPARING) { - uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; - if (interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) + auto isCastInterrupted = [&]() + { + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))) + return true; + + return false; + }; + + auto isCastDelayed = [&]() + { + if (!damage) + return false; + + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly))) + return true; + + return false; + }; + + if (isCastInterrupted()) victim->InterruptNonMeleeSpells(false); - else if (interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK) + else if (isCastDelayed()) spell->Delayed(); } - - if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) - if (spell->getState() == SPELL_STATE_CASTING) - { - if (spell->getState() == SPELL_STATE_CASTING && spell->m_spellInfo->HasChannelInterruptFlag(SpellAuraInterruptFlags::DamageChannelDuration) && damagetype != DOT) - spell->DelayedChannel(); } + } + + if (damage && victim->IsPlayer()) + if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) + if (spell->getState() == SPELL_STATE_CASTING && spell->m_spellInfo->HasChannelInterruptFlag(SpellAuraInterruptFlags::DamageChannelDuration)) + spell->DelayedChannel(); } } @@ -9055,6 +9064,10 @@ void Unit::setDeathState(DeathState s) void Unit::AtEnterCombat() { + if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL]) + if (spell->getState() == SPELL_STATE_PREPARING && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Combat)) + InterruptNonMeleeSpells(false); + RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::EnteringCombat); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 23961558e5..8efb1485a5 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3265,9 +3265,8 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && - m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) - if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) - + m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) + if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3890,7 +3889,7 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && + m_caster->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement) && (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { @@ -4276,7 +4275,7 @@ void Spell::SendSpellStart() uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; uint32 schoolImmunityMask = m_casttime != 0 ? m_caster->GetSchoolImmunityMask() : 0; - uint32 mechanicImmunityMask = m_casttime != 0 ? m_spellInfo->GetMechanicImmunityMask(m_caster, false) : 0; + uint32 mechanicImmunityMask = m_casttime != 0 ? m_spellInfo->GetMechanicImmunityMask(m_caster) : 0; if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -4836,7 +4835,7 @@ void Spell::SendChannelStart(uint32 duration) uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(m_caster, true); + uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(m_caster); if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -7238,10 +7237,6 @@ void Spell::Delayed() // only called in DealDamage() if (isDelayableNoMore()) // Spells may only be delayed twice return; - // spells not loosing casting time (slam, dynamites, bombs..) - //if (!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE)) - // return; - //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms int32 delayReduce = 100; // must be initialized to 100 for percent modifiers @@ -7483,8 +7478,7 @@ bool Spell::IsChannelActive() const bool Spell::IsAutoActionResetSpell() const { - /// @todo changed SPELL_INTERRUPT_FLAG_AUTOATTACK -> SPELL_INTERRUPT_FLAG_INTERRUPT to fix compile - is this check correct at all? - if (IsTriggered() || !(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT)) + if (IsTriggered()) return false; if (!m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR6_NOT_RESET_SWING_IF_INSTANT)) diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 98d734babb..0053a5882e 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -26,16 +26,19 @@ class Item; class AuraEffect; -enum SpellInterruptFlags : uint32 +enum class SpellInterruptFlags : uint32 { - SPELL_INTERRUPT_FLAG_MOVEMENT = 0x01, // why need this for instant? - SPELL_INTERRUPT_FLAG_PUSH_BACK = 0x02, // push back - SPELL_INTERRUPT_FLAG_UNK3 = 0x04, // any info? - SPELL_INTERRUPT_FLAG_INTERRUPT = 0x08, // interrupt - SPELL_INTERRUPT_FLAG_ABORT_ON_DMG = 0x10 // _complete_ interrupt on direct damage - //SPELL_INTERRUPT_UNK = 0x20 // unk, 564 of 727 spells having this spell start with "Glyph" + None = 0, + Movement = 0x00000001, + DamagePushbackPlayerOnly = 0x00000002, + Stun = 0x00000004, // useless, even spells without it get interrupted + Combat = 0x00000008, + DamageCancelsPlayerOnly = 0x00000010, + MeleeCombat = 0x00000020 // NYI }; +DEFINE_ENUM_FLAG(SpellInterruptFlags); + // See SpellAuraInterruptFlags for other values definitions enum SpellChannelInterruptFlags { diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 755c45a6eb..f0abf5a705 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1427,10 +1427,6 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->GetPowerType() != power && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION - && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) - return; - if (unitTarget->GetMaxPower(power) == 0) return; @@ -1490,10 +1486,6 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) return; Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->GetPowerType() != power && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) - return; - uint32 maxPower = unitTarget->GetMaxPower(power); if (maxPower == 0) return; @@ -2888,9 +2880,7 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex) // check if we can interrupt spell if ((spell->getState() == SPELL_STATE_CASTING || (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastTime() > 0.0f)) - && (curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE || curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_UNK) - && ((i == CURRENT_GENERIC_SPELL && curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) - || i == CURRENT_CHANNELED_SPELL)) + && curSpellInfo->CanBeInterrupted(unitTarget)) { if (m_originalCaster) { diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index b8e827d413..b9f98d0a35 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -992,7 +992,8 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntry const** effe AuraInterruptFlags = _interrupt ? SpellAuraInterruptFlags(_interrupt->AuraInterruptFlags[0]) : SpellAuraInterruptFlags::None; AuraInterruptFlags2 = _interrupt? SpellAuraInterruptFlags2(_interrupt->AuraInterruptFlags[1]) : SpellAuraInterruptFlags2::None; ChannelInterruptFlags = _interrupt ? SpellAuraInterruptFlags(_interrupt->ChannelInterruptFlags[0] ) : SpellAuraInterruptFlags::None; - ChannelInterruptFlags2 = _interrupt? SpellAuraInterruptFlags2(_interrupt->ChannelInterruptFlags[1]) : SpellAuraInterruptFlags2::None; + ChannelInterruptFlags2 = _interrupt ? SpellAuraInterruptFlags2(_interrupt->ChannelInterruptFlags[1]) : SpellAuraInterruptFlags2::None; + InterruptFlags = _interrupt ? SpellInterruptFlags(_interrupt->InterruptFlags) : SpellInterruptFlags::None; // SpellLevelsEntry SpellLevelsEntry const* _levels = GetSpellLevels(); @@ -1111,6 +1112,15 @@ bool SpellInfo::HasOnlyDamageEffects() const return true; } +bool SpellInfo::CanBeInterrupted(Unit* interruptTarget) const +{ + return HasAttribute(SPELL_ATTR7_CAN_ALWAYS_BE_INTERRUPTED) + || HasChannelInterruptFlag(SpellAuraInterruptFlags::Damage | SpellAuraInterruptFlags::EnteringCombat) + || (interruptTarget->IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly)) + || (!(interruptTarget->GetMechanicImmunityMask() & (1 << MECHANIC_INTERRUPT)) + && PreventionType & SPELL_PREVENTION_TYPE_SILENCE); +} + bool SpellInfo::HasAnyAuraInterruptFlag() const { return AuraInterruptFlags != SpellAuraInterruptFlags::None || AuraInterruptFlags2 != SpellAuraInterruptFlags2::None; @@ -3267,20 +3277,20 @@ uint32 SpellInfo::GetAllowedMechanicMask() const return _allowedMechanicMask; } -uint32 SpellInfo::GetMechanicImmunityMask(Unit* caster, bool channeled) const +uint32 SpellInfo::GetMechanicImmunityMask(Unit* caster) const { + if (HasAttribute(SPELL_ATTR7_CAN_ALWAYS_BE_INTERRUPTED)) + return 0; + uint32 casterMechanicImmunityMask = caster->GetMechanicImmunityMask(); uint32 mechanicImmunityMask = 0; - uint32 flags = channeled ? ChannelInterruptFlags.AsUnderlyingType() : InterruptFlags; - - // @todo: research other interrupt flags - if (flags & SPELL_INTERRUPT_FLAG_INTERRUPT) + if (CanBeInterrupted(caster)) { - if (casterMechanicImmunityMask & (1 << MECHANIC_SILENCE)) - mechanicImmunityMask |= (1 << MECHANIC_SILENCE); - if (casterMechanicImmunityMask & (1 << MECHANIC_INTERRUPT)) mechanicImmunityMask |= (1 << MECHANIC_INTERRUPT); + + if (casterMechanicImmunityMask & (1 << MECHANIC_SILENCE)) + mechanicImmunityMask |= (1 << MECHANIC_SILENCE); } return mechanicImmunityMask; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 90973b34ee..95c05536e4 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -370,7 +370,7 @@ class TC_GAME_API SpellInfo uint32 CategoryRecoveryTime; uint32 StartRecoveryCategory; uint32 StartRecoveryTime; - uint32 InterruptFlags; + EnumFlag InterruptFlags = SpellInterruptFlags::None; EnumFlag AuraInterruptFlags = SpellAuraInterruptFlags::None; EnumFlag AuraInterruptFlags2 = SpellAuraInterruptFlags2::None; EnumFlag ChannelInterruptFlags = SpellAuraInterruptFlags::None; @@ -481,6 +481,8 @@ class TC_GAME_API SpellInfo inline bool HasAttribute(SpellAttr10 attribute) const { return !!(AttributesEx10 & attribute); } inline bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } + bool CanBeInterrupted(Unit* interruptTarget) const; + bool HasAnyAuraInterruptFlag() const; bool HasAuraInterruptFlag(SpellAuraInterruptFlags flag) const { return AuraInterruptFlags.HasFlag(flag); } bool HasAuraInterruptFlag(SpellAuraInterruptFlags2 flag) const { return AuraInterruptFlags2.HasFlag(flag); } @@ -603,7 +605,7 @@ class TC_GAME_API SpellInfo uint32 GetAllowedMechanicMask() const; - uint32 GetMechanicImmunityMask(Unit* caster, bool channeled) const; + uint32 GetMechanicImmunityMask(Unit* caster) const; private: // loading helpers