• 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.
  • Create a faction for Warcraft 3 and enter Hive's 19th Techtree Contest: Co-Op Commanders! Click here to enter!
  • Get your art tools and paintbrushes ready and enter Hive's 34th Texturing Contest: Void! Click here to enter!

[Lua] Calling functions inside a timer

Oli

Oli

Level 4
Joined
Aug 9, 2015
Messages
34
Hi,

I have been struggling with the following code:

Lua:
...
    local shipsmoving = CreateGroup()
...
    function naval_movement_start()
        TimerStart(CreateTimer(), 0.1, true, ForGroupBJ(shipsmoving, function()
            local u = GetEnumUnit()
            naval_movement_accelerate(u)
            naval_movement(u)
            end))
    end

    function naval_movement_order()
        local u = GetTriggerUnit()
        GroupAddUnitSimple(u, shipsmoving)
        naval_movement_start()
    end

    function naval_movement(u)
        local cv = GetUnitUserData(u)
        SetUnitPositionLoc(u, PolarProjectionBJ(GetUnitLoc(u), RVM.actualspeed[cv], GetUnitFacing(u)))
        print("dog1")
    end

    function naval_movement_accelerate(u)
        local cv = GetUnitUserData(u)
        if RVM.actualspeed[cv] ~= RVM.maxspeed[cv] then
            RVM.actualspeed[cv] = RVM.actualspeed[cv] + RVM.acceleration[cv]
        end
    end
    
    OnMapInit(function ()
        local trig = CreateTrigger()
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
        TriggerAddAction(trig, naval_movement_order)
    end)
...

In the current state the timer runs only once each time a unit is moved. Only a part of the script is shown, I believe the cause is related with me coding the timer wrong.
May I ask for help?
 
Level 7
Joined
Jun 30, 2017
Messages
54
You're attempting to pass the return value of ForGroupBJ call as a timer callback function. ForGroupBJ has no return value, so you're effectively doing nothing with the timer.
You're calling ForGroupBJ exactly once (when the unit order event occurs).

What you have to do is define a callback function for the timer (it must not expect any arguments) and within that callback function you can call ForGroupBJ.
Try the following:

Lua:
function naval_movement_start()
    TimerStart(CreateTimer(), 0.1, true, function()
        ForGroupBJ(shipsmoving, function()
            local u = GetEnumUnit()
            naval_movement_accelerate(u)
            naval_movement(u)
        end)
    end)
end
 

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,942
Adding onto what Insanity said, here's some cleaned up code which might serve you better:
Lua:
    local shipsmoving = CreateGroup()
    local shipstimer = nil

    function naval_movement_order()

        -- Note: If the ordered unit is already in the group then it won't be added again
        -- But it's probably wise to add a more efficient check before calling this function
        GroupAddUnit(shipsmoving, GetTriggerUnit())

        -- Exit early if the timer is already running since we don't
        -- want to create a new timer or restart an existing timer
        if shipstimer ~= nil then
            return
        end

        shipstimer = CreateTimer()
        TimerStart(shipstimer, 0.1, true, function()
            ForGroup(shipsmoving, function()
                local u = GetEnumUnit()
                local cv = GetUnitUserData(u)
                RVM.actualspeed[cv] = RVM.actualspeed[cv] + RVM.acceleration[cv]
                if RVM.actualspeed[cv] > RVM.maxspeed[cv] then
                    RVM.actualspeed[cv] = RVM.maxspeed[cv]
                end
                set_unit_offset_position(u, RVM.actualspeed[cv], GetUnitFacing(u))
            end)
        end)
    end

    function naval_movement_stop()
        -- Note: This function will kill the timer and stop moving the ships
        if shipstimer == nil then
            return
        end
        PauseTimer(shipstimer)
        DestroyTimer(shipstimer)
        GroupClear(shipsmoving)
        shipstimer = nil
    end

    function set_unit_offset_position(u, offset, angle)
        local x = GetUnitX(u)
        local y = GetUnitY(u)
        local rad = angle * bj_PI / 180.0
        local newX = x + offset * Cos(rad)
        local newY = y + offset * Sin(rad)
        SetUnitPosition(u, newX, newY)
    end
 
    OnMapInit(function()
        local trig = CreateTrigger()
        TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
        TriggerAddAction(trig, naval_movement_order)
    end)
Of course I'm not 100% sure what you were trying to achieve so I could have made some mistakes. Anyway, this code avoids the memory leak from using GetUnitLoc(u), it avoids all BJ methods, and fixes your flawed logic when calculating speed "RVM.actualspeed[cv] ~= RVM.maxspeed[cv]", you shouldn't be making exact comparisons like that when working with Reals (floating point error), and if these are Integers it still seems incorrect since acceleration could bypass maxspeed and continue infinitely.
 
Last edited:
Top