diff options
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 15 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 1 | ||||
-rw-r--r-- | src/server/game/Spells/SpellScript.h | 59 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_item.cpp | 17 |
5 files changed, 80 insertions, 14 deletions
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index db5604c3d44..36a8ae32026 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -502,7 +502,7 @@ void Unit::Heartbeat() // SMSG_FLIGHT_SPLINE_SYNC for cyclic splines SendFlightSplineSyncUpdate(); - // Trigger heartbeat procs and generic aura behavior such as food emotes + // Trigger heartbeat procs and generic aura behavior such as food emotes and invoking aura script hooks TriggerAuraHeartbeat(); // Update Vignette position and visibility diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index da09f519130..19bb668699b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2073,6 +2073,18 @@ void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo) } } +void Aura::CallScriptOnHeartbeat() +{ + for (AuraScript* script : m_loadedScripts) + { + script->_PrepareScriptCall(AURA_SCRIPT_HOOK_ON_HEARTBEAT); + for (AuraScript::AuraHeartbeatHandler const& onHeartbeat : script->OnHeartbeat) + onHeartbeat.Call(script); + + script->_FinishScriptCall(); + } +} + bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { bool preventDefault = false; @@ -2627,6 +2639,9 @@ void UnitAura::Heartbeat() // Periodic food and drink emote animation HandlePeriodicFoodSpellVisualKit(); + + // Invoke the OnHeartbeat AuraScript hook + CallScriptOnHeartbeat(); } void UnitAura::HandlePeriodicFoodSpellVisualKit() diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 08621eed649..2852c959a5b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -264,6 +264,7 @@ class TC_GAME_API Aura bool CallScriptCheckAreaTargetHandlers(Unit* target); void CallScriptDispel(DispelInfo* dispelInfo); void CallScriptAfterDispel(DispelInfo* dispelInfo); + void CallScriptOnHeartbeat(); bool CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); bool CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); void CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index abe0608b45a..9c28dd10d7d 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -1048,6 +1048,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_CHECK_AREA_TARGET, AURA_SCRIPT_HOOK_DISPEL, AURA_SCRIPT_HOOK_AFTER_DISPEL, + AURA_SCRIPT_HOOK_ON_HEARTBEAT, AURA_SCRIPT_HOOK_ENTER_LEAVE_COMBAT, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, @@ -1174,6 +1175,58 @@ public: SafeWrapperType _safeWrapper; }; + class AuraHeartbeatHandler final + { + public: + union AuraHeartbeatFnType + { + void(AuraScript::* Member)(); + void(*Static)(); + }; + + using SafeWrapperType = void(*)(AuraScript* auraScript, AuraHeartbeatFnType callImpl); + + template<typename ScriptFunc> + explicit AuraHeartbeatHandler(ScriptFunc handler) + { + using ScriptClass = GetScriptClass_t<ScriptFunc>; + + static_assert(sizeof(AuraHeartbeatFnType) >= sizeof(ScriptFunc)); + static_assert(alignof(AuraHeartbeatFnType) >= alignof(ScriptFunc)); + + if constexpr (!std::is_void_v<ScriptClass>) + { + static_assert(std::is_invocable_r_v<void, ScriptFunc, ScriptClass>, + "AuraHeartbeat signature must be \"void HandleHeartbeat()\""); + + _callImpl = { .Member = reinterpret_cast<decltype(AuraHeartbeatFnType::Member)>(handler) }; + _safeWrapper = [](AuraScript* auraScript, AuraHeartbeatFnType callImpl) -> void + { + return (static_cast<ScriptClass*>(auraScript)->*reinterpret_cast<ScriptFunc>(callImpl.Member))(); + }; + } + else + { + static_assert(std::is_invocable_r_v<void, ScriptFunc>, + "AuraHeartbeatHandler signature must be \"static void HandleHeartbeat()\""); + + _callImpl = { .Static = reinterpret_cast<decltype(AuraHeartbeatFnType::Static)>(handler) }; + _safeWrapper = [](AuraScript* /*auraScript*/, AuraHeartbeatFnType callImpl) -> void + { + return reinterpret_cast<ScriptFunc>(callImpl.Static)(); + }; + } + } + + void Call(AuraScript* auraScript) const + { + return _safeWrapper(auraScript, _callImpl); + } + private: + AuraHeartbeatFnType _callImpl; + SafeWrapperType _safeWrapper; + }; + class TC_GAME_API EffectBase : public EffectHook { public: @@ -2017,6 +2070,12 @@ public: HookList<AuraDispelHandler> AfterDispel; #define AuraDispelFn(F) AuraDispelHandler(&F) + // executed on every heartbeat of a unit + // example: OnHeartbeat += AuraHeartbeatFn(class::function); + // where function is: void function (); + HookList<AuraHeartbeatHandler> OnHeartbeat; + #define AuraHeartbeatFn(F) AuraHeartbeatHandler(&F) + // executed when aura effect is applied with specified mode to target // should be used when when effect handler preventing/replacing is needed, do not use this hook for triggering spellcasts/removing auras etc - may be unsafe // example: OnEffectApply += AuraEffectApplyFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index f0c1e460815..1bc95391071 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -4326,17 +4326,9 @@ class spell_item_amalgams_seventh_spine : public AuraScript }); } - void ForcePeriodic(AuraEffect const* /*aurEff*/, bool& isPeriodic, int32& amplitude) + void UpdateSpecAura() { - // simulate heartbeat timer - isPeriodic = true; - amplitude = 5000; - } - - void UpdateSpecAura(AuraEffect const* aurEff) - { - PreventDefaultAction(); - Player* target = GetTarget()->ToPlayer(); + Player* target = GetUnitOwner()->ToPlayer(); if (!target) return; @@ -4345,7 +4337,7 @@ class spell_item_amalgams_seventh_spine : public AuraScript if (target->GetPrimarySpecialization() != spec) target->RemoveAurasDueToSpell(aura); else if (!target->HasAura(aura)) - target->CastSpell(target, aura, aurEff); + target->CastSpell(target, aura, GetEffect(EFFECT_0)); }; switch (target->GetClass()) @@ -4373,8 +4365,7 @@ class spell_item_amalgams_seventh_spine : public AuraScript void Register() override { - DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_item_amalgams_seventh_spine::ForcePeriodic, EFFECT_0, SPELL_AURA_DUMMY); - OnEffectPeriodic += AuraEffectPeriodicFn(spell_item_amalgams_seventh_spine::UpdateSpecAura, EFFECT_0, SPELL_AURA_DUMMY); + OnHeartbeat += AuraHeartbeatFn(spell_item_amalgams_seventh_spine::UpdateSpecAura); } }; |