From 51b9b1e85cc9339b860de67be88e003fc47d4322 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Tue, 12 Aug 2025 21:53:56 -0400 Subject: [PATCH] Core/Spells: Start spells triggered by SPELL_EFFECT_FORCE_CAST_2 without any trigger flags Port From (https://github.com/TrinityCore/TrinityCore/commit/0fd418eb7472039bb953a7167a48f6495c2b2efb) --- Source/Game/Spells/Spell.cs | 3 +- Source/Game/Spells/SpellEffects.cs | 46 ++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 0de7278b4..247bd601f 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -3027,6 +3027,8 @@ namespace Game.Spells if (!m_spellInfo.HasAttribute(SpellAttr12.StartCooldownOnCastStart)) SendSpellCooldown(); + m_spellState = SpellState.Launched; + if (m_spellInfo.LaunchDelay == 0) { HandleLaunchPhase(); @@ -3052,7 +3054,6 @@ namespace Game.Spells // Okay, maps created, now prepare flags m_immediateHandled = false; - m_spellState = SpellState.Launched; SetDelayStart(0); unitCaster = m_caster.ToUnit(); diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 600f33da1..cad61b2d9 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -411,7 +411,6 @@ namespace Game.Spells [SpellEffectHandler(SpellEffectName.ForceCast)] [SpellEffectHandler(SpellEffectName.ForceCastWithValue)] - [SpellEffectHandler(SpellEffectName.ForceCast2)] void EffectForceCast() { if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) @@ -463,7 +462,7 @@ namespace Game.Spells return; } - CastSpellExtraArgs args = new(TriggerCastFlags.FullMask & ~(TriggerCastFlags.IgnorePowerCost | TriggerCastFlags.IgnoreReagentCost)); + CastSpellExtraArgs args = new(TriggerCastFlags.FullMask & ~(TriggerCastFlags.IgnorePowerCost | TriggerCastFlags.CastDirectly | TriggerCastFlags.IgnoreReagentCost)); // set basepoints for trigger with value effect if (effectInfo.Effect == SpellEffectName.ForceCastWithValue) for (int i = 0; i < spellInfo.GetEffects().Count; ++i) @@ -472,6 +471,49 @@ namespace Game.Spells unitTarget.CastSpell(m_caster, spellInfo.Id, args); } + [SpellEffectHandler(SpellEffectName.ForceCast2)] + void EffectForceCast2() + { + if (effectHandleMode != SpellEffectHandleMode.LaunchTarget) + return; + + if (unitTarget == null) + return; + + uint triggered_spell_id = effectInfo.TriggerSpell; + if (triggered_spell_id == 0) + { + Log.outWarn(LogFilter.Spells, $"Spell::EffectForceCast2: Spell {m_spellInfo.Id} [EffectIndex: {effectInfo.EffectIndex}] does not have triggered spell."); + return; + } + + // normal case + if (!Global.SpellMgr.HasSpellInfo(triggered_spell_id, GetCastDifficulty())) + { + Log.outError(LogFilter.Spells, $"Spell::EffectForceCast2 of spell {m_spellInfo.Id}: triggering unknown spell id {triggered_spell_id}."); + return; + } + + // Start the cast during next update tick + // This is neccessary to preserve proper SMSG_SPELL_START and SMSG_SPELL_GO packet sequence + // (if packet sequence is not preserved then client cast bar in UI bugs and doesn't stop when the spell is interrupted) + // Triggered spells from effects handled during launch phase (such as SPELL_EFFECT_TRIGGER_SPELL) + // normally have their SMSG_SPELL_GO sent before parent spell SMSG_SPELL_GO + // SPELL_EFFECT_FORCE_CAST_2 requires these packets to be sent after parent spell + // but also needs to be handled during launch phase as well + var target = new CastSpellTargetArg(m_caster); + var caster = unitTarget; + unitTarget.m_Events.AddEventAtOffset(() => + { + if (target.Targets == null) + return; + + target.Targets.Update(caster); + + caster.CastSpell(target, triggered_spell_id); + }, TimeSpan.Zero); + } + [SpellEffectHandler(SpellEffectName.TriggerSpell2)] void EffectTriggerRitualOfSummoning() {