fix(Core/Spells): revert CAST proc ordering and add ApplySpellMod recursion guard (#24990)

Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
This commit is contained in:
blinkysc
2026-03-03 16:45:06 -06:00
committed by GitHub
parent a013968436
commit cccd52dab8
8 changed files with 36 additions and 390 deletions

View File

@@ -3951,70 +3951,8 @@ void Spell::_cast(bool skipCheck)
}
else
{
// CAST phase procs for immediate spells (including channeled)
if (m_originalCaster)
{
uint32 procAttacker = m_procAttacker;
if (!procAttacker)
{
bool IsPositive = m_spellInfo->IsPositive();
if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC)
{
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
}
else
{
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG;
}
}
uint32 hitMask = PROC_HIT_NORMAL;
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
if (ihit->missCondition != SPELL_MISS_NONE)
continue;
if (!ihit->crit)
continue;
hitMask |= PROC_HIT_CRITICAL;
break;
}
Unit::ProcSkillsAndAuras(m_originalCaster, m_originalCaster, procAttacker, PROC_FLAG_NONE, hitMask, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_CAST);
}
// Immediate spell, no big deal
handle_immediate();
// Clean up deferred 0-charge spell modifier auras
// Copy to vector first — aura->Remove() can modify m_appliedMods
std::vector<Aura*> appliedModsCopy(m_appliedMods.begin(), m_appliedMods.end());
for (Aura* aura : appliedModsCopy)
{
if (!aura->IsRemoved() && aura->IsUsingCharges()
&& !aura->GetCharges())
aura->Remove();
}
// Also clean up deferred modifier auras not in m_appliedMods
if (Unit* caster = m_caster)
{
std::vector<Aura*> deferred;
for (auto const& [id, aura] : caster->GetOwnedAuras())
{
if (!aura->IsRemoved() && aura->IsUsingCharges()
&& !aura->GetCharges()
&& (aura->HasEffectType(SPELL_AURA_ADD_FLAT_MODIFIER)
|| aura->HasEffectType(SPELL_AURA_ADD_PCT_MODIFIER)))
deferred.push_back(aura);
}
for (Aura* aura : deferred)
if (!aura->IsRemoved())
aura->Remove();
}
}
if (resetAttackTimers)
@@ -4042,9 +3980,13 @@ void Spell::_cast(bool skipCheck)
if (modOwner)
modOwner->SetSpellModTakingSpell(this, false);
// CAST phase procs for delayed spells
if (m_spellState == SPELL_STATE_DELAYED
&& m_originalCaster)
// Handle procs on cast - only for non-triggered spells
// Triggered spells (from auras, items, etc.) should not fire CAST phase procs
// as they are not player-initiated casts. This prevents issues like Arcane Potency
// charges being consumed by periodic damage effects (e.g., Blizzard ticks).
// Must be called AFTER handle_immediate() so spell mods (like Missile Barrage's
// duration reduction) are applied before the aura is consumed by the proc.
if (m_originalCaster && !IsTriggered())
{
uint32 procAttacker = m_procAttacker;
if (!procAttacker)