fix(Core/Spells): apply SPELLFAMILY_GENERIC mods to spells by default (#24996)

Co-authored-by: ariel- <ariel-@users.noreply.github.com>
This commit is contained in:
sogladev
2026-03-08 18:44:29 +01:00
committed by GitHub
parent 6d9b89c976
commit 06c0c6b655
5 changed files with 68 additions and 26 deletions

View File

@@ -1105,14 +1105,12 @@ bool AuraEffect::IsAffectedOnSpell(SpellInfo const* spell) const
{ {
if (!spell) if (!spell)
return false; 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; return false;
// Check EffectClassMask return true;
if (m_spellInfo->Effects[m_effIndex].SpellClassMask & spell->SpellFamilyFlags)
return true;
return false;
} }
bool AuraEffect::HasSpellClassMask() const bool AuraEffect::HasSpellClassMask() const

View File

@@ -1285,6 +1285,20 @@ bool SpellInfo::IsAutoRepeatRangedSpell() const
return AttributesEx2 & SPELL_ATTR2_AUTO_REPEAT; 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 bool SpellInfo::IsAffectedBySpellMods() const
{ {
return !(AttributesEx3 & SPELL_ATTR3_IGNORE_CASTER_MODIFIERS); 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); SpellInfo const* affectSpell = sSpellMgr->GetSpellInfo(mod->spellId);
if (!affectSpell) if (!affectSpell)
{
return false; return false;
}
if (!sScriptMgr->OnIsAffectedBySpellModCheck(affectSpell, this, mod)) if (!sScriptMgr->OnIsAffectedBySpellModCheck(affectSpell, this, mod))
{
return true; return true;
}
// False if affect_spell == nullptr or spellFamily not equal return IsAffected(affectSpell->SpellFamilyName, mod->mask);
if (affectSpell->SpellFamilyName != SpellFamilyName)
return false;
if (mod->mask && !(mod->mask & SpellFamilyFlags))
return false;
return true;
} }
bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const

View File

@@ -466,6 +466,8 @@ public:
bool IsRangedWeaponSpell() const; bool IsRangedWeaponSpell() const;
bool IsAutoRepeatRangedSpell() const; bool IsAutoRepeatRangedSpell() const;
[[nodiscard]] bool IsAffected(uint32 familyName, flag96 const& familyFlags) const;
bool IsAffectedBySpellMods() const; bool IsAffectedBySpellMods() const;
bool IsAffectedBySpellMod(SpellModifier const* mod) const; bool IsAffectedBySpellMod(SpellModifier const* mod) const;

View File

@@ -823,17 +823,10 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
// check spell family name/flags (if set) for spells // check spell family name/flags (if set) for spells
if (eventInfo.GetTypeMask() & SPELL_PROC_FLAG_MASK) if (eventInfo.GetTypeMask() & SPELL_PROC_FLAG_MASK)
{
if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo())
{ if (!eventSpellInfo->IsAffected(procEntry.SpellFamilyName, procEntry.SpellFamilyMask))
if (procEntry.SpellFamilyName && procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName)
return false; return false;
if (procEntry.SpellFamilyMask && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags))
return false;
}
}
// check spell type mask (if set) // check spell type mask (if set)
if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))
{ {

View File

@@ -790,6 +790,52 @@ TEST_F(SpellProcTest, CanSpellTriggerProcOnEvent_SpellFamilyFlagsZeroAcceptsAll)
EXPECT_TRUE(sSpellMgr->CanSpellTriggerProcOnEvent(procEntry, eventInfo)); 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 // Real-world Spell Proc Examples
// ============================================================================= // =============================================================================