fix(Core/Spells): Prevent extra attack abilities from chain-proccing (#24806)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: trickerer <onlysuffering@gmail.com>
This commit is contained in:
@@ -1239,6 +1239,29 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn
|
|||||||
if (GetCasterGUID() != eventInfo.GetActor()->GetGUID())
|
if (GetCasterGUID() != eventInfo.GetActor()->GetGUID())
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
case SPELL_AURA_PROC_TRIGGER_SPELL:
|
||||||
|
case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
|
||||||
|
{
|
||||||
|
// Don't proc extra attacks while already processing extra attack spell
|
||||||
|
uint32 triggerSpellId = m_spellInfo->Effects[GetEffIndex()].TriggerSpell;
|
||||||
|
if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId))
|
||||||
|
{
|
||||||
|
if (triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
|
||||||
|
{
|
||||||
|
uint32 lastExtraAttackSpell = eventInfo.GetActor()->GetLastExtraAttackSpell();
|
||||||
|
|
||||||
|
// Patch 1.12.0(?) extra attack abilities can no longer chain proc themselves
|
||||||
|
if (lastExtraAttackSpell == triggerSpellId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Patch 2.2.0 Sword Specialization (Warrior, Rogue) extra attack can no longer proc additional extra attacks
|
||||||
|
// 3.3.5 Sword Specialization (Warrior), Hack and Slash (Rogue)
|
||||||
|
if (lastExtraAttackSpell == SPELL_SWORD_SPECIALIZATION || lastExtraAttackSpell == SPELL_HACK_AND_SLASH)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,6 +282,45 @@ public:
|
|||||||
return false; // Allow proc
|
return false; // Allow proc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Extra Attack Chain-Proc Prevention - simulates SpellAuraEffects.cpp:1245-1261
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration for simulating extra attack chain-proc prevention
|
||||||
|
*/
|
||||||
|
struct ExtraAttackProcConfig
|
||||||
|
{
|
||||||
|
bool triggeredSpellHasExtraAttacks = false; // triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)
|
||||||
|
uint32 triggerSpellId = 0; // m_spellInfo->Effects[GetEffIndex()].TriggerSpell
|
||||||
|
uint32 lastExtraAttackSpell = 0; // eventInfo.GetActor()->GetLastExtraAttackSpell()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simulate extra attack chain-proc prevention from CheckEffectProc
|
||||||
|
* Returns true if proc should be blocked
|
||||||
|
*
|
||||||
|
* @param config Extra attack proc configuration
|
||||||
|
* @return true if proc should be blocked
|
||||||
|
*/
|
||||||
|
static bool ShouldBlockExtraAttackChainProc(ExtraAttackProcConfig const& config)
|
||||||
|
{
|
||||||
|
// Only applies when the triggered spell grants extra attacks
|
||||||
|
if (!config.triggeredSpellHasExtraAttacks)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Patch 1.12.0(?) extra attack abilities can no longer chain proc themselves
|
||||||
|
if (config.lastExtraAttackSpell == config.triggerSpellId)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Patch 2.2.0 Sword Specialization (Warrior, Rogue) extra attack can no longer proc additional extra attacks
|
||||||
|
// 3.3.5 Sword Specialization (Warrior), Hack and Slash (Rogue)
|
||||||
|
if (config.lastExtraAttackSpell == 16459 || config.lastExtraAttackSpell == 66923)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// DisableEffectsMask - simulates SpellAuras.cpp:2244-2258
|
// DisableEffectsMask - simulates SpellAuras.cpp:2244-2258
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ExtraAttackChainProcTest.cpp
|
||||||
|
* @brief Unit tests for extra attack chain-proc prevention
|
||||||
|
*
|
||||||
|
* Tests the logic from SpellAuraEffects.cpp:1245-1261 (CheckEffectProc):
|
||||||
|
* - Self-chain prevention (same extra attack spell can't proc itself)
|
||||||
|
* - Cross-chain prevention (Sword Specialization / Hack and Slash block all extra attack procs)
|
||||||
|
* - Non-blacklisted extra attack spells allow cross-proccing
|
||||||
|
* - Non-extra-attack procs are unaffected by the guard
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ProcChanceTestHelper.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
// Use existing enum from Unit.h: SPELL_SWORD_SPECIALIZATIONIALIZATION (16459), SPELL_HACK_AND_SLASH (66923)
|
||||||
|
constexpr uint32 SPELL_RECKONING = 32746; // Reckoning (Paladin)
|
||||||
|
constexpr uint32 SPELL_HAND_OF_JUSTICE = 15601; // Hand of Justice extra attack
|
||||||
|
|
||||||
|
class ExtraAttackChainProcTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
ProcChanceTestHelper::ExtraAttackProcConfig MakeConfig(
|
||||||
|
bool hasExtraAttacks, uint32 triggerSpellId, uint32 lastExtraAttack)
|
||||||
|
{
|
||||||
|
ProcChanceTestHelper::ExtraAttackProcConfig config;
|
||||||
|
config.triggeredSpellHasExtraAttacks = hasExtraAttacks;
|
||||||
|
config.triggerSpellId = triggerSpellId;
|
||||||
|
config.lastExtraAttackSpell = lastExtraAttack;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Normal proc (no extra attack in progress)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, NormalProc_AllowedWhenNoExtraAttackInProgress)
|
||||||
|
{
|
||||||
|
// lastExtraAttackSpell == 0 means no extra attack is executing
|
||||||
|
auto config = MakeConfig(true, SPELL_SWORD_SPECIALIZATION, 0);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Extra attack proc should be allowed when no extra attack is in progress";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Self-chain prevention
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, SelfChain_BlockedWhenSameSpell)
|
||||||
|
{
|
||||||
|
// Sword Spec trying to proc during its own extra attack
|
||||||
|
auto config = MakeConfig(true, SPELL_SWORD_SPECIALIZATION, SPELL_SWORD_SPECIALIZATION);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Extra attack spell should not chain-proc itself";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Cross-chain prevention (blacklisted spells)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, CrossChain_BlockedBySwordSpecialization)
|
||||||
|
{
|
||||||
|
// Reckoning trying to proc during Sword Spec extra attack
|
||||||
|
auto config = MakeConfig(true, SPELL_RECKONING, SPELL_SWORD_SPECIALIZATION);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Sword Specialization extra attack should block all other extra attack procs";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, CrossChain_BlockedByHackAndSlash)
|
||||||
|
{
|
||||||
|
// Reckoning trying to proc during Hack and Slash extra attack
|
||||||
|
auto config = MakeConfig(true, SPELL_RECKONING, SPELL_HACK_AND_SLASH);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Hack and Slash extra attack should block all other extra attack procs";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Non-blacklisted extra attacks allow cross-proccing
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, DifferentExtraAttack_AllowedWhenNotBlacklisted)
|
||||||
|
{
|
||||||
|
// Sword Spec trying to proc during Hand of Justice extra attack
|
||||||
|
// Hand of Justice (15601) is not blacklisted, so cross-proc is allowed
|
||||||
|
auto config = MakeConfig(true, SPELL_SWORD_SPECIALIZATION, SPELL_HAND_OF_JUSTICE);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Non-blacklisted extra attack spells should allow cross-proccing";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Non-extra-attack procs unaffected
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, NonExtraAttackProc_UnaffectedByExtraAttackState)
|
||||||
|
{
|
||||||
|
// A proc that does NOT grant extra attacks should never be blocked,
|
||||||
|
// even during Sword Spec extra attack
|
||||||
|
auto config = MakeConfig(false, 12345, SPELL_SWORD_SPECIALIZATION);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Non-extra-attack procs should be unaffected by extra attack state";
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Real spell scenarios
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, Reckoning_SelfChainBlocked)
|
||||||
|
{
|
||||||
|
// Reckoning (32746) trying to proc during its own extra attack
|
||||||
|
auto config = MakeConfig(true, SPELL_RECKONING, SPELL_RECKONING);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Reckoning should not chain-proc itself";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExtraAttackChainProcTest, Reckoning_AllowedDuringHandOfJustice)
|
||||||
|
{
|
||||||
|
// Reckoning trying to proc during Hand of Justice extra attack
|
||||||
|
// Hand of Justice is not blacklisted, so Reckoning is allowed
|
||||||
|
auto config = MakeConfig(true, SPELL_RECKONING, SPELL_HAND_OF_JUSTICE);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ProcChanceTestHelper::ShouldBlockExtraAttackChainProc(config))
|
||||||
|
<< "Reckoning should be allowed during Hand of Justice extra attack";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user