• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] SpellEventHandler

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
spelleventhandler.lua
Lua:
--[[ spelleventhandler.lua v1.0.0 by AGD | https://www.hiveworkshop.com/threads/330661/


    Description:

        Based on the resource RegisterSpellEffectEvent
        (https://www.hiveworkshop.com/threads/187193/), but with added
        flexibility and functionality. In particular, this allows you to
        register more than one handler function for every ability. This
        allows for a more advanced spellmaking.
        Moreover, this also supports all the spell events namely,
        EVENT_PLAYER_UNIT_SPELL_CHANNEL, EVENT_PLAYER_UNIT_SPELL_CAST,
        EVENT_PLAYER_UNIT_SPELL_EFFECT, EVENT_PLAYER_UNIT_SPELL_ENDCAST,
        and EVENT_PLAYER_UNIT_SPELL_FINISH. This is important in many
        cases, especially considering how common channeling spells are.


    Requirements:

        - EventListener
            Link: https://www.hiveworkshop.com/threads/323354/
        - Global Initialization
            Link: https://www.hiveworkshop.com/threads/317099/


    API:

        function RegisterSpellEventHandler(abilId, eventType, handlerFunc)
        function DeregisterSpellEventHandler(abilId, eventType, handlerFunc)
        - These functions are safe to call even before any initialization by
          blizzard

            Arguments:
            - abilId
                > Rawcode of the activation ability, example: FourCC("A001")
                > if <nil | 0>, handlerFunc would run for ALL registered
                  ability ids
            - eventType
                > EVENT_PLAYER_UNIT_SPELL_CHANNEL
                | EVENT_PLAYER_UNIT_SPELL_CAST
                | EVENT_PLAYER_UNIT_SPELL_EFFECT
                | EVENT_PLAYER_UNIT_SPELL_ENDCAST
                | EVENT_PLAYER_UNIT_SPELL_FINISH
                > if <nil>, registers/deregisters to/from ALL event types

            Return Value:
            - <nil>


        EVENT_SPELL_CHANNEL
        EVENT_SPELL_CAST
        EVENT_SPELL_EFFECT
        EVENT_SPELL_ENDCAST
        EVENT_SPELL_FINISH
            - These can be used optionally instead of the ones in common.j
              for a more concise naming

]]--
do
    -- Aliases
    EVENT_SPELL_CHANNEL = EVENT_PLAYER_UNIT_SPELL_CHANNEL
    EVENT_SPELL_CAST     = EVENT_PLAYER_UNIT_SPELL_CAST
    EVENT_SPELL_EFFECT     = EVENT_PLAYER_UNIT_SPELL_EFFECT
    EVENT_SPELL_ENDCAST = EVENT_PLAYER_UNIT_SPELL_ENDCAST
    EVENT_SPELL_FINISH     = EVENT_PLAYER_UNIT_SPELL_FINISH

    local handler_t = {}
    local trigger_h

    local function UpdateSpellHandler(callback, abilId, handlerFunc)
        callback(abilId, EVENT_PLAYER_UNIT_SPELL_CHANNEL, handlerFunc)
        callback(abilId, EVENT_PLAYER_UNIT_SPELL_CAST, handlerFunc)
        callback(abilId, EVENT_PLAYER_UNIT_SPELL_EFFECT, handlerFunc)
        callback(abilId, EVENT_PLAYER_UNIT_SPELL_ENDCAST, handlerFunc)
        callback(abilId, EVENT_PLAYER_UNIT_SPELL_FINISH, handlerFunc)
    end

    function RegisterSpellEventHandler(abilId, eventType, handlerFunc)
        if not handlerFunc then return end

        abilId = abilId or 0

        if eventType then
            local eventId = GetHandleId(eventType)

            if not handler_t[eventId] then
                handler_t[eventId] = {}

                if not handler_t[eventId][abilId] then
                    handler_t[eventId][abilId] = EventListener()
                end
            end

            handler_t[eventId][abilId]:register(handlerFunc)

        else
            UpdateSpellHandler(RegisterSpellEventHandler, abilId, handlerFunc)
        end
    end

    function DeregisterSpellEventHandler(abilId, eventType, handlerFunc)
        if not handlerFunc then return end

        abilId = abilId or 0

        if eventType then
            local eventId = GetHandleId(eventType)

            if handler_t[eventId] and handler_t[eventId][abilId] then
                handler_t[eventId][abilId]:deregister(handlerFunc)
            end
        else
            UpdateSpellHandler(DeregisterSpellEventHandler, abilId, handlerFunc)
        end
    end

    onInitialization(function ()
        trigger_h = CreateTrigger()

        TriggerRegisterAnyUnitEventBJ(trigger_h, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
        TriggerRegisterAnyUnitEventBJ(trigger_h, EVENT_PLAYER_UNIT_SPELL_CAST)
        TriggerRegisterAnyUnitEventBJ(trigger_h, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        TriggerRegisterAnyUnitEventBJ(trigger_h, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        TriggerRegisterAnyUnitEventBJ(trigger_h, EVENT_PLAYER_UNIT_SPELL_FINISH)

        TriggerAddCondition(trigger_h, Filter(function ()
            local eventId = GetTriggerEventId()

            if handler_t[eventId] then
                local handler = handler_t[eventId][GetSpellAbilityId()]

                if handler then
                    local global_handler = handler_t[eventId][0]
                    if global_handler then global_handler:execute() end

                    handler:execute()
                end
            end
        end))
    end)

end


Example Usage
Lua:
do
    local SPELL_ID = FourCC('A000')
    local SPELL_EVENT_TYPE = EVENT_PLAYER_UNIT_SPELL_EFFECT

    RegisterSpellEventHandler(SPELL_ID, SPELL_EVENT_TYPE, function ()
        local u = GetTriggerUnit()
        local x = GetSpellTargetX()
        local y = GetSpellTargetY()

        AddSpecialEffect(...)

        ... -- <Spell actions here>

    end)
end


v1.0.0
- Initial release
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I've made some major changes to this library here, including trimming down quite a bit on the public API:

Lua:
--[[ spelleventhandler.lua v2.0.0 by AGD | https://www.hiveworkshop.com/threads/330661/
    
    Description:
    
        Based on the resource RegisterSpellEffectEvent
        (https://www.hiveworkshop.com/threads/187193/), but with added
        flexibility and functionality. In particular, this allows you to
        register more than one handler function for every ability. This
        allows for a more advanced spellmaking.
        
        Moreover, this also supports all the spell events namely,
        EVENT_PLAYER_UNIT_SPELL_CHANNEL, EVENT_PLAYER_UNIT_SPELL_CAST,
        EVENT_PLAYER_UNIT_SPELL_EFFECT, EVENT_PLAYER_UNIT_SPELL_ENDCAST,
        and EVENT_PLAYER_UNIT_SPELL_FINISH. This is important in many
        cases, especially considering how common channeling spells are.
    
    Requirements:

        - EventListener
            Link: https://www.hiveworkshop.com/threads/323354/
    
    API:
    
        function RegisterSpellEventHandler(abilId, eventType, handlerFunc)
            Arguments:
            - abilId
                > Rawcode of the activation ability, example: FourCC("A001")
                > if <nil | 0>, handlerFunc would run for ALL registered
                  ability ids
            - eventType
                > channel
                | cast   
                | effect 
                | endcast
                | finish 
                > if <nil>, registers/deregisters to/from ALL event types

            Return Value:
            - function
                > call this function to un-register the event handler.

]]--
do
-- Aliases
local eventTypes = {        
    channel = EVENT_PLAYER_UNIT_SPELL_CHANNEL,
    cast    = EVENT_PLAYER_UNIT_SPELL_CAST,
    effect  = EVENT_PLAYER_UNIT_SPELL_EFFECT,
    endcast = EVENT_PLAYER_UNIT_SPELL_ENDCAST,
    finish  = EVENT_PLAYER_UNIT_SPELL_FINISH
}

local events

---@param abilId integer
---@param eventStr string channel, cast, effect, endcast or finish
---@param handlerFunc function
---@return function call_this_to_deregister
function RegisterSpellEventHandler(abilId, eventStr, handlerFunc)
    if not handlerFunc then return end
    
    if abilId then
        if type(abilId) == "string" then
            abilId = FourCC(abilId)
        end
    else
        abilId = 0
    end
    if not events then
        events = {}
        local t = CreateTrigger()
    
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_FINISH)
        
        TriggerAddCondition(t, Filter(function()
            local event = events[GetTriggerEventId()]
            
            if event then
                local handler = event[GetSpellAbilityId()]
                
                if handler then
                    local anySpellEvent = events[0]
                    if anySpellEvent then anySpellEvent:execute() end

                    handler:execute()
                end
            end
        end))
    end
    if eventStr then
        local eventId
        if type(eventId) == "string" then
            eventId = eventTypes[eventStr]
        else
            eventId = eventStr --someone passed an actual event. Allow it discretely.
        end
        local event = events[eventId]
        
        if not event then
            event = {}
            if not event[abilId] then
                events[eventId][abilId] = EventListener()
            end
        end
        
        events[eventId][abilId]:register(handlerFunc)
        return function()
            events[eventId][abilId]:deregister(handlerFunc)
        end
    else
        for _,event in ipairs(eventTypes) do
            events[event][abilId]:register(handlerFunc)
        end
        return function()
            for _,event in ipairs(eventTypes) do
                events[event][abilId]:deregister(handlerFunc)
            end
        end
    end
end
end
 
Top