From 16aa620ca06b81c31334239801a942e919dc5fd3 Mon Sep 17 00:00:00 2001 From: Shauren Date: Mon, 28 Jul 2025 22:19:28 +0200 Subject: [PATCH] Core/Spells: Don't ignore power costs and reagents for spells triggered by SPELL_EFFECT_TRIGGER_SPELL, SPELL_EFFECT_TRIGGER_MISSILE, SPELL_EFFECT_FORCE_CAST, SPELL_AURA_PERIODIC_TRIGGER_SPELL, SPELL_AURA_PROC_TRIGGER_SPELL and fixed implementation of SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES --- .../game/Spells/Auras/SpellAuraEffects.cpp | 15 +++- src/server/game/Spells/Spell.cpp | 11 ++- src/server/game/Spells/SpellDefines.h | 77 +++++++++++-------- src/server/game/Spells/SpellEffects.cpp | 10 ++- src/server/shared/SharedDefines.h | 2 +- 5 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 0d5d0eded0..448c646647 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5673,7 +5673,10 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) { if (Unit* triggerCaster = triggeredSpellInfo->NeedsToBeTriggeredByCaster(m_spellInfo) ? caster : target) { - triggerCaster->CastSpell(target, triggeredSpellInfo->Id, this); + triggerCaster->CastSpell(target, triggerSpellId, CastSpellExtraArgsInit { + .TriggerFlags = TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST), + .TriggeringAura = this + }); TC_LOG_DEBUG("spells", "AuraEffect::HandlePeriodicTriggerSpellAuraTick: Spell %u Trigger %u", GetId(), triggeredSpellInfo->Id); } } @@ -5689,6 +5692,7 @@ void AuraEffect::HandlePeriodicTriggerSpellWithValueAuraTick(Unit* target, Unit* if (Unit* triggerCaster = triggeredSpellInfo->NeedsToBeTriggeredByCaster(m_spellInfo) ? caster : target) { CastSpellExtraArgs args(this); + args.SetTriggerFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST)); for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), GetAmount()); triggerCaster->CastSpell(target, triggerSpellId, args); @@ -6259,7 +6263,9 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId)) { TC_LOG_DEBUG("spells", "AuraEffect::HandleProcTriggerSpellAuraProc: Triggering spell %u from aura %u proc", triggeredSpellInfo->Id, GetId()); - triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo->Id, this); + triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo->Id, CastSpellExtraArgs(this) + .SetTriggeringSpell(eventInfo.GetProcSpell()) + .SetTriggerFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST))); } else if (triggerSpellId && GetAuraType() != SPELL_AURA_DUMMY) TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); @@ -6277,7 +6283,9 @@ void AuraEffect::HandleProcTriggerSpellCopyAuraProc(AuraApplication* aurApp, Pro return; TC_LOG_DEBUG("spells", "AuraEffect::HandleProcTriggerSpellAuraProc: Triggering spell %u from aura %u proc", triggeredSpellInfo->Id, GetId()); - triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo->Id, this); + triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo->Id, CastSpellExtraArgs(this) + .SetTriggeringSpell(eventInfo.GetProcSpell()) + .SetTriggerFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST))); } void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) @@ -6289,6 +6297,7 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId)) { CastSpellExtraArgs args(this); + args.SetTriggerFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST)); args.AddSpellMod(SPELLVALUE_BASE_POINT0, GetAmount()); triggerCaster->CastSpell(triggerTarget, triggerSpellId, args); TC_LOG_DEBUG("spells", "AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Triggering spell %u with value %d from aura %u proc", triggeredSpellInfo->Id, GetAmount(), GetId()); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index e1cb97fcc0..a4e15db3f1 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5263,7 +5263,7 @@ void Spell::TakePower() if (!unitCaster) return; - if (m_CastItem || m_triggeredByAuraSpell) + if (m_CastItem || m_spellInfo->HasAttribute(SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES)) return; //Don't take power if the spell is cast while .cheat power is enabled. @@ -5483,7 +5483,7 @@ void Spell::TakeReagents() ItemTemplate const* castItemTemplate = m_CastItem ? m_CastItem->GetTemplate() : nullptr; // do not take reagents for these item casts - if (castItemTemplate && castItemTemplate->GetFlags() & ITEM_FLAG_NO_REAGENT_COST) + if ((castItemTemplate && castItemTemplate->GetFlags() & ITEM_FLAG_NO_REAGENT_COST) || m_spellInfo->HasAttribute(SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES)) return; Player* p_caster = m_caster->ToPlayer(); @@ -8947,3 +8947,10 @@ bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) } } //namespace Trinity + +CastSpellExtraArgs::CastSpellExtraArgs() = default; +CastSpellExtraArgs::CastSpellExtraArgs(CastSpellExtraArgs const& other) = default; +CastSpellExtraArgs::CastSpellExtraArgs(CastSpellExtraArgs&& other) noexcept = default; +CastSpellExtraArgs& CastSpellExtraArgs::operator=(CastSpellExtraArgs const& other) = default; +CastSpellExtraArgs& CastSpellExtraArgs::operator=(CastSpellExtraArgs&& other) noexcept = default; +CastSpellExtraArgs::~CastSpellExtraArgs() = default; diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 62f66ae695..9a377c0a8b 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -192,16 +192,51 @@ enum TriggerCastFlags : uint32 DEFINE_ENUM_FLAG(TriggerCastFlags); -struct TC_GAME_API CastSpellExtraArgs +struct CastSpellExtraArgsInit { - CastSpellExtraArgs() {} - CastSpellExtraArgs(bool triggered) : TriggerFlags(triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE) {} - CastSpellExtraArgs(TriggerCastFlags trigger) : TriggerFlags(trigger) {} - CastSpellExtraArgs(Item* item) : TriggerFlags(TRIGGERED_FULL_MASK), CastItem(item) {} - CastSpellExtraArgs(AuraEffect const* eff) : TriggerFlags(TRIGGERED_FULL_MASK), TriggeringAura(eff) {} - CastSpellExtraArgs(ObjectGuid const& origCaster) : TriggerFlags(TRIGGERED_FULL_MASK), OriginalCaster(origCaster) {} - CastSpellExtraArgs(AuraEffect const* eff, ObjectGuid const& origCaster) : TriggerFlags(TRIGGERED_FULL_MASK), TriggeringAura(eff), OriginalCaster(origCaster) {} + TriggerCastFlags TriggerFlags = TRIGGERED_NONE; + Item* CastItem = nullptr; + Spell const* TriggeringSpell = nullptr; + AuraEffect const* TriggeringAura = nullptr; + ObjectGuid OriginalCaster = ObjectGuid::Empty; + + struct + { + friend struct CastSpellExtraArgs; + friend class WorldObject; + + private: + void AddMod(SpellValueMod mod, int32 val) { data.push_back({ mod, val }); } + + auto begin() const { return data.cbegin(); } + auto end() const { return data.cend(); } + + std::vector> data; + } SpellValueOverrides; + + std::any CustomArg; +}; + +struct TC_GAME_API CastSpellExtraArgs : public CastSpellExtraArgsInit +{ + CastSpellExtraArgs(); + CastSpellExtraArgs(bool triggered) { TriggerFlags = triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE; } + CastSpellExtraArgs(TriggerCastFlags trigger) { TriggerFlags = trigger; } + CastSpellExtraArgs(Item* item) { TriggerFlags = TRIGGERED_FULL_MASK; CastItem = item; } + CastSpellExtraArgs(Spell const* triggeringSpell) { TriggerFlags = TRIGGERED_FULL_MASK; SetTriggeringSpell(triggeringSpell); } + CastSpellExtraArgs(AuraEffect const* eff) { TriggerFlags = TRIGGERED_FULL_MASK; SetTriggeringAura(eff); } + CastSpellExtraArgs(ObjectGuid const& origCaster) { TriggerFlags = TRIGGERED_FULL_MASK; SetOriginalCaster(origCaster); } + CastSpellExtraArgs(AuraEffect const* eff, ObjectGuid const& origCaster) { TriggerFlags = TRIGGERED_FULL_MASK; SetTriggeringAura(eff); SetOriginalCaster(origCaster); } CastSpellExtraArgs(SpellValueMod mod, int32 val) { SpellValueOverrides.AddMod(mod, val); } + CastSpellExtraArgs(CastSpellExtraArgsInit&& init) : CastSpellExtraArgsInit(std::move(init)) { SetTriggeringSpell(TriggeringSpell); } + + CastSpellExtraArgs(CastSpellExtraArgs const& other); + CastSpellExtraArgs(CastSpellExtraArgs&& other) noexcept; + + CastSpellExtraArgs& operator=(CastSpellExtraArgs const& other); + CastSpellExtraArgs& operator=(CastSpellExtraArgs&& other) noexcept; + + ~CastSpellExtraArgs(); CastSpellExtraArgs& SetTriggerFlags(TriggerCastFlags flag) { TriggerFlags = flag; return *this; } CastSpellExtraArgs& SetCastItem(Item* item) { CastItem = item; return *this; } @@ -211,32 +246,6 @@ struct TC_GAME_API CastSpellExtraArgs CastSpellExtraArgs& AddSpellMod(SpellValueMod mod, int32 val) { SpellValueOverrides.AddMod(mod, val); return *this; } CastSpellExtraArgs& AddSpellBP0(int32 val) { return AddSpellMod(SPELLVALUE_BASE_POINT0, val); } // because i don't want to type SPELLVALUE_BASE_POINT0 300 times CastSpellExtraArgs& SetCustomArg(std::any customArg) { CustomArg = std::move(customArg); return *this; } - - TriggerCastFlags TriggerFlags = TRIGGERED_NONE; - Item* CastItem = nullptr; - Spell const* TriggeringSpell = nullptr; - AuraEffect const* TriggeringAura = nullptr; - ObjectGuid OriginalCaster = ObjectGuid::Empty; - struct - { - friend struct CastSpellExtraArgs; - friend class WorldObject; - - private: - void AddMod(SpellValueMod mod, int32 val) { data.push_back({ mod, val }); } - - auto begin() const { return data.cbegin(); } - auto end() const { return data.cend(); } - - std::vector> data; - } SpellValueOverrides; - std::any CustomArg; - - CastSpellExtraArgs(CastSpellExtraArgs const&) = delete; - CastSpellExtraArgs(CastSpellExtraArgs&&) = delete; - - CastSpellExtraArgs& operator=(CastSpellExtraArgs const&) = delete; - CastSpellExtraArgs& operator=(CastSpellExtraArgs&&) = delete; }; enum class SummonPropertiesParamType : uint8 diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index aab87cab9d..a54c9d76a1 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -571,7 +571,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) } // original caster guid only for GO cast - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); + CastSpellExtraArgs args(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST)); args.SetOriginalCaster(m_originalCasterGUID); if (!m_castItemGUID.IsEmpty() && sSpellMgr->AssertSpellInfo(m_spellInfo->Effects[effIndex].TriggerSpell)->HasAttribute(SPELL_ATTR2_RETAIN_ITEM_CAST)) @@ -624,7 +624,11 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) targets.SetGOTarget(go); } - CastSpellExtraArgs args(m_originalCasterGUID); + CastSpellExtraArgs args(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST)); + args.SetOriginalCaster(m_originalCasterGUID); + args.SetTriggeringSpell(this); + args.SetCustomArg(m_customArg); + // set basepoints for trigger with value effect if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_TRIGGER_MISSILE_SPELL_WITH_VALUE) for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -679,7 +683,7 @@ void Spell::EffectForceCast(SpellEffIndex effIndex) return; } - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); + CastSpellExtraArgs args(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_REAGENT_COST)); if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_FORCE_CAST_WITH_VALUE) for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage); diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 8ad096d5ff..4eb157007a 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -584,7 +584,7 @@ enum SpellAttr6 : uint32 SPELL_ATTR6_NOT_AN_ATTACK = 0x00000004, /*NYI*/ // TITLE Not an Attack SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC = 0x00000008, // TITLE Can Assist Immune PC SPELL_ATTR6_IGNORE_FOR_MOD_TIME_RATE = 0x00000010, /*NYI, time rate not implemented*/ // TITLE Ignore For Mod Time Rate - SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES = 0x00000020, // TITLE Do Not Consume Resources + SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES = 0x00000020, // TITLE Do Not Consume Resources DESCRIPTION Requires power/reagents to cast but doesn't consume them SPELL_ATTR6_FLOATING_COMBAT_TEXT_ON_CAST = 0x00000040, // TITLE Floating Combat Text On Cast (client only) SPELL_ATTR6_AURA_IS_WEAPON_PROC = 0x00000080, // TITLE Aura Is Weapon Proc SPELL_ATTR6_DO_NOT_CHAIN_TO_CROWD_CONTROLLED_TARGETS = 0x00000100, // TITLE Do Not Chain To Crowd-Controlled Targets DESCRIPTION Implicit targeting (chaining and area targeting) will not impact crowd controlled targets