From 06c0c6b655509889e56c8517adb00badee2efaa9 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 8 Mar 2026 18:44:29 +0100 Subject: [PATCH] fix(Core/Spells): apply SPELLFAMILY_GENERIC mods to spells by default (#24996) Co-authored-by: ariel- --- .../game/Spells/Auras/SpellAuraEffects.cpp | 10 ++-- src/server/game/Spells/SpellInfo.cpp | 27 ++++++----- src/server/game/Spells/SpellInfo.h | 2 + src/server/game/Spells/SpellMgr.cpp | 9 +--- src/test/server/game/Spells/SpellProcTest.cpp | 46 +++++++++++++++++++ 5 files changed, 68 insertions(+), 26 deletions(-) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 045b21565..f8298d56d 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1105,14 +1105,12 @@ bool AuraEffect::IsAffectedOnSpell(SpellInfo const* spell) const { if (!spell) return false; - // Check family name - if (spell->SpellFamilyName != m_spellInfo->SpellFamilyName) + + // Check family name and EffectClassMask + if (!spell->IsAffected(m_spellInfo->SpellFamilyName, m_spellInfo->Effects[m_effIndex].SpellClassMask)) return false; - // Check EffectClassMask - if (m_spellInfo->Effects[m_effIndex].SpellClassMask & spell->SpellFamilyFlags) - return true; - return false; + return true; } bool AuraEffect::HasSpellClassMask() const diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 978f73d9f..3b96e673e 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1285,6 +1285,20 @@ bool SpellInfo::IsAutoRepeatRangedSpell() const return AttributesEx2 & SPELL_ATTR2_AUTO_REPEAT; } +bool SpellInfo::IsAffected(uint32 familyName, flag96 const& familyFlags) const +{ + if (!familyName) + return true; + + if (familyName != SpellFamilyName) + return false; + + if (familyFlags && !(familyFlags & SpellFamilyFlags)) + return false; + + return true; +} + bool SpellInfo::IsAffectedBySpellMods() const { return !(AttributesEx3 & SPELL_ATTR3_IGNORE_CASTER_MODIFIERS); @@ -1300,23 +1314,12 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const SpellInfo const* affectSpell = sSpellMgr->GetSpellInfo(mod->spellId); if (!affectSpell) - { return false; - } if (!sScriptMgr->OnIsAffectedBySpellModCheck(affectSpell, this, mod)) - { return true; - } - // False if affect_spell == nullptr or spellFamily not equal - if (affectSpell->SpellFamilyName != SpellFamilyName) - return false; - - if (mod->mask && !(mod->mask & SpellFamilyFlags)) - return false; - - return true; + return IsAffected(affectSpell->SpellFamilyName, mod->mask); } bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 50b6d88d4..131471937 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -466,6 +466,8 @@ public: bool IsRangedWeaponSpell() const; bool IsAutoRepeatRangedSpell() const; + [[nodiscard]] bool IsAffected(uint32 familyName, flag96 const& familyFlags) const; + bool IsAffectedBySpellMods() const; bool IsAffectedBySpellMod(SpellModifier const* mod) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index cb4f80d39..d6064fe33 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -823,17 +823,10 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE // check spell family name/flags (if set) for spells if (eventInfo.GetTypeMask() & SPELL_PROC_FLAG_MASK) - { if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) - { - if (procEntry.SpellFamilyName && procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName) + if (!eventSpellInfo->IsAffected(procEntry.SpellFamilyName, procEntry.SpellFamilyMask)) return false; - if (procEntry.SpellFamilyMask && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags)) - return false; - } - } - // check spell type mask (if set) if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) { diff --git a/src/test/server/game/Spells/SpellProcTest.cpp b/src/test/server/game/Spells/SpellProcTest.cpp index 7bf7fe869..aa44b8557 100644 --- a/src/test/server/game/Spells/SpellProcTest.cpp +++ b/src/test/server/game/Spells/SpellProcTest.cpp @@ -790,6 +790,52 @@ TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyFlagsZeroAcceptsAll) EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo)); } +TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_GenericFamilyIgnoresMask) +{ + // Generic family (0) should bypass family mask checks entirely + auto* spellInfo = SpellInfoBuilder() + .WithId(101) + .WithSpellFamilyName(SPELLFAMILY_MAGE) + .WithSpellFamilyFlags(0x1, 0, 0) // some mage flag + .Build(); + _spellInfos.push_back(spellInfo); + + DamageInfo damageInfo(nullptr, nullptr, 100, spellInfo, SPELL_SCHOOL_MASK_FIRE, SPELL_DIRECT_DAMAGE); + + auto procEntry = SpellProcEntryBuilder() + .WithProcFlags(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG) + .WithSpellFamilyName(0) // generic family + .WithSpellFamilyMask(flag96(0x2, 0, 0)) // does NOT match the spell's flags + .WithSpellPhaseMask(PROC_SPELL_PHASE_HIT) + .Build(); + + auto eventInfo = ProcEventInfoBuilder() + .WithTypeMask(PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG) + .WithSpellPhaseMask(PROC_SPELL_PHASE_HIT) + .WithHitMask(PROC_HIT_NORMAL) + .WithDamageInfo(&damageInfo) + .Build(); + + EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo)) + << "Generic family entries should ignore mask restrictions"; +} + +TEST_F(SpellProcTest, SpellInfo_IsAffected_GenericBehavior) +{ + auto* spellInfo = SpellInfoBuilder() + .WithId(102) + .WithSpellFamilyName(SPELLFAMILY_WARLOCK) + .WithSpellFamilyFlags(0x4, 0, 0) + .Build(); + _spellInfos.push_back(spellInfo); + + // generic family should return true regardless of mask + EXPECT_TRUE(spellInfo->IsAffected(0, flag96(0x4, 0, 0))); + EXPECT_TRUE(spellInfo->IsAffected(0, flag96(0x8, 0, 0))); + // a non-generic family still respects mask + EXPECT_FALSE(spellInfo->IsAffected(SPELLFAMILY_PALADIN, flag96(0x4, 0, 0))); +} + // ============================================================================= // Real-world Spell Proc Examples // =============================================================================