fix(Scripts/Spells): Fix Remorseless Attacks not being consumed by Mutilate (#27425)

Co-authored-by: AlsoNotMehh <[email protected]>
This commit is contained in:
AlsoNotMehh
2026-10-03 17:00:53 -03:00
committed by GitHub
co-authored by AlsoNotMehh
parent eca127953f
commit b8a3a172bc
6 changed files with 76 additions and 23 deletions
@@ -0,0 +1,8 @@
-- Remove erroneous Relentless Strikes spell_proc override that was incorrectly assigned to Remorseless Attacks
DELETE FROM `spell_proc` WHERE `SpellId` = -14143;
-- Remorseless Attacks: Bind AuraScript to prevent charge consumption on Mutilate MH/OH,
-- so both sub-spells benefit from the critical strike bonus before being consumed in AfterCast.
DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_rog_remorseless_attacks' AND `spell_id` IN (-14143, 14143, 14149);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(-14143, 'spell_rog_remorseless_attacks');
+57 -11
View File
@@ -54,7 +54,8 @@ enum RogueSpells
SPELL_ROGUE_OVERKILL_TRIGGERED = 58427,
SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916,
SPELL_ROGUE_HONOR_AMONG_THIEVES_TRIGGERED = 51699,
SPELL_ROGUE_COLD_BLOOD = 14177
SPELL_ROGUE_COLD_BLOOD = 14177,
SPELL_ROGUE_REMORSELESS_ATTACKS_R1 = 14143
};
enum RogueSpellIcons
@@ -1110,7 +1111,10 @@ class spell_rog_cold_blood : public AuraScript
bool _usedByMutilate = false;
public:
bool WasUsedByMutilate() const { return _usedByMutilate; }
bool WasUsedByMutilate() const
{
return _usedByMutilate;
}
bool CheckProc(ProcEventInfo& eventInfo)
{
@@ -1135,6 +1139,42 @@ public:
}
};
// 14143, 14149 - Remorseless Attacks
class spell_rog_remorseless_attacks : public AuraScript
{
PrepareAuraScript(spell_rog_remorseless_attacks);
bool _usedByMutilate = false;
public:
bool WasUsedByMutilate() const
{
return _usedByMutilate;
}
bool CheckProc(ProcEventInfo& eventInfo)
{
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
if (!spellInfo)
return true;
// Block Mutilate MH (0x2) and OH (0x4) from consuming the charge immediately
if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE
&& (spellInfo->SpellFamilyFlags[1] & 0x6))
{
_usedByMutilate = true;
return false;
}
return true;
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_rog_remorseless_attacks::CheckProc);
}
};
// 1329 - Mutilate (parent spell, all ranks)
class spell_rog_mutilate : public SpellScript
{
@@ -1146,16 +1186,21 @@ class spell_rog_mutilate : public SpellScript
if (!caster)
return;
Aura* cb = caster->GetAura(SPELL_ROGUE_COLD_BLOOD);
if (!cb)
return;
if (Aura* cb = caster->GetAura(SPELL_ROGUE_COLD_BLOOD))
{
auto* script = dynamic_cast<spell_rog_cold_blood*>(
cb->GetScriptByName("spell_rog_cold_blood"));
if (script && script->WasUsedByMutilate())
cb->Remove();
}
auto* script = dynamic_cast<spell_rog_cold_blood*>(
cb->GetScriptByName("spell_rog_cold_blood"));
if (!script || !script->WasUsedByMutilate())
return;
cb->Remove();
if (Aura* remorseless = caster->GetAuraOfRankedSpell(SPELL_ROGUE_REMORSELESS_ATTACKS_R1))
{
auto* script = dynamic_cast<spell_rog_remorseless_attacks*>(
remorseless->GetScriptByName("spell_rog_remorseless_attacks"));
if (script && script->WasUsedByMutilate())
remorseless->Remove();
}
}
void Register() override
@@ -1197,4 +1242,5 @@ void AddSC_rogue_spell_scripts()
RegisterSpellScript(spell_rog_focused_attacks);
RegisterSpellScript(spell_rog_mutilate);
RegisterSpellScript(spell_rog_cold_blood);
RegisterSpellScript(spell_rog_remorseless_attacks);
}
@@ -122,7 +122,7 @@ TEST_F(SpellProcDBCValidationTest, CountEntriesWithDBCData)
// All entries should eventually have DBC data
// For now, just verify the count
EXPECT_EQ(_allEntries.size(), 869u);
EXPECT_EQ(_allEntries.size(), 868u);
}
TEST_F(SpellProcDBCValidationTest, CountEntriesAddingValue)
@@ -233,7 +233,7 @@ TEST_F(SpellProcDBCValidationTest, IdentifyDBCOverrides)
for (auto const& entry : _allEntries)
{
// SKIP REASON: Cannot compare against DBC defaults when DBC data
// is not populated. All 869 entries currently have DBC fields = 0.
// is not populated. All 868 entries currently have DBC fields = 0.
// Once populated, this loop will count actual DBC overrides.
if (!entry.HasDBCData())
continue;
@@ -17,7 +17,7 @@
/**
* @file SpellProcDataDrivenTest.cpp
* @brief Comprehensive data-driven tests for ALL 869 spell_proc entries
* @brief Comprehensive data-driven tests for ALL 868 spell_proc entries
*
* This file auto-tests every spell_proc entry from the database.
* Data is generated by: src/test/scripts/generate_spell_proc_data.py
@@ -252,12 +252,12 @@ protected:
};
// =============================================================================
// Comprehensive Tests for All 869 Entries
// Comprehensive Tests for All 868 Entries
// =============================================================================
TEST_F(SpellProcDatabaseTest, AllEntriesLoaded)
{
EXPECT_EQ(_allEntries.size(), 869u) << "Should have all 869 spell_proc entries loaded";
EXPECT_EQ(_allEntries.size(), 868u) << "Should have all 868 spell_proc entries loaded";
}
TEST_F(SpellProcDatabaseTest, AllEntriesWithProcFlags_PositiveTest)
@@ -17,7 +17,7 @@
/**
* @file SpellProcFullCoverageTest.cpp
* @brief Data-driven tests for ALL 869 spell_proc entries
* @brief Data-driven tests for ALL 868 spell_proc entries
*
* Tests proc calculations for every spell_proc entry:
* - Cooldown blocking behavior
@@ -30,7 +30,7 @@
* DESIGN NOTE: Why Tests Skip Certain Entries
* ============================================================================
*
* This test file uses parameterized tests that run against ALL 869 spell_proc
* This test file uses parameterized tests that run against ALL 868 spell_proc
* entries. Each test validates a specific feature (cooldowns, level reduction,
* attribute flags, etc.). Tests use GTEST_SKIP() for entries that don't have
* the feature being tested.
@@ -346,7 +346,7 @@ TEST_P(SpellProcFullCoverageTest, SpellId_NonZero)
}
// =============================================================================
// Test Instantiation - ALL 869 entries
// Test Instantiation - ALL 868 entries
// =============================================================================
INSTANTIATE_TEST_SUITE_P(
@@ -453,6 +453,6 @@ TEST_F(SpellProcCoverageStatsTest, CountEntriesWithReqManaCost)
TEST_F(SpellProcCoverageStatsTest, TotalEntryCount)
{
std::cout << "[ INFO ] Total spell_proc entries tested: " << _allEntries.size() << std::endl;
EXPECT_EQ(_allEntries.size(), 869u)
<< "Expected 869 entries but got " << _allEntries.size();
EXPECT_EQ(_allEntries.size(), 868u)
<< "Expected 868 entries but got " << _allEntries.size();
}
@@ -101,7 +101,7 @@ struct SpellProcTestEntry
/**
* @brief All spell_proc entries from the database
* Total: 869 entries
* Total: 868 entries
*/
inline std::vector<SpellProcTestEntry> GetAllSpellProcTestEntries()
{
@@ -271,7 +271,6 @@ inline std::vector<SpellProcTestEntry> GetAllSpellProcTestEntries()
{ -14531, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -14186, 0, 8, 1107296782, 2, 0, 0, 0, 2, 2, 2, 500, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -14156, 0, 8, 4063232, 9, 0, 0, 0, 4, 0, 0, 0, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -14143, 0, 8, 1191182854, 2097152, 0, 0, 1, 2, 0, 8, 0, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -13983, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 1000, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -13754, 0, 8, 16, 0, 0, 0, 0, 2, 0, 0, 0, 0.0f, 0.0f, 0, 0, 0, 0, 0 },
{ -13165, 0, 0, 0, 0, 0, 64, 0, 2, 0, 0, 0, 0.0f, 0.0f, 0, 0, 0, 0, 0 },