/*
 * 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();
    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);
    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
    using GetScriptClass_t = typename GetScriptClass::type;
    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,
};
#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
public:
    class CastHandler final
    {
    public:
        using SpellCastFnType = void(SpellScript::*)();
        using SafeWrapperType = void(*)(SpellScript* spellScript, SpellCastFnType callImpl);
        template
        explicit CastHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellCastFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellCastFnType) >= alignof(ScriptFunc));
            static_assert(std::is_invocable_r_v,
                "CastHandler signature must be \"void HandleCast()\"");
            _callImpl = reinterpret_cast(handler);
            _safeWrapper = [](SpellScript* spellScript, SpellCastFnType callImpl) -> void
            {
                return (static_cast(spellScript)->*reinterpret_cast(callImpl))();
            };
        }
        void Call(SpellScript* spellScript) const
        {
            return _safeWrapper(spellScript, _callImpl);
        }
    private:
        SpellCastFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class CheckCastHandler final
    {
    public:
        union SpellCheckCastFnType
        {
            SpellCastResult(SpellScript::* Member)();
            SpellCastResult(*Static)();
        };
        using SafeWrapperType = SpellCastResult(*)(SpellScript* spellScript, SpellCheckCastFnType callImpl);
        template
        explicit CheckCastHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellCheckCastFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellCheckCastFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "CheckCastHandler signature must be \"SpellCastResult CheckCast()\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, SpellCheckCastFnType callImpl) -> SpellCastResult
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))();
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                   "CheckCastHandler signature must be \"static SpellCastResult CheckCast()\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, SpellCheckCastFnType callImpl) -> SpellCastResult
                {
                    return reinterpret_cast(callImpl.Static)();
                };
            }
        }
        SpellCastResult Call(SpellScript* spellScript) const
        {
            return _safeWrapper(spellScript, _callImpl);
        }
    private:
        SpellCheckCastFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    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 SpellEffectFnType = void(SpellScript::*)(SpellEffIndex effIndex);
        using SafeWrapperType = void(*)(SpellScript* spellScript, SpellEffIndex effIndex, SpellEffectFnType callImpl);
        template
        explicit EffectHandler(ScriptFunc handler, uint8 effIndex, uint16 effName)
            : EffectBase(effIndex, effName)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellEffectFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellEffectFnType) >= alignof(ScriptFunc));
            static_assert(std::is_invocable_r_v,
                "EffectHandler signature must be \"void HandleEffect(SpellEffIndex effIndex)\"");
            _callImpl = reinterpret_cast(handler);
            _safeWrapper = [](SpellScript* spellScript, SpellEffIndex effIndex, SpellEffectFnType callImpl) -> void
            {
                return (static_cast(spellScript)->*reinterpret_cast(callImpl))(effIndex);
            };
        }
        void Call(SpellScript* spellScript, SpellEffIndex effIndex) const
        {
            return _safeWrapper(spellScript, effIndex, _callImpl);
        }
    private:
        SpellEffectFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class BeforeHitHandler final
    {
    public:
        using SpellBeforeHitFnType = void(SpellScript::*)(SpellMissInfo missInfo);
        using SafeWrapperType = void(*)(SpellScript* spellScript, SpellMissInfo missInfo, SpellBeforeHitFnType callImpl);
        template
        explicit BeforeHitHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellBeforeHitFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellBeforeHitFnType) >= alignof(ScriptFunc));
            static_assert(std::is_invocable_r_v,
                "BeforeHitHandler signature must be \"void HandleBeforeHit(SpellMissInfo missInfo)\"");
            _callImpl = reinterpret_cast(handler);
            _safeWrapper = [](SpellScript* spellScript, SpellMissInfo missInfo, SpellBeforeHitFnType callImpl) -> void
            {
                return (static_cast(spellScript)->*reinterpret_cast(callImpl))(missInfo);
            };
        }
        void Call(SpellScript* spellScript, SpellMissInfo missInfo) const
        {
            return _safeWrapper(spellScript, missInfo, _callImpl);
        }
    private:
        SpellBeforeHitFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class HitHandler final
    {
    public:
        using SpellHitFnType = void(SpellScript::*)();
        using SafeWrapperType = void(*)(SpellScript* spellScript, SpellHitFnType callImpl);
        template
        explicit HitHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellHitFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellHitFnType) >= alignof(ScriptFunc));
            static_assert(std::is_invocable_r_v,
                "HitHandler signature must be \"void HandleHit()\"");
            _callImpl = reinterpret_cast(handler);
            _safeWrapper = [](SpellScript* spellScript, SpellHitFnType callImpl) -> void
            {
                return (static_cast(spellScript)->*reinterpret_cast(callImpl))();
            };
        }
        void Call(SpellScript* spellScript) const
        {
            return _safeWrapper(spellScript, _callImpl);
        }
    private:
        SpellHitFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class OnCalcCritChanceHandler final
    {
    public:
        union SpellOnCalcCritChanceFnType
        {
            void(SpellScript::* Member)(Unit const* victim, float& critChance);
            void(*Static)(Unit const* victim, float& critChance);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, Unit const* victim, float& critChance, SpellOnCalcCritChanceFnType callImpl);
        template
        explicit OnCalcCritChanceHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellOnCalcCritChanceFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellOnCalcCritChanceFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "OnCalcCritChanceHandler signature must be \"void CalcCritChance(Unit const* victim, float& critChance)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, Unit const* victim, float& critChance, SpellOnCalcCritChanceFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(victim, critChance);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "OnCalcCritChanceHandler signature must be \"static void CalcCritChance(Unit const* victim, float& critChance)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, Unit const* victim, float& critChance, SpellOnCalcCritChanceFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(victim, critChance);
                };
            }
        }
        void Call(SpellScript* spellScript, Unit const* victim, float& critChance) const
        {
            return _safeWrapper(spellScript, victim, critChance, _callImpl);
        }
    private:
        SpellOnCalcCritChanceFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    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:
        union SpellObjectAreaTargetSelectFnType
        {
            void(SpellScript::* Member)(std::list& targets);
            void(*Static)(std::list& targets);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, std::list& targets, SpellObjectAreaTargetSelectFnType callImpl);
        template
        explicit ObjectAreaTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
            : TargetHook(effIndex, targetType, true, false)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellObjectAreaTargetSelectFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellObjectAreaTargetSelectFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v&>,
                    "ObjectAreaTargetSelectHandler signature must be \"void SetTargets(std::list& targets)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, std::list& targets, SpellObjectAreaTargetSelectFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(targets);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v&>,
                    "ObjectAreaTargetSelectHandler signature must be \"static void SetTargets(std::list& targets)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, std::list& targets, SpellObjectAreaTargetSelectFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(targets);
                };
            }
        }
        void Call(SpellScript* spellScript, std::list& targets) const
        {
            return _safeWrapper(spellScript, targets, _callImpl);
        }
    private:
        SpellObjectAreaTargetSelectFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class ObjectTargetSelectHandler final : public TargetHook
    {
    public:
        union SpellObjectTargetSelectFnType
        {
            void(SpellScript::* Member)(WorldObject*& target);
            void(*Static)(WorldObject*& target);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, WorldObject*& target, SpellObjectTargetSelectFnType callImpl);
        template
        explicit ObjectTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
            : TargetHook(effIndex, targetType, false, false)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellObjectTargetSelectFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellObjectTargetSelectFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "ObjectTargetSelectHandler signature must be \"void SetTarget(WorldObject*& target)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, WorldObject*& target, SpellObjectTargetSelectFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(target);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "ObjectTargetSelectHandler signature must be \"static void SetTarget(WorldObject*& target)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, WorldObject*& target, SpellObjectTargetSelectFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(target);
                };
            }
        }
        void Call(SpellScript* spellScript, WorldObject*& target) const
        {
            return _safeWrapper(spellScript, target, _callImpl);
        }
    private:
        SpellObjectTargetSelectFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class DestinationTargetSelectHandler final : public TargetHook
    {
    public:
        union SpellDestinationTargetSelectFnType
        {
            void(SpellScript::* Member)(SpellDestination& target);
            void(*Static)(SpellDestination& target);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, SpellDestination& target, SpellDestinationTargetSelectFnType callImpl);
        template
        explicit DestinationTargetSelectHandler(ScriptFunc handler, uint8 effIndex, uint16 targetType)
            : TargetHook(effIndex, targetType, false, true)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellDestinationTargetSelectFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellDestinationTargetSelectFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "DestinationTargetSelectHandler signature must be \"void SetTarget(SpellDestination& target)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, SpellDestination& target, SpellDestinationTargetSelectFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(target);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "DestinationTargetSelectHandler signature must be \"static void SetTarget(SpellDestination& target)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, SpellDestination& target, SpellDestinationTargetSelectFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(target);
                };
            }
        }
        void Call(SpellScript* spellScript, SpellDestination& target) const
        {
            return _safeWrapper(spellScript, target, _callImpl);
        }
    private:
        SpellDestinationTargetSelectFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class DamageAndHealingCalcHandler final
    {
    public:
        union DamageAndHealingCalcFnType
        {
            void(SpellScript::* Member)(Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod);
            void(*Static)(Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, DamageAndHealingCalcFnType callImpl);
        template
        explicit DamageAndHealingCalcHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(DamageAndHealingCalcFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(DamageAndHealingCalcFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "DamageAndHealingCalcHandler signature must be \"void CalcDamage(Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, DamageAndHealingCalcFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(victim, damageOrHealing, flatMod, pctMod);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "DamageAndHealingCalcHandler signature must be \"static void CalcDamage(Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, DamageAndHealingCalcFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(victim, damageOrHealing, flatMod, pctMod);
                };
            }
        }
        void Call(SpellScript* spellScript, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod) const
        {
            return _safeWrapper(spellScript, victim, damageOrHealing, flatMod, pctMod, _callImpl);
        }
    private:
        DamageAndHealingCalcFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class OnCalculateResistAbsorbHandler final
    {
    public:
        union SpellOnResistAbsorbCalculateFnType
        {
            void(SpellScript::* Member)(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount);
            void(*Static)(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount);
        };
        using SafeWrapperType = void(*)(SpellScript* spellScript, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount, SpellOnResistAbsorbCalculateFnType callImpl);
        template
        explicit OnCalculateResistAbsorbHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(SpellOnResistAbsorbCalculateFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(SpellOnResistAbsorbCalculateFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "OnCalculateResistAbsorbHandler signature must be \"void CalcAbsorbResist(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* spellScript, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount, SpellOnResistAbsorbCalculateFnType callImpl) -> void
                {
                    return (static_cast(spellScript)->*reinterpret_cast(callImpl.Member))(damageInfo, resistAmount, absorbAmount);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "OnCalculateResistAbsorbHandler signature must be \"static void CalcAbsorbResist(DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](SpellScript* /*spellScript*/, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount, SpellOnResistAbsorbCalculateFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(damageInfo, resistAmount, absorbAmount);
                };
            }
        }
        void Call(SpellScript* spellScript, DamageInfo const& damageInfo, uint32& resistAmount, int32& absorbAmount) const
        {
            return _safeWrapper(spellScript, damageInfo, resistAmount, absorbAmount, _callImpl);
        }
    private:
        SpellOnResistAbsorbCalculateFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
     // left for custom compatibility only, DO NOT USE
    #define PrepareSpellScript(CLASSNAME)
    SpellScript();
    ~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(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(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)
    // 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
    // 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) 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_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
public:
    class CheckAreaTargetHandler final
    {
    public:
        union AuraCheckAreaTargetFnType
        {
            bool(AuraScript::* Member)(Unit* target);
            bool(*Static)(Unit* target);
        };
        using SafeWrapperType = bool(*)(AuraScript* auraScript, Unit* target, AuraCheckAreaTargetFnType callImpl);
        template
        explicit CheckAreaTargetHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraCheckAreaTargetFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraCheckAreaTargetFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "CheckAreaTargetHandler signature must be \"bool CheckTarget(Unit* target)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, Unit* target, AuraCheckAreaTargetFnType callImpl) -> bool
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(target);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "CheckAreaTargetHandler signature must be \"static bool CheckTarget(Unit* target)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, Unit* target, AuraCheckAreaTargetFnType callImpl) -> bool
                {
                    return reinterpret_cast(callImpl.Static)(target);
                };
            }
        }
        bool Call(AuraScript* auraScript, Unit* target) const
        {
            return _safeWrapper(auraScript, target, _callImpl);
        }
    private:
        AuraCheckAreaTargetFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class AuraDispelHandler final
    {
    public:
        union AuraDispelFnType
        {
            void(AuraScript::* Member)(DispelInfo* dispelInfo);
            void(*Static)(DispelInfo* dispelInfo);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, DispelInfo* dispelInfo, AuraDispelFnType callImpl);
        template
        explicit AuraDispelHandler(ScriptFunc handler)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraDispelFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraDispelFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "AuraDispelHandler signature must be \"void HandleDispel(DispelInfo* dispelInfo)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, DispelInfo* dispelInfo, AuraDispelFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(dispelInfo);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "AuraDispelHandler signature must be \"static void HandleDispel(DispelInfo* dispelInfo)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, DispelInfo* dispelInfo, AuraDispelFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(dispelInfo);
                };
            }
        }
        void Call(AuraScript* auraScript, DispelInfo* dispelInfo) const
        {
            return _safeWrapper(auraScript, dispelInfo, _callImpl);
        }
    private:
        AuraDispelFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    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:
        union AuraEffectPeriodicFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff);
            void(*Static)(AuraEffect const* aurEff);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectPeriodicFnType callImpl);
        template
        explicit EffectPeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectPeriodicFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectPeriodicFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectPeriodicHandler signature must be \"void HandlePeriodic(AuraEffect const* aurEff)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectPeriodicFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectPeriodicHandler signature must be \"static void HandlePeriodic(AuraEffect const* aurEff)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, AuraEffectPeriodicFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff) const
        {
            return _safeWrapper(auraScript, aurEff, _callImpl);
        }
    private:
        AuraEffectPeriodicFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectUpdatePeriodicHandler final : public EffectBase
    {
    public:
        union AuraEffectUpdatePeriodicFnType
        {
            void(AuraScript::* Member)(AuraEffect* aurEff);
            void(*Static)(AuraEffect* aurEff);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect* aurEff, AuraEffectUpdatePeriodicFnType callImpl);
        template
        explicit EffectUpdatePeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectUpdatePeriodicFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectUpdatePeriodicFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectUpdatePeriodicHandler signature must be \"void HandleUpdatePeriodic(AuraEffect* aurEff)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect* aurEff, AuraEffectUpdatePeriodicFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectUpdatePeriodicHandler signature must be \"static void HandleUpdatePeriodic(AuraEffect* aurEff)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect* aurEff, AuraEffectUpdatePeriodicFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect* aurEff) const
        {
            return _safeWrapper(auraScript, aurEff, _callImpl);
        }
    private:
        AuraEffectUpdatePeriodicFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectCalcAmountHandler final : public EffectBase
    {
    public:
        union AuraEffectCalcAmountFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated);
            void(*Static)(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated, AuraEffectCalcAmountFnType callImpl);
        template
        explicit EffectCalcAmountHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectCalcAmountFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectCalcAmountFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcAmountHandler signature must be \"void CalcAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated, AuraEffectCalcAmountFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, amount, canBeRecalculated);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcAmountHandler signature must be \"static void CalcAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated, AuraEffectCalcAmountFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, amount, canBeRecalculated);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) const
        {
            return _safeWrapper(auraScript, aurEff, amount, canBeRecalculated, _callImpl);
        }
    private:
        AuraEffectCalcAmountFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectCalcPeriodicHandler final : public EffectBase
    {
    public:
        union AuraEffectCalcPeriodicFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer);
            void(*Static)(AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer, AuraEffectCalcPeriodicFnType callImpl);
        template
        explicit EffectCalcPeriodicHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectCalcPeriodicFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectCalcPeriodicFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcPeriodicHandler signature must be \"void CalcPeriodic(AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer, AuraEffectCalcPeriodicFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, isPeriodic, periodicTimer);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcPeriodicHandler signature must be \"static void CalcPeriodic(AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer, AuraEffectCalcPeriodicFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, isPeriodic, periodicTimer);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, bool& isPeriodic, int32& periodicTimer) const
        {
            return _safeWrapper(auraScript, aurEff, isPeriodic, periodicTimer, _callImpl);
        }
    private:
        AuraEffectCalcPeriodicFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectCalcSpellModHandler final : public EffectBase
    {
    public:
        union AuraEffectCalcSpellModFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, SpellModifier*& spellMod);
            void(*Static)(AuraEffect const* aurEff, SpellModifier*& spellMod);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, SpellModifier*& spellMod, AuraEffectCalcSpellModFnType callImpl);
        template
        explicit EffectCalcSpellModHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectCalcSpellModFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectCalcSpellModFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcSpellModHandler signature must be \"void CalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, SpellModifier*& spellMod, AuraEffectCalcSpellModFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, spellMod);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcSpellModHandler signature must be \"static void CalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, SpellModifier*& spellMod, AuraEffectCalcSpellModFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, spellMod);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, SpellModifier*& spellMod) const
        {
            return _safeWrapper(auraScript, aurEff, spellMod, _callImpl);
        }
    private:
        AuraEffectCalcSpellModFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectCalcCritChanceHandler final : public EffectBase
    {
    public:
        union AuraEffectCalcCritChanceFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, Unit const* victim, float& critChance);
            void(*Static)(AuraEffect const* aurEff, Unit const* victim, float& critChance);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, Unit const* victim, float& critChance, AuraEffectCalcCritChanceFnType callImpl);
        template
        explicit EffectCalcCritChanceHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectCalcCritChanceFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectCalcCritChanceFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcCritChanceHandler signature must be \"void CalcCritChance(AuraEffect const* aurEff, Unit const* victim, float& critChance)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, Unit const* victim, float& critChance, AuraEffectCalcCritChanceFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, victim, critChance);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcCritChanceHandler signature must be \"static void CalcCritChance(AuraEffect const* aurEff, Unit const* victim, float& critChance)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, Unit const* victim, float& critChance, AuraEffectCalcCritChanceFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, victim, critChance);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, Unit const* victim, float& critChance) const
        {
            return _safeWrapper(auraScript, aurEff, victim, critChance, _callImpl);
        }
    private:
        AuraEffectCalcCritChanceFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectCalcDamageAndHealingHandler final : public EffectBase
    {
    public:
        union AuraEffectDamageAndHealingCalcFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod);
            void(*Static)(AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, AuraEffectDamageAndHealingCalcFnType callImpl);
        template
        explicit EffectCalcDamageAndHealingHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectDamageAndHealingCalcFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectDamageAndHealingCalcFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcDamageAndHealingHandler signature must be \"void CalcDamageAndHealing(AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, AuraEffectDamageAndHealingCalcFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, victim, damageOrHealing, flatMod, pctMod);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectCalcDamageAndHealingHandler signature must be \"static void CalcDamageAndHealing(AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod, AuraEffectDamageAndHealingCalcFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, victim, damageOrHealing, flatMod, pctMod);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod) const
        {
            return _safeWrapper(auraScript, aurEff, victim, damageOrHealing, flatMod, pctMod, _callImpl);
        }
    private:
        AuraEffectDamageAndHealingCalcFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectApplyHandler final : public EffectBase
    {
    public:
        union AuraEffectApplicationModeFnType
        {
            void(AuraScript::* Member)(AuraEffect const* aurEff, AuraEffectHandleModes mode);
            void(*Static)(AuraEffect const* aurEff, AuraEffectHandleModes mode);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectHandleModes mode, AuraEffectApplicationModeFnType callImpl);
        template
        explicit EffectApplyHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType, AuraEffectHandleModes mode)
            : EffectBase(effIndex, auraType), _mode(mode)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectApplicationModeFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectApplicationModeFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectApplyHandler signature must be \"void HandleApplyOrRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectHandleModes mode, AuraEffectApplicationModeFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, mode);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectApplyHandler signature must be \"static void HandleApplyOrRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect const* aurEff, AuraEffectHandleModes mode, AuraEffectApplicationModeFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, mode);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect const* aurEff, AuraEffectHandleModes mode) const
        {
            if (!(_mode & mode))
                return;
            return _safeWrapper(auraScript, aurEff, mode, _callImpl);
        }
    private:
        AuraEffectApplicationModeFnType _callImpl;
        SafeWrapperType _safeWrapper;
        AuraEffectHandleModes _mode;
    };
    class EffectAbsorbHandler final : public EffectBase
    {
    public:
        union AuraEffectAbsorbFnType
        {
            void(AuraScript::* Member)(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount);
            void(*Static)(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount, AuraEffectAbsorbFnType callImpl);
        template
        explicit EffectAbsorbHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectAbsorbFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectAbsorbFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectAbsorbHandler signature must be \"void HandleAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount, AuraEffectAbsorbFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast(callImpl.Member))(aurEff, dmgInfo, absorbAmount);
                };
            }
            else
            {
                static_assert(std::is_invocable_r_v,
                    "EffectAbsorbHandler signature must be \"static void HandleAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)\"");
                _callImpl = { .Static = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* /*auraScript*/, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount, AuraEffectAbsorbFnType callImpl) -> void
                {
                    return reinterpret_cast(callImpl.Static)(aurEff, dmgInfo, absorbAmount);
                };
            }
        }
        void Call(AuraScript* auraScript, AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) const
        {
            return _safeWrapper(auraScript, aurEff, dmgInfo, absorbAmount, _callImpl);
        }
    private:
        AuraEffectAbsorbFnType _callImpl;
        SafeWrapperType _safeWrapper;
    };
    class EffectAbsorbHealHandler final : public EffectBase
    {
    public:
        union AuraEffectAbsorbHealFnType
        {
            void(AuraScript::* Member)(AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount);
            void(*Static)(AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount);
        };
        using SafeWrapperType = void(*)(AuraScript* auraScript, AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount, AuraEffectAbsorbHealFnType callImpl);
        template
        explicit EffectAbsorbHealHandler(ScriptFunc handler, uint8 effIndex, uint16 auraType)
            : EffectBase(effIndex, auraType)
        {
            using ScriptClass = GetScriptClass_t;
            static_assert(sizeof(AuraEffectAbsorbHealFnType) >= sizeof(ScriptFunc));
            static_assert(alignof(AuraEffectAbsorbHealFnType) >= alignof(ScriptFunc));
            if constexpr (!std::is_void_v)
            {
                static_assert(std::is_invocable_r_v,
                    "EffectAbsorbHealHandler signature must be \"void HandleAbsorb(AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount)\"");
                _callImpl = { .Member = reinterpret_cast(handler) };
                _safeWrapper = [](AuraScript* auraScript, AuraEffect* aurEff, HealInfo& healInfo, uint32& absorbAmount, AuraEffectAbsorbHealFnType callImpl) -> void
                {
                    return (static_cast(auraScript)->*reinterpret_cast