/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#ifndef TRINITY_SPELL_SCRIPT_H
#define TRINITY_SPELL_SCRIPT_H
#include "ObjectGuid.h"
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "Util.h"
#include
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
#include
#endif
class Aura;
class AuraApplication;
class AuraEffect;
class Creature;
class Corpse;
class DamageInfo;
class DispelInfo;
class DynamicObject;
class GameObject;
class HealInfo;
class Item;
class ModuleReference;
class Player;
class ProcEventInfo;
class Spell;
class SpellEffectInfo;
class SpellInfo;
class SpellScript;
class Unit;
class WorldLocation;
class WorldObject;
struct SpellDestination;
struct SpellModifier;
struct SpellValue;
enum Difficulty : uint8;
enum class ItemContext : uint8;
#define SPELL_EFFECT_ANY ((uint16)-1)
#define SPELL_AURA_ANY ((uint16)-1)
enum SpellScriptState
{
SPELL_SCRIPT_STATE_NONE = 0,
SPELL_SCRIPT_STATE_REGISTRATION,
SPELL_SCRIPT_STATE_LOADING,
SPELL_SCRIPT_STATE_UNLOADING
};
#define SPELL_SCRIPT_STATE_END (SPELL_SCRIPT_STATE_UNLOADING + 1)
// helper class from which SpellScript and AuraScript derive, use these classes instead
class TC_GAME_API SpellScriptBase
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
public:
SpellScriptBase() noexcept;
virtual ~SpellScriptBase();
SpellScriptBase(SpellScriptBase const& right) = delete;
SpellScriptBase(SpellScriptBase&& right) = delete;
SpellScriptBase& operator=(SpellScriptBase const& right) = delete;
SpellScriptBase& operator=(SpellScriptBase&& right) = delete;
void _Register();
void _Unload();
void _Init(std::string const& scriptname, uint32 spellId);
std::string_view GetScriptName() const;
protected:
virtual bool _Validate(SpellInfo const* entry);
// compile barrier to avoid instantiating operator+= in every script file
template
class HookList : public ::HookList
{
public:
HookList& operator+=(T&& t);
};
class TC_GAME_API EffectHook
{
public:
explicit EffectHook(uint8 effIndex);
EffectHook(EffectHook const& right) = delete;
EffectHook(EffectHook&& right) noexcept;
EffectHook& operator=(EffectHook const& right) = delete;
EffectHook& operator=(EffectHook&& right) noexcept;
virtual ~EffectHook();
uint32 GetAffectedEffectsMask(SpellInfo const* spellInfo) const;
bool IsEffectAffected(SpellInfo const* spellInfo, uint8 effIndex) const;
virtual bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex) const = 0;
std::string EffIndexToString() const;
protected:
uint8 _effIndex;
};
template
struct GetScriptClass
{
using type = void;
};
template
struct GetScriptClass
{
using type = Class;
};
template
struct GetScriptClass
{
using type = Class;
};
template
struct GetScriptClass
{
using type = std::remove_const_t;
};
template
using GetScriptClass_t = typename GetScriptClass::type;
template
struct ScriptFuncInvoker
{
union SizeAndAlignment
{
Ret(BaseClass::* Member)(Args...);
Ret(* Static)(BaseClass&, Args...);
};
static constexpr std::size_t Size = sizeof(SizeAndAlignment);
static constexpr std::size_t Alignment = alignof(SizeAndAlignment);
struct alignas(Alignment) StorageType : std::array { };
template
struct Impl
{
using ScriptClass = GetScriptClass_t;
static Ret Invoke(BaseClass& script, Args... args, StorageType callImpl)
{
return std::invoke(reinterpret_cast(callImpl.data())->Func, static_cast(script), args...);
}
ScriptFunc Func;
};
StorageType ImplStorage;
Ret(* Thunk)(BaseClass&, Args..., StorageType);
};
uint8 m_currentScriptState;
std::string_view m_scriptName;
uint32 m_scriptSpellId;
private:
#ifdef TRINITY_API_USE_DYNAMIC_LINKING
// Strong reference to keep the binary code loaded
std::shared_ptr m_moduleReference;
#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
public:
//
// SpellScript/AuraScript interface base
// these functions are safe to override, see notes below for usage instructions
//
// Function in which handler functions are registered, must be implemented in script
virtual void Register() = 0;
// Function called on server startup, if returns false script won't be used in core
// use for: dbc/template data presence/correctness checks
virtual bool Validate([[maybe_unused]] SpellInfo const* spellInfo) { return true; }
// Function called when script is created, if returns false script will be unloaded afterwards
// use for: initializing local script variables (DO NOT USE CONSTRUCTOR FOR THIS PURPOSE!)
virtual bool Load() { return true; }
// Function called when script is destroyed
// use for: deallocating memory allocated by script
virtual void Unload() { }
// Helpers
static bool ValidateSpellInfo(std::initializer_list spellIds)
{
return ValidateSpellInfoImpl(spellIds.begin(), spellIds.end());
}
template
static bool ValidateSpellInfo(T const& spellIds)
{
return ValidateSpellInfoImpl(std::cbegin(spellIds), std::cend(spellIds));
}
static bool ValidateSpellEffect(std::initializer_list> effects)
{
return ValidateSpellEffectsImpl(effects.begin(), effects.end());
}
template
static bool ValidateSpellEffect(T const& spellEffects)
{
return ValidateSpellEffectsImpl(std::cbegin(spellEffects), std::cend(spellEffects));
}
private:
template
static bool ValidateSpellInfoImpl(Iterator begin, Iterator end)
{
bool allValid = true;
while (begin != end)
{
if (!ValidateSpellInfoImpl(*begin))
allValid = false;
++begin;
}
return allValid;
}
template
static bool ValidateSpellEffectsImpl(Iterator begin, Iterator end)
{
bool allValid = true;
while (begin != end)
{
if (!ValidateSpellEffectImpl(begin->first, begin->second))
allValid = false;
++begin;
}
return allValid;
}
static bool ValidateSpellInfoImpl(uint32 spellId);
static bool ValidateSpellEffectImpl(uint32 spellId, SpellEffIndex effectIndex);
};
// SpellScript interface - enum used for runtime checks of script function calls
enum SpellScriptHookType
{
SPELL_SCRIPT_HOOK_EFFECT_LAUNCH = SPELL_SCRIPT_STATE_END,
SPELL_SCRIPT_HOOK_EFFECT_LAUNCH_TARGET,
SPELL_SCRIPT_HOOK_EFFECT_HIT,
SPELL_SCRIPT_HOOK_EFFECT_HIT_TARGET,
SPELL_SCRIPT_HOOK_EFFECT_SUCCESSFUL_DISPEL,
SPELL_SCRIPT_HOOK_BEFORE_HIT,
SPELL_SCRIPT_HOOK_HIT,
SPELL_SCRIPT_HOOK_AFTER_HIT,
SPELL_SCRIPT_HOOK_OBJECT_AREA_TARGET_SELECT,
SPELL_SCRIPT_HOOK_OBJECT_TARGET_SELECT,
SPELL_SCRIPT_HOOK_DESTINATION_TARGET_SELECT,
SPELL_SCRIPT_HOOK_CHECK_CAST,
SPELL_SCRIPT_HOOK_BEFORE_CAST,
SPELL_SCRIPT_HOOK_ON_CAST,
SPELL_SCRIPT_HOOK_ON_RESIST_ABSORB_CALCULATION,
SPELL_SCRIPT_HOOK_AFTER_CAST,
SPELL_SCRIPT_HOOK_CALC_CRIT_CHANCE,
SPELL_SCRIPT_HOOK_CALC_DAMAGE,
SPELL_SCRIPT_HOOK_CALC_HEALING,
SPELL_SCRIPT_HOOK_ON_PRECAST,
SPELL_SCRIPT_HOOK_CALC_CAST_TIME,
SPELL_SCRIPT_HOOK_EMPOWER_STAGE_COMPLETED,
SPELL_SCRIPT_HOOK_EMPOWER_COMPLETED,
};
#define HOOK_SPELL_HIT_START SPELL_SCRIPT_HOOK_EFFECT_HIT
#define HOOK_SPELL_HIT_END SPELL_SCRIPT_HOOK_AFTER_HIT + 1
class TC_GAME_API SpellScript : public SpellScriptBase
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
template
using ScriptFuncInvoker = SpellScriptBase::ScriptFuncInvoker;
public:
class CastHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit CastHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
static_assert(std::is_invocable_r_v,
R""(CastHandler signature must be "void HandleCast()")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript) const
{
return _invoker.Thunk(*spellScript, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class CheckCastHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit CheckCastHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(CheckCastHandler signature must be "SpellCastResult CheckCast()")"");
else
static_assert(std::is_invocable_r_v,
R""(CheckCastHandler signature must be "static SpellCastResult CheckCast(your_script_class& script, SpellScript&)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
SpellCastResult Call(SpellScript* spellScript) const
{
return _invoker.Thunk(*spellScript, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class TC_GAME_API EffectBase : public EffectHook
{
public:
EffectBase(uint8 effIndex, uint16 effName);
EffectBase(EffectBase const& right) = delete;
EffectBase(EffectBase&& right) noexcept;
EffectBase& operator=(EffectBase const& right) = delete;
EffectBase& operator=(EffectBase&& right) noexcept;
~EffectBase();
std::string ToString() const;
bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex) const override;
private:
uint16 _effName;
};
class EffectHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit EffectHandler(ScriptFunc handler, uint8 effIndex, uint16 effName)
: EffectBase(effIndex, effName)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
static_assert(std::is_invocable_r_v,
R""(EffectHandler signature must be "void HandleEffect(SpellEffIndex effIndex)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, SpellEffIndex effIndex) const
{
return _invoker.Thunk(*spellScript, effIndex, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class BeforeHitHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit BeforeHitHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
static_assert(std::is_invocable_r_v,
R""(BeforeHitHandler signature must be "void HandleBeforeHit(SpellMissInfo missInfo)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, SpellMissInfo missInfo) const
{
return _invoker.Thunk(*spellScript, missInfo, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class HitHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit HitHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
static_assert(std::is_invocable_r_v,
R""(HitHandler signature must be "void HandleHit()")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript) const
{
return _invoker.Thunk(*spellScript, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class OnCalcCritChanceHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit OnCalcCritChanceHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(OnCalcCritChanceHandler signature must be "void CalcCritChance(Unit const* victim, float& critChance)")"");
else
static_assert(std::is_invocable_r_v,
R""(OnCalcCritChanceHandler signature must be "static void CalcCritChance(your_script_class& script, Unit const* victim, float& critChance)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, Unit const* victim, float& critChance) const
{
return _invoker.Thunk(*spellScript, victim, critChance, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class TC_GAME_API TargetHook : public EffectHook
{
public:
TargetHook(uint8 effectIndex, uint16 targetType, bool area, bool dest);
TargetHook(TargetHook const& right) = delete;
TargetHook(TargetHook&& right) noexcept;
TargetHook& operator=(TargetHook const& right) = delete;
TargetHook& operator=(TargetHook&& right) noexcept;
virtual ~TargetHook();
bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex) const override;
std::string ToString() const;
uint16 GetTarget() const { return _targetType; }
protected:
uint16 _targetType;
bool _area;
bool _dest;
};
class ObjectAreaTargetSelectHandler final : public TargetHook
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker&>;
template
explicit ObjectAreaTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
: TargetHook(effIndex, targetType, true, false)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v&>,
R""(ObjectAreaTargetSelectHandler signature must be "void SetTargets(std::list& targets)")"");
else
static_assert(std::is_invocable_r_v&>,
R""(ObjectAreaTargetSelectHandler signature must be "static void SetTargets(your_script_class& script, std::list& targets)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, std::list& targets) const
{
return _invoker.Thunk(*spellScript, targets, _invoker.ImplStorage);
}
bool HasSameTargetFunctionAs(ObjectAreaTargetSelectHandler const& other) const
{
return _invoker.ImplStorage == other._invoker.ImplStorage;
}
private:
ScriptFuncInvoker _invoker;
};
class ObjectTargetSelectHandler final : public TargetHook
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit ObjectTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
: TargetHook(effIndex, targetType, false, false)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(ObjectTargetSelectHandler signature must be "void SetTarget(WorldObject*& target)")"");
else
static_assert(std::is_invocable_r_v,
R""(ObjectTargetSelectHandler signature must be "static void SetTarget(your_script_class& script, WorldObject*& target)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, WorldObject*& target) const
{
return _invoker.Thunk(*spellScript, target, _invoker.ImplStorage);
}
bool HasSameTargetFunctionAs(ObjectTargetSelectHandler const& other) const
{
return _invoker.ImplStorage == other._invoker.ImplStorage;
}
private:
ScriptFuncInvoker _invoker;
};
class DestinationTargetSelectHandler final : public TargetHook
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit DestinationTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
: TargetHook(effIndex, targetType, false, true)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(DestinationTargetSelectHandler signature must be "void SetTarget(SpellDestination& target)")"");
else
static_assert(std::is_invocable_r_v,
R""(DestinationTargetSelectHandler signature must be "static void SetTarget(your_script_class& script, SpellDestination& target)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, SpellDestination& target) const
{
return _invoker.Thunk(*spellScript, target, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class DamageAndHealingCalcHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit DamageAndHealingCalcHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(DamageAndHealingCalcHandler signature must be "void CalcDamage(SpellEffectInfo const& spellEffectInfo, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)")"");
else
static_assert(std::is_invocable_r_v,
R""(DamageAndHealingCalcHandler signature must be "static void CalcDamage(your_script_class& script, SpellEffectInfo const& spellEffectInfo, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, SpellEffectInfo const& spellEffectInfo, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod) const
{
return _invoker.Thunk(*spellScript, spellEffectInfo, victim, damageOrHealing, flatMod, pctMod, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class OnCalculateResistAbsorbHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit OnCalculateResistAbsorbHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(OnCalculateResistAbsorbHandler signature must be "void CalcAbsorbResist(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount)")"");
else
static_assert(std::is_invocable_r_v,
R""(OnCalculateResistAbsorbHandler signature must be "static void CalcAbsorbResist(your_script_class& script, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount) const
{
return _invoker.Thunk(*spellScript, damageInfo, resistAmount, absorbAmount, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EmpowerStageCompletedHandler final
{
public:
using ScriptFuncInvoker = SpellScript::ScriptFuncInvoker;
template
explicit EmpowerStageCompletedHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
static_assert(std::is_invocable_r_v,
R""(EmpowerStageCompleted/EmpowerCompleted signature must be "void HandleEmpowerStageCompleted(int32 completedStagesCount)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(SpellScript* spellScript, int32 completedStagesCount) const
{
return _invoker.Thunk(*spellScript, completedStagesCount, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
// left for custom compatibility only, DO NOT USE
#define PrepareSpellScript(CLASSNAME)
SpellScript() noexcept;
~SpellScript();
bool _Validate(SpellInfo const* entry) override;
bool _Load(Spell* spell);
void _InitHit();
bool _IsEffectPrevented(SpellEffIndex effIndex) const { return (m_hitPreventEffectMask & (1 << effIndex)) != 0; }
bool _IsDefaultEffectPrevented(SpellEffIndex effIndex) const { return (m_hitPreventDefaultEffectMask & (1 << effIndex)) != 0; }
void _PrepareScriptCall(SpellScriptHookType hookType);
void _FinishScriptCall();
bool IsInCheckCastHook() const;
bool IsAfterTargetSelectionPhase() const;
bool IsInTargetHook() const;
bool IsInModifiableHook() const;
bool IsInHitPhase() const;
bool IsInEffectHook() const;
private:
Spell* m_spell;
uint32 m_hitPreventEffectMask;
uint32 m_hitPreventDefaultEffectMask;
public:
//
// SpellScript interface
//
// example: void OnPrecast() override { }
virtual void OnPrecast() { }
//
// hooks to which you can attach your functions
//
// example: BeforeCast += SpellCastFn(class::function);
HookList BeforeCast;
// example: OnCast += SpellCastFn(class::function);
HookList OnCast;
// example: AfterCast += SpellCastFn(class::function);
HookList AfterCast;
#define SpellCastFn(F) CastHandler(&F)
// example: OnCheckCast += SpellCheckCastFn();
// where function is SpellCastResult function()
HookList OnCheckCast;
#define SpellCheckCastFn(F) CheckCastHandler(&F)
// example: int32 CalcCastTime(int32 castTime) override { return 1500; }
virtual int32 CalcCastTime(int32 castTime) { return castTime; }
// example: OnEffect**** += SpellEffectFn(class::function, EffectIndexSpecifier, EffectNameSpecifier);
// where function is void function(SpellEffIndex effIndex)
HookList OnEffectLaunch;
HookList OnEffectLaunchTarget;
HookList OnEffectHit;
HookList OnEffectHitTarget;
HookList OnEffectSuccessfulDispel;
#define SpellEffectFn(F, I, N) EffectHandler(&F, I, N)
// example: BeforeHit += BeforeSpellHitFn(class::function);
// where function is void function(SpellMissInfo missInfo)
HookList BeforeHit;
#define BeforeSpellHitFn(F) BeforeHitHandler(&F)
// example: OnHit += SpellHitFn(class::function);
HookList OnHit;
// example: AfterHit += SpellHitFn(class::function);
HookList AfterHit;
// where function is: void function()
#define SpellHitFn(F) HitHandler(&F)
// example: OnCalcCritChance += SpellOnCalcCritChanceFn(class::function);
// where function is: void function(Unit* victim, float& critChance)
HookList OnCalcCritChance;
#define SpellOnCalcCritChanceFn(F) OnCalcCritChanceHandler(&F)
// example: OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(class::function, EffectIndexSpecifier, TargetsNameSpecifier);
// where function is void function(std::list& targets)
HookList OnObjectAreaTargetSelect;
#define SpellObjectAreaTargetSelectFn(F, I, N) ObjectAreaTargetSelectHandler(&F, I, N)
// example: OnObjectTargetSelect += SpellObjectTargetSelectFn(class::function, EffectIndexSpecifier, TargetsNameSpecifier);
// where function is void function(WorldObject*& target)
HookList OnObjectTargetSelect;
#define SpellObjectTargetSelectFn(F, I, N) ObjectTargetSelectHandler(&F, I, N)
// example: OnDestinationTargetSelect += SpellDestinationTargetSelectFn(class::function, EffectIndexSpecifier, TargetsNameSpecifier);
// where function is void function(SpellDestination& target)
HookList OnDestinationTargetSelect;
#define SpellDestinationTargetSelectFn(F, I, N) DestinationTargetSelectHandler(&F, I, N)
// example: CalcDamage += SpellCalcDamageFn(class::function);
// where function is void function(SpellEffectInfo const& effectInfo, Unit* victim, int32& damage, int32& flatMod, float& pctMod)
HookList CalcDamage;
#define SpellCalcDamageFn(F) DamageAndHealingCalcHandler(&F)
// example: CalcHealing += SpellCalcHealingFn(class::function);
// where function is void function(SpellEffectInfo const& effectInfo, Unit* victim, int32& healing, int32& flatMod, float& pctMod)
HookList CalcHealing;
#define SpellCalcHealingFn(F) DamageAndHealingCalcHandler(&F)
// example: OnCalculateResistAbsorb += SpellOnResistAbsorbCalculateFn(class::function);
// where function is void function(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount)
HookList OnCalculateResistAbsorb;
#define SpellOnResistAbsorbCalculateFn(F) OnCalculateResistAbsorbHandler(&F)
// example: OnEmpowerStageCompleted += SpellOnEmpowerStageCompletedFn(class::function);
// where function is void function(int32 completedStages)
HookList OnEmpowerStageCompleted;
#define SpellOnEmpowerStageCompletedFn(F) EmpowerStageCompletedHandler(&F)
// example: OnEmpowerCompleted += SpellOnEmpowerCompletedFn(class::function);
// where function is void function(int32 completedStages)
HookList OnEmpowerCompleted;
#define SpellOnEmpowerCompletedFn(F) EmpowerStageCompletedHandler(&F)
// hooks are executed in following order, at specified event of spell:
// 1. OnPrecast - executed during spell preparation (before cast bar starts)
// 2. BeforeCast - executed when spell preparation is finished (when cast bar becomes full) before cast is handled
// 3. OnCheckCast - allows to override result of CheckCast function
// 4a. OnObjectAreaTargetSelect - executed just before adding selected targets to final target list (for area targets)
// 4b. OnObjectTargetSelect - executed just before adding selected target to final target list (for single unit targets)
// 4c. OnDestinationTargetSelect - executed just before adding selected target to final target list (for destination targets)
// 5. OnCast - executed just before spell is launched (creates missile) or executed
// 6. AfterCast - executed after spell missile is launched and immediate spell actions are done
// 7. OnEffectLaunch - executed just before specified effect handler call - when spell missile is launched
// 8. OnCalcCritChance - executed just after specified effect handler call - when spell missile is launched - called for each target from spell target map
// 9. OnEffectLaunchTarget - executed just before specified effect handler call - when spell missile is launched - called for each target from spell target map
// 10a. CalcDamage - executed during specified effect handler call - when spell missile is launched - called for each target from spell target map
// 10b. CalcHealing - executed during specified effect handler call - when spell missile is launched - called for each target from spell target map
// 11. OnCalculateResistAbsorb - executed when damage resist/absorbs is calculated - before spell hit target
// 12. OnEffectHit - executed just before specified effect handler call - when spell missile hits dest
// 13. BeforeHit - executed just before spell hits a target - called for each target from spell target map
// 14. OnEffectHitTarget - executed just before specified effect handler call - called for each target from spell target map
// 15. OnHit - executed just before spell deals damage and procs auras - when spell hits target - called for each target from spell target map
// 16. AfterHit - executed just after spell finishes all it's jobs for target - called for each target from spell target map
// 17. OnEmpowerStageCompleted - executed when empowered spell completes each stage
// 18. OnEmpowerCompleted - executed when empowered spell is released
// this hook is only executed after a successful dispel of any aura
// OnEffectSuccessfulDispel - executed just after effect successfully dispelled aura(s)
//
// methods allowing interaction with Spell object
//
// methods useable during all spell handling phases
Unit* GetCaster() const;
GameObject* GetGObjCaster() const;
Unit* GetOriginalCaster() const;
SpellInfo const* GetSpellInfo() const;
SpellEffectInfo const& GetEffectInfo(SpellEffIndex effIndex) const;
SpellValue const* GetSpellValue() const;
// methods useable after spell is prepared
// accessors to the explicit targets of the spell
// explicit target - target selected by caster (player, game client, or script - DoCast(explicitTarget, ...), required for spell to be cast
// examples:
// -shadowstep - explicit target is the unit you want to go behind of
// -chain heal - explicit target is the unit to be healed first
// -holy nova/arcane explosion - explicit target = NULL because target you are selecting doesn't affect how spell targets are selected
// you can determine if spell requires explicit targets by dbc columns:
// - Targets - mask of explicit target types
// - ImplicitTargetXX set to TARGET_XXX_TARGET_YYY, _TARGET_ here means that explicit target is used by the effect, so spell needs one too
// returns: WorldLocation which was selected as a spell destination or NULL
WorldLocation const* GetExplTargetDest() const;
void SetExplTargetDest(WorldLocation const& loc);
// returns: WorldObject which was selected as an explicit spell target or NULL if there's no target
WorldObject* GetExplTargetWorldObject() const;
// returns: Unit which was selected as an explicit spell target or NULL if there's no target
Unit* GetExplTargetUnit() const;
// returns: GameObject which was selected as an explicit spell target or NULL if there's no target
GameObject* GetExplTargetGObj() const;
// returns: Item which was selected as an explicit spell target or NULL if there's no target
Item* GetExplTargetItem() const;
// methods usable only after spell targets have been fully selected
int64 GetUnitTargetCountForEffect(SpellEffIndex effect) const;
int64 GetGameObjectTargetCountForEffect(SpellEffIndex effect) const;
int64 GetItemTargetCountForEffect(SpellEffIndex effect) const;
int64 GetCorpseTargetCountForEffect(SpellEffIndex effect) const;
// methods useable only during spell hit on target, or during spell launch on target:
// returns: target of current effect if it was Unit otherwise NULL
Unit* GetHitUnit() const;
// returns: target of current effect if it was Creature otherwise NULL
Creature* GetHitCreature() const;
// returns: target of current effect if it was Player otherwise NULL
Player* GetHitPlayer() const;
// returns: target of current effect if it was Item otherwise NULL
Item* GetHitItem() const;
// returns: target of current effect if it was GameObject otherwise NULL
GameObject* GetHitGObj() const;
// returns: target of current effect if it was Corpse otherwise nullptr
Corpse* GetHitCorpse() const;
// returns: destination of current effect
WorldLocation* GetHitDest() const;
// setter/getter for for damage done by spell to target of spell hit
// returns damage calculated before hit, and real dmg done after hit
int32 GetHitDamage() const;
void SetHitDamage(int32 damage);
void PreventHitDamage() { SetHitDamage(0); }
// setter/getter for for heal done by spell to target of spell hit
// returns healing calculated before hit, and real dmg done after hit
int32 GetHitHeal() const;
void SetHitHeal(int32 heal);
void PreventHitHeal() { SetHitHeal(0); }
// returns: true if spell critically hits current HitUnit
bool IsHitCrit() const;
Spell* GetSpell() const { return m_spell; }
// returns current spell hit target aura
Aura* GetHitAura(bool dynObjAura = false, bool withRemoved = false) const;
// prevents applying aura on current spell hit target
void PreventHitAura();
// prevents effect execution on current spell hit target
// including other effect/hit scripts
// will not work on aura/damage/heal
// will not work if effects were already handled
void PreventHitEffect(SpellEffIndex effIndex);
// prevents default effect execution on current spell hit target
// will not work on aura/damage/heal effects
// will not work if effects were already handled
void PreventHitDefaultEffect(SpellEffIndex effIndex);
// method available only in EffectHandler method
SpellEffectInfo const& GetEffectInfo() const;
int32 GetEffectValue() const;
void SetEffectValue(int32 value);
float GetEffectVariance() const;
void SetEffectVariance(float variance);
// returns: cast item if present.
Item* GetCastItem() const;
// Creates item. Calls Spell::DoCreateItem method.
void CreateItem(uint32 itemId, ItemContext context);
// Returns SpellInfo from the spell that triggered the current one
SpellInfo const* GetTriggeringSpell() const;
// finishes spellcast prematurely with selected error message
void FinishCast(SpellCastResult result, int32* param1 = nullptr, int32* param2 = nullptr);
void SetCustomCastResultMessage(SpellCustomErrors result);
// returns desired cast difficulty for triggered spells
Difficulty GetCastDifficulty() const;
};
// AuraScript interface - enum used for runtime checks of script function calls
enum AuraScriptHookType
{
AURA_SCRIPT_HOOK_EFFECT_APPLY = SPELL_SCRIPT_STATE_END,
AURA_SCRIPT_HOOK_EFFECT_AFTER_APPLY,
AURA_SCRIPT_HOOK_EFFECT_REMOVE,
AURA_SCRIPT_HOOK_EFFECT_AFTER_REMOVE,
AURA_SCRIPT_HOOK_EFFECT_PERIODIC,
AURA_SCRIPT_HOOK_EFFECT_UPDATE_PERIODIC,
AURA_SCRIPT_HOOK_EFFECT_CALC_AMOUNT,
AURA_SCRIPT_HOOK_EFFECT_CALC_PERIODIC,
AURA_SCRIPT_HOOK_EFFECT_CALC_SPELLMOD,
AURA_SCRIPT_HOOK_EFFECT_CALC_CRIT_CHANCE,
AURA_SCRIPT_HOOK_EFFECT_CALC_DAMAGE_AND_HEALING,
AURA_SCRIPT_HOOK_EFFECT_ABSORB,
AURA_SCRIPT_HOOK_EFFECT_AFTER_ABSORB,
AURA_SCRIPT_HOOK_EFFECT_MANASHIELD,
AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD,
AURA_SCRIPT_HOOK_EFFECT_SPLIT,
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,
AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC,
AURA_SCRIPT_HOOK_PREPARE_PROC,
AURA_SCRIPT_HOOK_PROC,
AURA_SCRIPT_HOOK_EFFECT_PROC,
AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC,
AURA_SCRIPT_HOOK_AFTER_PROC,
/*AURA_SCRIPT_HOOK_APPLY,
AURA_SCRIPT_HOOK_REMOVE, */
};
/*
#define HOOK_AURA_EFFECT_START HOOK_AURA_EFFECT_APPLY
#define HOOK_AURA_EFFECT_END HOOK_AURA_EFFECT_CALC_SPELLMOD + 1
#define HOOK_AURA_EFFECT_COUNT HOOK_AURA_EFFECT_END - HOOK_AURA_EFFECT_START
*/
class TC_GAME_API AuraScript : public SpellScriptBase
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
template
using ScriptFuncInvoker = SpellScriptBase::ScriptFuncInvoker;
public:
class CheckAreaTargetHandler final
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit CheckAreaTargetHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(CheckAreaTargetHandler signature must be "bool CheckTarget(Unit* target)")"");
else
static_assert(std::is_invocable_r_v,
R""(CheckAreaTargetHandler signature must be "static bool CheckTarget(your_script_class& script, Unit* target)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
bool Call(AuraScript* auraScript, Unit* target) const
{
return _invoker.Thunk(*auraScript, target, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class AuraDispelHandler final
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit AuraDispelHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(AuraDispelHandler signature must be "void HandleDispel(DispelInfo* dispelInfo)")"");
else
static_assert(std::is_invocable_r_v,
R""(AuraDispelHandler signature must be "static void HandleDispel(your_script_class& script, DispelInfo* dispelInfo)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, DispelInfo* dispelInfo) const
{
return _invoker.Thunk(*auraScript, dispelInfo, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class AuraHeartbeatHandler final
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit AuraHeartbeatHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(AuraHeartbeatHandler signature must be "void HandleHeartbeat()")"");
else
static_assert(std::is_invocable_r_v,
R""(AuraHeartbeatHandler signature must be "static void HandleHeartbeat(your_script_class& script)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript) const
{
return _invoker.Thunk(*auraScript, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class TC_GAME_API EffectBase : public EffectHook
{
public:
EffectBase(uint8 effIndex, uint16 auraType);
EffectBase(EffectBase const& right) = delete;
EffectBase(EffectBase&& right) noexcept;
EffectBase& operator=(EffectBase const& right) = delete;
EffectBase& operator=(EffectBase&& right) noexcept;
virtual ~EffectBase();
std::string ToString() const;
bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex) const override;
private:
uint16 _auraType;
};
class EffectPeriodicHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectPeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectPeriodicHandler signature must be "void HandlePeriodic(AuraEffect const* aurEff)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectPeriodicHandler signature must be "static void HandlePeriodic(your_script_class& script, AuraEffect const* aurEff)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff) const
{
return _invoker.Thunk(*auraScript, aurEff, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectUpdatePeriodicHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectUpdatePeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectUpdatePeriodicHandler signature must be "void HandleUpdatePeriodic(AuraEffect* aurEff)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectUpdatePeriodicHandler signature must be "static void HandleUpdatePeriodic(your_script_class& script, AuraEffect* aurEff)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect* aurEff) const
{
return _invoker.Thunk(*auraScript, aurEff, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectCalcAmountHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectCalcAmountHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectCalcAmountHandler signature must be "void CalcAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectCalcAmountHandler signature must be "static void CalcAmount(your_script_class& script, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) const
{
return _invoker.Thunk(*auraScript, aurEff, amount, canBeRecalculated, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectCalcPeriodicHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectCalcPeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectCalcPeriodicHandler signature must be "void CalcPeriodic(AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectCalcPeriodicHandler signature must be "static void CalcPeriodic(your_script_class& script, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer) const
{
return _invoker.Thunk(*auraScript, aurEff, isPeriodic, periodicTimer, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectCalcSpellModHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectCalcSpellModHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectCalcSpellModHandler signature must be "void CalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectCalcSpellModHandler signature must be "static void CalcSpellMod(your_script_class& script, AuraEffect const* aurEff, SpellModifier*& spellMod)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, SpellModifier*& spellMod) const
{
return _invoker.Thunk(*auraScript, aurEff, spellMod, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectCalcCritChanceHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectCalcCritChanceHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectCalcCritChanceHandler signature must be "void CalcCritChance(AuraEffect const* aurEff, Unit const* victim, float& critChance)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectCalcCritChanceHandler signature must be "static void CalcCritChance(your_script_class& script, AuraEffect const* aurEff, Unit const* victim, float& critChance)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, Unit const* victim, float& critChance) const
{
return _invoker.Thunk(*auraScript, aurEff, victim, critChance, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectCalcDamageAndHealingHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectCalcDamageAndHealingHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectCalcDamageAndHealingHandler signature must be "void CalcDamageAndHealing(AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectCalcDamageAndHealingHandler signature must be "static void CalcDamageAndHealing(your_script_class& script, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod) const
{
return _invoker.Thunk(*auraScript, aurEff, victim, damageOrHealing, flatMod, pctMod, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectApplyHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectApplyHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType, AuraEffectHandleModes mode)
: EffectBase(effIndex, auraType), _mode(mode)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectApplyHandler signature must be "void HandleApplyOrRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectApplyHandler signature must be "static void HandleApplyOrRemove(your_script_class& script, AuraEffect const* aurEff, AuraEffectHandleModes mode)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectHandleModes mode) const
{
if (!(_mode & mode))
return;
return _invoker.Thunk(*auraScript, aurEff, mode, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
AuraEffectHandleModes _mode;
};
class EffectAbsorbHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectAbsorbHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectAbsorbHandler signature must be "void HandleAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectAbsorbHandler signature must be "static void HandleAbsorb(your_script_class& script, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) const
{
return _invoker.Thunk(*auraScript, aurEff, dmgInfo, absorbAmount, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class EffectAbsorbHealHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit EffectAbsorbHealHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
: EffectBase(effIndex, auraType)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(EffectAbsorbHealHandler signature must be "void HandleAbsorb(AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount)")"");
else
static_assert(std::is_invocable_r_v,
R""(EffectAbsorbHealHandler signature must be "static void HandleAbsorb(your_script_class& script, AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
void Call(AuraScript* auraScript, AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount) const
{
return _invoker.Thunk(*auraScript, aurEff, healInfo, absorbAmount, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class CheckProcHandler final
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker;
template
explicit CheckProcHandler(ScriptFunc handler)
{
using ScriptClass = GetScriptClass_t;
static_assert(ScriptFuncInvoker::Size >= sizeof(ScriptFunc));
static_assert(ScriptFuncInvoker::Alignment >= alignof(ScriptFunc));
if constexpr (std::is_member_function_pointer_v)
static_assert(std::is_invocable_r_v,
R""(CheckProcHandler signature must be "bool CheckProc(ProcEventInfo& eventInfo)")"");
else
static_assert(std::is_invocable_r_v,
R""(CheckProcHandler signature must be "static bool CheckProc(your_script_class& script, ProcEventInfo& eventInfo)")"");
new (_invoker.ImplStorage.data()) ScriptFuncInvoker::Impl{ .Func = handler };
_invoker.Thunk = &ScriptFuncInvoker::Impl::Invoke;
}
bool Call(AuraScript* auraScript, ProcEventInfo& eventInfo) const
{
return _invoker.Thunk(*auraScript, eventInfo, _invoker.ImplStorage);
}
private:
ScriptFuncInvoker _invoker;
};
class CheckEffectProcHandler final : public EffectBase
{
public:
using ScriptFuncInvoker = AuraScript::ScriptFuncInvoker