• 🏆 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] Function calls with GUI

Status
Not open for further replies.

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
An idea came to me yesterday, as I was in the process of bringing some of my vJass Spell System demo spells over to the Spell Event I'm making for Lua: create GUI functions using the GUI "Player Group" API (of course not actually using real player groups), instead of needing separate GUI triggers for different branches of functionality. This allows every component of a spell to exist within a single GUI trigger.

This requires Global Variable Remapper, as it is, after all, the future of GUI:

Lua:
OnLibraryInit("GlobalRemap", function()
    local inUse, oldFF
    --udg_ExampleFunc is a Player Group in GUI:
    GlobalRemap("udg_ExampleFunc", function() inUse=true end)
   
    oldFF = AddHook("ForForce", function(whichForce, whichFunc)
        if inUse then
            inUse=false
            --Do stuff with the user's function (e.g. start a new coroutine, cache it for an "onMissileImpact" type of event)
        else
            oldFF(whichForce, whichFunc)
        end
    end)
end)

Then, in GUI, it would look like:

  • ExampleTrigger
    • Events
    • Conditions
    • Actions
      • Player Group - Pick all players in ExampleFunc and do Actions
        • Loop - Actions
          • -------- Do stuff --------
I'd like to streamline the manufacturing of the Lua code, so there will likely be a new resource appearing in the code submissions forum which handles the GlobalRemap/AddHook API behind the scenes, making it so the user only needs to provide the name of the variable, then think about what they want to do with the GUI code.

Edit 20.09.2022: This now exists as [Lua] - Action (The future of GUI actions)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467

This is what I've currently got as a prototype generalized function creator for GUI:

Lua:
OnLibraryInit("GlobalRemap", function()
    local cached={}
    local old
    old=AddHook("ForForce", function(whichForce, whichFunc)
        if cached[whichForce] then
            cached[whichForce](whichFunc)
        else
            old(whichForce, whichFunc)
        end
    end)
    ---@param whichVar      string
    ---@param onCreate      fun(function)
    ---@param onArrayRef?   fun(index)
    function CreateGUIFunctionInterface(whichVar, onCreate, onArrayRef)
        if whichVar:sub(1,4)~="udg_" then
            whichVar="udg_"..whichVar
        end
        if onArrayRef then
            GlobalRemapArray(whichVar, function(index)
                onArrayRef(index)
                return whichVar
            end)
        else
            GlobalRemap(whichVar, function()return whichVar end)
        end
        cached[whichVar]=onCreate
    end
end)


Lua:
--[[
    Procedure v1.0 (beta) by Bribe

    What it does: Allows GUI to declare its own functions to be passed to a Lua system.

    Why it can benefit: Allows you to have all of your functions in one GUI trigger, each with their own independent "waits" if desired.

    How it works: Situationally replaces ForForce, allowing you to manipulate the callback function instead.
--]]
OnLibraryInit("GlobalRemap", function()
    Procedure={}
    local procedures={}
    local oldForForce,lastIndex
    oldForForce=AddHook("ForForce", function(whichForce, whichFunc)
        if procedures[whichForce] then
            local index=lastIndex
            lastIndex=nil
            procedures[whichForce](whichFunc, index)
        else
            oldForForce(whichForce, whichFunc)
        end
    end)

    ---@param whichVar      string          --The name of the user-defined global. It will add the udg_ prefix if you don't feel like adding it yourself.
    ---@param onForForce    fun(function)   --Takes the GUI function passed to ForForce and allows you to do whatever you want with it instead.
    ---@param isArray?      boolean         --If true, will pass the array index to the onForForce callback.
    function Procedure.create(whichVar, onForForce, isArray)
        if whichVar:sub(1,4)~="udg_" then
            whichVar="udg_"..whichVar
        end
        if isArray then
            GlobalRemapArray(whichVar, function(index)
                lastIndex=index
                return whichVar
            end)
        else
            GlobalRemap(whichVar, function()
                lastIndex=nil
                return whichVar
            end)
        end
        procedures[whichVar]=onForForce
    end
    local durations=setmetatable({},{__mode="k"})
    local getCoroutine=coroutine.running
    Procedure.create("udg_Loop_forDuration", function(func)
        local co = getCoroutine()
        if durations[co] then
            while durations[co] > 0 do
                func()
            end
        end
    end)
    GlobalRemap("udg_Loop_duration", function() return durations[getCoroutine()] end, function(val) durations[getCoroutine()] = val end)
    
    ---Nearly the same as Precise PolledWait, with the exception that it tracks the duration.
    ---@param duration real
    function Procedure.wait(duration)
        local co = getCoroutine()
        if co then
            local t = CreateTimer()
            TimerStart(t, duration, false, function()
                DestroyTimer(t)
                if durations[co] then
                    durations[co] = durations[co] - duration
                end
                coroutine.resume(co)
            end)
            coroutine.yield(co)
        end
    end
    GlobalRemap("udg_Loop_wait", nil, Procedure.wait)

    ---Allows the function to be run as a coroutine a certain number of times (determined by the array index)
    ---Most importantly, the calling function does not wait for these coroutines to complete (just like in GUI when you execute a trigger from another trigger).
    ---@param func function
    ---@param count integer
    Procedure.create("udg_Loop_actions", function(func, count)
        for _=1,count do
            coroutine.resume(coroutine.create(func))
        end
    end, true)
end)
 
Last edited:
Status
Not open for further replies.
Top