Skip to content

Commit

Permalink
Core/Auras: Implemented OnHeartbeat AuraScript hook and refactored an…
Browse files Browse the repository at this point in the history
… aurascript to use it as example (#29945)

* Updated Amalgam's Seventh Spine dummy aura script to use the new AuraScript hook
  • Loading branch information
Ovahlord committed Apr 27, 2024
1 parent 253f306 commit 55ce5b1
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions src/server/game/Spells/Auras/SpellAuras.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,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;
Expand Down Expand Up @@ -2620,6 +2632,9 @@ void UnitAura::Heartbeat()

// Periodic food and drink emote animation
HandlePeriodicFoodSpellVisualKit();

// Invoke the OnHeartbeat AuraScript hook
CallScriptOnHeartbeat();
}

void UnitAura::HandlePeriodicFoodSpellVisualKit()
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Spells/Auras/SpellAuras.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
59 changes: 59 additions & 0 deletions src/server/game/Spells/SpellScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1174,6 +1175,58 @@ class TC_GAME_API AuraScript : public SpellScriptBase
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:
Expand Down Expand Up @@ -2017,6 +2070,12 @@ class TC_GAME_API AuraScript : public SpellScriptBase
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);
Expand Down
17 changes: 4 additions & 13 deletions src/server/scripts/Spells/spell_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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())
Expand Down Expand Up @@ -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);
}
};

Expand Down

0 comments on commit 55ce5b1

Please sign in to comment.