• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!
  • ✅ The POLL for Hive's Texturing Contest #34 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!
  • ✅ The POLL for Hive's Techtree Contest #20 is OPEN! Vote for the TOP 3 FACTIONS! 🔗Click here to cast your vote!

[Lua] Construction Event Listeners

Level 1
Joined
Jul 1, 2026
Messages
2
Hello everyone,
I am in a project with friends and I am having trouble rooting out a problem in some code. The code is designed to register listeners for construction events, such as EVENT_PLAYER_UNIT_CONSTRUCTION_FINISH, etc. It stores these listeners by unitTypeId then by eventId. I've narrowed it down to when the unitTypeAndEventRegistered function is called, the type for listenersByEvent returns as a table as expected but the type for the inner index (listenersByEvent[whichUnitType]) returns as nil, it is supposed to return a function. I apologize in advance for messy code, I am still relatively new to Lua and programming in general as well as there are things I've done to this code for debugging purposes. This code may change in the future for a more generalized event handler but for now this is what I have as I learn.

Lua:
do
    local constructionEventInfo = {
        constructCancel = {
            id = EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL,
            name = "Construction Cancel",
        },
        constructFinish = {
            id = EVENT_PLAYER_UNIT_CONSTRUCT_FINISH,
            name = "Construction Finish",
        },
        constructStart = {
            id = EVENT_PLAYER_UNIT_CONSTRUCT_START,
            name = "Construction Start",
        },
    }

    local ConstructionEventListeners = {}
    local listenerCount = 0

    local function unitTypeAndEventRegistered(whichUnitTypeId, whichEventId)
        if listenerCount < 1 then return false end
        local listenersByEvent = ConstructionEventListeners[whichUnitTypeId]
        if type(listenersByEvent) == "table" and type(listenersByEvent[whichEventId]) == "function" then
            return true
        end
        return false
    end

    function RegisterConstructionEventListener(whichUnitTypeId, listener, eventId)
        if unitTypeAndEventRegistered(whichUnitTypeId, eventId) then
            return
        end

        if ConstructionEventListeners[whichUnitTypeId] == nil then
            ConstructionEventListeners[whichUnitTypeId] = {}
        end

        ConstructionEventListeners[whichUnitTypeId][eventId] = listener
        listenerCount = listenerCount + 1
    end

    local function onConstructionEvent()
        local eventId = GetTriggerEventId()
        local unit = GetTriggerUnit()
        local unitTypeId = GetUnitTypeId(unit)

        local eventData = {
            constructUnit = unit,
        }

        if unitTypeAndEventRegistered(unitTypeId, eventId) then
            BJDebugMsg("Firing listener for " .. GetUnitName(unit) .. ", id: " .. I2S(unitTypeId))
            ConstructionEventListeners[unitTypeId][eventId](eventData)
        end
    end

    function InitializeConstructionTrigger()
        local constructEventTrigger = CreateTrigger()

        for i = 0, bj_MAX_PLAYERS - 1 do
            for _, constructEvent in pairs(constructionEventInfo) do
                TriggerRegisterPlayerUnitEvent(constructEventTrigger, Player(i), constructEvent.id, nil)
            end
        end

        TriggerAddAction(constructEventTrigger, onConstructionEvent)
    end
end
Lua:
do
    local MasonryBuffConfig = {
        workshopTypeId = FourCC('sk20'),
        masonryBuffId = FourCC('as01'),
        --masonryBuffRepairId = FourCC('sa02')
    }
    local function addMasonryBuffs(whichUnit)
        UnitAddAbility(whichUnit, MasonryBuffConfig.masonryBuffId)
        --UnitAddAbility(whichUnit, MasonryBuffConfig.masonryBuffRepairId)
    end
    local function onConstructFinish(eventData)
        BJDebugMsg("MasonryBuff construct listener fired for " .. GetUnitName(eventData.constructUnit))
        addMasonryBuffs(eventData.constructUnit)
    end
    function InitializeMasonryBuff()
        BJDebugMsg("InitializeMasonryBuff start")
        local group = CreateGroup()
        GroupEnumUnitsInRect(group, GetPlayableMapRect(), nil)
        local unit = FirstOfGroup(group)
        while unit ~= nil do
            if GetUnitTypeId(unit) == MasonryBuffConfig.workshopTypeId then
                BJDebugMsg("Found pre-existing workshop: " .. GetUnitName(unit))
                addMasonryBuffs(unit)
            end
            GroupRemoveUnit(group, unit)
            unit = FirstOfGroup(group)
        end
        DestroyGroup(group)
        BJDebugMsg("Registering MasonryBuff construction listener for id:" .. I2S(MasonryBuffConfig.workshopTypeId))
        RegisterConstructionEventListener(MasonryBuffConfig.workshopTypeId, onConstructFinish, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
        
    end
end
 
I've encountered this type of problem before while dabbling in Lua. EVENT_PLAYER_UNIT_CONSTRUCT_FINISH is a playerunitevent which extends eventid, but GetTriggerEventId() returns eventid. In JASS, this would be completely fine, but not for Lua.

Nowadays, it's considered to be a no-no to use GetHandleId() in Lua, but I think you can cast eventId into a player unit event via ConvertPlayerUnitEvent(GetHandleId(eventId)) in the local function onConstructionEvent() and use that instead as your proper eventId.
 
I've encountered this type of problem before while dabbling in Lua. EVENT_PLAYER_UNIT_CONSTRUCT_FINISH is a playerunitevent which extends eventid, but GetTriggerEventId() returns eventid. In JASS, this would be completely fine, but not for Lua.

Nowadays, it's considered to be a no-no to use GetHandleId() in Lua, but I think you can cast eventId into a player unit event via ConvertPlayerUnitEvent(GetHandleId(eventId)) in the local function onConstructionEvent() and use that instead as your proper eventId.
That indeed fixed it, I appreciate it MyPad. I can see GetHandleId() not being needed in Lua because Lua tables are much more flexible, making handle ids obsolete, is that the only reason, or is there another reason why it's a no-no?
 
Back
Top