From 25ff0c9ef491457576092255529cef1f05284c0f Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:15:16 -0600 Subject: [PATCH] fix(Core/Spells): Exempt kill/death events from triggered-spell proc filter (#24859) Co-authored-by: blinkysc --- src/server/game/Spells/Auras/SpellAuras.cpp | 5 +- src/test/mocks/ProcChanceTestHelper.h | 6 +- .../Spells/SpellProcTriggeredFilterTest.cpp | 108 ++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index d313b5ca1..9bb9ba9e3 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2164,9 +2164,12 @@ uint8 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, return 0; // check if aura can proc when spell is triggered (exception for hunter auto shot & wands) + // Kill/killed/death events should not be blocked by the triggered-spell check - + // the kill itself is the proc trigger, not the spell that dealt the killing blow if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_FROM_PROCS) && !(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && - !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK)) + !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK) && + !(eventInfo.GetTypeMask() & (PROC_FLAG_KILL | PROC_FLAG_KILLED | PROC_FLAG_DEATH))) { if (spell->IsTriggered() && !spell->GetSpellInfo()->HasAttribute(SPELL_ATTR3_NOT_A_PROC)) return 0; diff --git a/src/test/mocks/ProcChanceTestHelper.h b/src/test/mocks/ProcChanceTestHelper.h index 41e0dfa38..6ce878b8f 100644 --- a/src/test/mocks/ProcChanceTestHelper.h +++ b/src/test/mocks/ProcChanceTestHelper.h @@ -268,9 +268,13 @@ public: // Check if triggered spell filtering applies // SpellAuras.cpp:2195-2208 + static constexpr uint32 KILL_DEATH_PROC_FLAG_MASK = + PROC_FLAG_KILL | PROC_FLAG_KILLED | PROC_FLAG_DEATH; + if (!config.auraHasCanProcFromProcs && !(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && - !(eventTypeMask & AUTO_ATTACK_PROC_FLAG_MASK)) + !(eventTypeMask & AUTO_ATTACK_PROC_FLAG_MASK) && + !(eventTypeMask & KILL_DEATH_PROC_FLAG_MASK)) { // Filter triggered spells unless they have NOT_A_PROC if (config.isTriggered && !config.spellHasNotAProc) diff --git a/src/test/server/game/Spells/SpellProcTriggeredFilterTest.cpp b/src/test/server/game/Spells/SpellProcTriggeredFilterTest.cpp index 997425915..d55510e34 100644 --- a/src/test/server/game/Spells/SpellProcTriggeredFilterTest.cpp +++ b/src/test/server/game/Spells/SpellProcTriggeredFilterTest.cpp @@ -251,6 +251,114 @@ TEST_F(SpellProcTriggeredFilterTest, TakenAutoAttack_AllowsTriggeredSpells) << "TAKEN_MELEE_AUTO_ATTACK should allow triggered spells"; } +// ============================================================================= +// PROC_FLAG_KILL / PROC_FLAG_KILLED / PROC_FLAG_DEATH Exception +// Kill/death events bypass the triggered-spell filter because +// the kill itself is the proc trigger, not the spell that dealt +// the killing blow. +// ============================================================================= + +TEST_F(SpellProcTriggeredFilterTest, KillEvent_AllowsTriggeredSpells) +{ + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; + config.auraHasCanProcFromProcs = false; + config.spellHasNotAProc = false; + + auto procEntry = CreateBasicProcEntry(); + + // PROC_FLAG_KILL should bypass triggered-spell filter + EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, PROC_FLAG_KILL)) + << "PROC_FLAG_KILL should allow triggered spells"; +} + +TEST_F(SpellProcTriggeredFilterTest, KilledEvent_AllowsTriggeredSpells) +{ + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; + config.auraHasCanProcFromProcs = false; + config.spellHasNotAProc = false; + + auto procEntry = CreateBasicProcEntry(); + + // PROC_FLAG_KILLED should bypass triggered-spell filter + EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, PROC_FLAG_KILLED)) + << "PROC_FLAG_KILLED should allow triggered spells"; +} + +TEST_F(SpellProcTriggeredFilterTest, DeathEvent_AllowsTriggeredSpells) +{ + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; + config.auraHasCanProcFromProcs = false; + config.spellHasNotAProc = false; + + auto procEntry = CreateBasicProcEntry(); + + // PROC_FLAG_DEATH should bypass triggered-spell filter + EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, PROC_FLAG_DEATH)) + << "PROC_FLAG_DEATH should allow triggered spells"; +} + +TEST_F(SpellProcTriggeredFilterTest, KillWithOtherFlags_StillAllowsTriggeredSpells) +{ + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; + config.auraHasCanProcFromProcs = false; + config.spellHasNotAProc = false; + + auto procEntry = CreateBasicProcEntry(); + + // PROC_FLAG_KILL combined with other flags should still bypass + uint32 combinedEvent = PROC_FLAG_KILL | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + + EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, combinedEvent)) + << "PROC_FLAG_KILL combined with other flags should still bypass filter"; +} + +TEST_F(SpellProcTriggeredFilterTest, Scenario_RapidKilling_AutoShotKill) +{ + // Rapid Killing (34949) has PROC_FLAG_KILL + // Auto Shot (75) repeated casts are IsTriggered=true + // Kill event should proc Rapid Killing regardless + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; // Auto Shot is triggered + config.auraHasCanProcFromProcs = false; // Rapid Killing doesn't have this + config.spellHasNotAProc = false; // Auto Shot doesn't have NOT_A_PROC + + auto procEntry = SpellProcEntryBuilder() + .WithProcFlags(PROC_FLAG_KILL) + .WithChance(100.0f) + .Build(); + + // PROC_FLAG_KILL exemption should allow this + EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, PROC_FLAG_KILL)) + << "Rapid Killing should proc from Auto Shot kill"; +} + +TEST_F(SpellProcTriggeredFilterTest, KillEvent_SelfLoopStillBlocked) +{ + // Even with kill event exemption, self-loop should still be blocked + ProcChanceTestHelper::TriggeredSpellConfig config; + config.isTriggered = true; + config.triggeredByAuraSpellId = 34949; // Same as proc aura + config.procAuraSpellId = 34949; + + auto procEntry = SpellProcEntryBuilder() + .WithProcFlags(PROC_FLAG_KILL) + .WithChance(100.0f) + .Build(); + + EXPECT_TRUE(ProcChanceTestHelper::ShouldBlockTriggeredSpell( + config, procEntry, PROC_FLAG_KILL)) + << "Self-loop should still block even on kill events"; +} + // ============================================================================= // Combined Scenarios // =============================================================================