fix(Core/Spells): Fix Rapid Recuperation, Rapid Killing, and auto-generate PROC_ATTR_REQ_SPELLMOD (#24830)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
-- Fix spell_script_names for spell_hun_rapid_recuperation
|
||||||
|
-- Script was moved from talent (53228/53232) to periodic mana aura (56654/58882)
|
||||||
|
DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_hun_rapid_recuperation';
|
||||||
|
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
|
||||||
|
(56654, 'spell_hun_rapid_recuperation'),
|
||||||
|
(58882, 'spell_hun_rapid_recuperation');
|
||||||
|
|
||||||
|
-- Remove explicit Inner Focus spell_proc entry (now auto-generated with PROC_ATTR_REQ_SPELLMOD)
|
||||||
|
DELETE FROM `spell_proc` WHERE `SpellId` = 14751;
|
||||||
@@ -6563,12 +6563,6 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster)
|
|||||||
caster->CastSpell(target, triggerSpellId, false);
|
caster->CastSpell(target, triggerSpellId, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Hunter - Rapid Recuperation
|
|
||||||
case 56654:
|
|
||||||
case 58882:
|
|
||||||
int32 amount = int32(target->GetMaxPower(POWER_MANA) * GetAmount() / 100.0f);
|
|
||||||
target->CastCustomSpell(target, triggerSpellId, &amount, nullptr, nullptr, true, nullptr, this);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2073,6 +2073,20 @@ void SpellMgr::LoadSpellProcs()
|
|||||||
if (addTriggerFlag)
|
if (addTriggerFlag)
|
||||||
procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC;
|
procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC;
|
||||||
|
|
||||||
|
// Modifier auras with charges should require spellmod validation
|
||||||
|
if (spellInfo->ProcCharges)
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
|
{
|
||||||
|
if (spellInfo->Effects[i].IsAura(SPELL_AURA_ADD_FLAT_MODIFIER) ||
|
||||||
|
spellInfo->Effects[i].IsAura(SPELL_AURA_ADD_PCT_MODIFIER))
|
||||||
|
{
|
||||||
|
procEntry.AttributesMask |= PROC_ATTR_REQ_SPELLMOD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate DisableEffectsMask for effects that shouldn't trigger procs
|
// Calculate DisableEffectsMask for effects that shouldn't trigger procs
|
||||||
uint32 nonProcMask = 0;
|
uint32 nonProcMask = 0;
|
||||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
|
|||||||
@@ -73,8 +73,6 @@ enum HunterSpells
|
|||||||
// Proc system spells
|
// Proc system spells
|
||||||
SPELL_HUNTER_THRILL_OF_THE_HUNT_MANA = 34720,
|
SPELL_HUNTER_THRILL_OF_THE_HUNT_MANA = 34720,
|
||||||
SPELL_HUNTER_REPLENISHMENT = 57669,
|
SPELL_HUNTER_REPLENISHMENT = 57669,
|
||||||
SPELL_HUNTER_RAPID_RECUPERATION_R1 = 56654,
|
|
||||||
SPELL_HUNTER_RAPID_RECUPERATION_R2 = 58882,
|
|
||||||
SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS = 57894,
|
SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS = 57894,
|
||||||
SPELL_HUNTER_KILL_COMMAND_HUNTER = 34027,
|
SPELL_HUNTER_KILL_COMMAND_HUNTER = 34027,
|
||||||
SPELL_HUNTER_RAPID_RECUPERATION_MANA_R1 = 56654,
|
SPELL_HUNTER_RAPID_RECUPERATION_MANA_R1 = 56654,
|
||||||
@@ -1403,48 +1401,29 @@ class spell_hun_hunting_party : public AuraScript
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// -53228 - Rapid Recuperation
|
// 56654, 58882 - Rapid Recuperation
|
||||||
class spell_hun_rapid_recuperation : public AuraScript
|
class spell_hun_rapid_recuperation : public AuraScript
|
||||||
{
|
{
|
||||||
PrepareAuraScript(spell_hun_rapid_recuperation);
|
PrepareAuraScript(spell_hun_rapid_recuperation);
|
||||||
|
|
||||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
bool Validate(SpellInfo const* spellInfo) override
|
||||||
{
|
{
|
||||||
return ValidateSpellInfo({ SPELL_HUNTER_RAPID_RECUPERATION_R1, SPELL_HUNTER_RAPID_RECUPERATION_R2 });
|
return ValidateSpellInfo({ spellInfo->Effects[EFFECT_0].TriggerSpell });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckProc(ProcEventInfo& eventInfo)
|
void HandlePeriodic(AuraEffect const* aurEff)
|
||||||
{
|
|
||||||
SpellInfo const* procSpell = eventInfo.GetSpellInfo();
|
|
||||||
if (!procSpell)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// This effect only from Rapid Killing (mana regen)
|
|
||||||
return (procSpell->SpellFamilyFlags[1] & 0x01000000) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleProc(ProcEventInfo& /*eventInfo*/)
|
|
||||||
{
|
{
|
||||||
PreventDefaultAction();
|
PreventDefaultAction();
|
||||||
uint32 triggeredSpell = 0;
|
|
||||||
switch (GetSpellInfo()->Id)
|
Unit* target = GetTarget();
|
||||||
{
|
uint32 triggerSpellId = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell;
|
||||||
case 53228: // Rank 1
|
int32 amount = CalculatePct(static_cast<int32>(target->GetMaxPower(POWER_MANA)), aurEff->GetAmount());
|
||||||
triggeredSpell = SPELL_HUNTER_RAPID_RECUPERATION_R1;
|
target->CastCustomSpell(target, triggerSpellId, &amount, nullptr, nullptr, true, nullptr, aurEff);
|
||||||
break;
|
|
||||||
case 53232: // Rank 2
|
|
||||||
triggeredSpell = SPELL_HUNTER_RAPID_RECUPERATION_R2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GetTarget()->CastSpell(GetTarget(), triggeredSpell, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Register() override
|
void Register() override
|
||||||
{
|
{
|
||||||
DoCheckProc += AuraCheckProcFn(spell_hun_rapid_recuperation::CheckProc);
|
OnEffectPeriodic += AuraEffectPeriodicFn(spell_hun_rapid_recuperation::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||||
OnProc += AuraProcFn(spell_hun_rapid_recuperation::HandleProc);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1522,7 +1501,7 @@ class spell_hun_kill_command_pet : public AuraScript
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// -53228 - Rapid Recuperation (trigger)
|
// -53228 - Rapid Recuperation (talent aura)
|
||||||
class spell_hun_rapid_recuperation_trigger : public AuraScript
|
class spell_hun_rapid_recuperation_trigger : public AuraScript
|
||||||
{
|
{
|
||||||
PrepareAuraScript(spell_hun_rapid_recuperation_trigger);
|
PrepareAuraScript(spell_hun_rapid_recuperation_trigger);
|
||||||
@@ -1559,8 +1538,8 @@ class spell_hun_rapid_recuperation_trigger : public AuraScript
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint8 rank = GetSpellInfo()->GetRank();
|
uint8 rank = GetSpellInfo()->GetRank();
|
||||||
if (rank > 0 && rank <= 2)
|
uint32 spellId = triggerSpells[rank - 1];
|
||||||
GetTarget()->CastSpell(GetTarget(), triggerSpells[rank - 1], true, nullptr, aurEff);
|
eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), spellId, true, nullptr, aurEff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Register() override
|
void Register() override
|
||||||
@@ -1676,9 +1655,9 @@ void AddSC_hunter_spell_scripts()
|
|||||||
RegisterSpellScript(spell_hun_explosive_shot);
|
RegisterSpellScript(spell_hun_explosive_shot);
|
||||||
RegisterSpellScript(spell_hun_thrill_of_the_hunt);
|
RegisterSpellScript(spell_hun_thrill_of_the_hunt);
|
||||||
RegisterSpellScript(spell_hun_hunting_party);
|
RegisterSpellScript(spell_hun_hunting_party);
|
||||||
RegisterSpellScript(spell_hun_rapid_recuperation);
|
|
||||||
RegisterSpellScript(spell_hun_glyph_of_mend_pet);
|
RegisterSpellScript(spell_hun_glyph_of_mend_pet);
|
||||||
// Proc system scripts
|
// Proc system scripts
|
||||||
|
RegisterSpellScript(spell_hun_rapid_recuperation);
|
||||||
RegisterSpellScript(spell_hun_kill_command_pet);
|
RegisterSpellScript(spell_hun_kill_command_pet);
|
||||||
RegisterSpellScript(spell_hun_piercing_shots);
|
RegisterSpellScript(spell_hun_piercing_shots);
|
||||||
RegisterSpellScript(spell_hun_rapid_recuperation_trigger);
|
RegisterSpellScript(spell_hun_rapid_recuperation_trigger);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "ProcChanceTestHelper.h"
|
#include "ProcChanceTestHelper.h"
|
||||||
#include "ProcEventInfoHelper.h"
|
#include "ProcEventInfoHelper.h"
|
||||||
|
#include "SpellInfoTestHelper.h"
|
||||||
#include "AuraStub.h"
|
#include "AuraStub.h"
|
||||||
#include "UnitStub.h"
|
#include "UnitStub.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
@@ -135,6 +136,151 @@ TEST_F(SpellProcAttributeTest, ReqSpellmod_AttributeNotSet)
|
|||||||
EXPECT_FALSE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
EXPECT_FALSE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// PROC_ATTR_REQ_SPELLMOD Auto-Generation Tests
|
||||||
|
// Validates that LoadSpellProcs auto-generates REQ_SPELLMOD for modifier
|
||||||
|
// auras with charges, preventing charge consumption by unrelated spells.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Replicates the auto-generation logic from SpellMgr::LoadSpellProcs
|
||||||
|
void ApplyAutoGeneratedSpellmodFlag(SpellInfo const* spellInfo, SpellProcEntry& procEntry)
|
||||||
|
{
|
||||||
|
if (spellInfo->ProcCharges)
|
||||||
|
{
|
||||||
|
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
|
{
|
||||||
|
if (spellInfo->Effects[i].IsAura(SPELL_AURA_ADD_FLAT_MODIFIER) ||
|
||||||
|
spellInfo->Effects[i].IsAura(SPELL_AURA_ADD_PCT_MODIFIER))
|
||||||
|
{
|
||||||
|
procEntry.AttributesMask |= PROC_ATTR_REQ_SPELLMOD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_PctModifierWithCharges)
|
||||||
|
{
|
||||||
|
// Rapid Killing (35099): ADD_PCT_MODIFIER + 1 charge
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(35099)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_ADD_PCT_MODIFIER)
|
||||||
|
.WithProcFlags(PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS)
|
||||||
|
.WithProcCharges(1)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithProcFlags(PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS)
|
||||||
|
.WithCharges(1)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_TRUE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_FlatModifierWithCharges)
|
||||||
|
{
|
||||||
|
// Clearcasting-like: ADD_FLAT_MODIFIER + charges
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(12345)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_ADD_FLAT_MODIFIER)
|
||||||
|
.WithProcCharges(2)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithCharges(2)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_TRUE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_NoCharges_NotSet)
|
||||||
|
{
|
||||||
|
// Modifier aura without charges should NOT get the flag
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(12345)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_ADD_PCT_MODIFIER)
|
||||||
|
.WithProcCharges(0)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithCharges(0)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_FALSE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_NonModifierWithCharges_NotSet)
|
||||||
|
{
|
||||||
|
// Non-modifier aura with charges should NOT get the flag
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(12345)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_PROC_TRIGGER_SPELL)
|
||||||
|
.WithProcCharges(1)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithCharges(1)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_FALSE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_ModifierOnSecondEffect)
|
||||||
|
{
|
||||||
|
// Modifier on effect index 1, not 0
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(12345)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_PROC_TRIGGER_SPELL)
|
||||||
|
.WithEffect(1, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_ADD_FLAT_MODIFIER)
|
||||||
|
.WithProcCharges(1)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithCharges(1)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_TRUE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpellProcAttributeTest, ReqSpellmod_AutoGen_PreservesExistingAttributes)
|
||||||
|
{
|
||||||
|
// Should OR with existing attributes, not replace
|
||||||
|
auto spellInfo = SpellInfoBuilder()
|
||||||
|
.WithId(12345)
|
||||||
|
.WithEffect(0, SPELL_EFFECT_APPLY_AURA, SPELL_AURA_ADD_PCT_MODIFIER)
|
||||||
|
.WithProcCharges(1)
|
||||||
|
.BuildUnique();
|
||||||
|
|
||||||
|
SpellProcEntry procEntry = SpellProcEntryBuilder()
|
||||||
|
.WithCharges(1)
|
||||||
|
.WithChance(100.0f)
|
||||||
|
.WithAttributesMask(PROC_ATTR_TRIGGERED_CAN_PROC)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
ApplyAutoGeneratedSpellmodFlag(spellInfo.get(), procEntry);
|
||||||
|
|
||||||
|
EXPECT_TRUE(procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD);
|
||||||
|
EXPECT_TRUE(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC);
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// PROC_ATTR_USE_STACKS_FOR_CHARGES (0x10) Tests
|
// PROC_ATTR_USE_STACKS_FOR_CHARGES (0x10) Tests
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user