• 🏆 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!

Can you explain me how works Lua?

Status
Not open for further replies.
Level 24
Joined
Jun 26, 2020
Messages
1,852
I'm getting started in Lua after 4 months of learning Jass and vJass, I know some basics things like I can't use the words "call", "set", "takes" or "returns", for code functions I can do it inside of the current function, the IDs needs FourCC and all the variables can have any type of value, I will ask you what I need when I consider it necessary, to start I wanna ask how to init a trigger, and to reffer it in another trigger do I need create a global variable for it?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Lua:
function CreateTriggerExample()

    local trig = CreateTrigger() -- local variable storing the trigger
    TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_CAST)
    TriggerAddAction(trig, function () -- with lua you don't even need to create a function outside of this one for the Conditions/Actions
        local u = GetTriggerUnit()
        KillUnit(u)
    end)

    myGlobalTrig = CreateTrigger() -- global variable storing the trigger
    TriggerRegisterAnyUnitEventBJ(myGlobalTrig, EVENT_PLAYER_UNIT_SPELL_CAST)
    TriggerAddAction(trig, function () -- with lua you don't even need to create a function outside of this one for the Conditions/Actions
        local u = GetTriggerUnit()
        KillUnit(u)
    end)

end

function SomeOtherFunction()
    DestroyTrigger(myGlobalTrig)
end
It's basically the same thing as Jass but with less limitations.

So just call CreateTriggerExample() and the above triggers will be created. If you wanted to destroy your trigger at a later time or in another function you can store it in a global variable and reference it like you normally would.

The nice thing about Lua is that you can do everything inside of a single function (no need for a separate callback function and dealing with globals to pass information between the two).

You can make much better use of local variables since these can be referenced more easily. For example:
Lua:
function OtherExample()

    local u = CreateUnit(id, unitid, x, y, face) -- i'm creating a unit outside of our trigger

    local trig = CreateTrigger()
    TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_CAST)
    TriggerAddAction(trig, function ()
        KillUnit(u) -- and i'm referencing the created unit inside of the actions of this trigger
    end)

end
Then for making everything easier to initialize you can use this: [Lua] Global Initialization
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,852
Thank you for answer.
You can make much better use of local variables since these can be referenced more easily. For example:
Wow, so its even better than I thought
Then for making everything easier to initialize you can use this: [Lua] Global Initialization
It is not clear to me what I have to do, I just have to add this code or I have to do this manually?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
To be honest I never bothered using Global Initialization. I would create a function that runs all of my other functions, and call it in the trigger editor:
  • Events:
  • Game - Elapsed game time is 0.50 seconds
  • Actions:
  • Custom script: GameSetup()
So I'd have a function called GameSetup which called of my other functions.
Lua:
function GameSetup()
    SetupUnits()
    SetupPlayers()
    SetItems()
    -- etc
end
Maybe the newbie way of doing it but it works.
 
Level 24
Joined
Jun 26, 2020
Messages
1,852
To be honest I never bothered using Global Initialization. I would create a function that runs all of my other functions, and call it in the trigger editor:
  • Events:
  • Game - Elapsed game time is 0.50 seconds
  • Actions:
  • Custom script: GameSetup()
So I'd have a function called GameSetup which called of my other functions.
Lua:
function GameSetup()
    SetupUnits()
    SetupPlayers()
    SetItems()
    -- etc
end
Maybe the newbie way of doing it but it works.
Je, so I will ask them XD
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
Not sure about Libraries but Lua uses Tables for just about everything. You should read through the Lua manual and look into it (v5.3).

Here's a Table that mimics a Struct:
Lua:
person = {
  age = 18,
  name = "bob",
  height = "5'10",
}

print(person.age, person.name, person.height) -- display our persons age, name, and height
Also, there's no reason you have to code in pure Lua, check this out: [General] - is JESP still a thing?

I'm currently coding my maps in c# -> Orden4/WCSharp
 
Level 24
Joined
Jun 26, 2020
Messages
1,852

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
One more thing, what does "do"?
Here's what Do is used for in Lua:
Why use a do-end block in Lua?

I was under the impression it would just DO the code inside, like call it automatically, but I was wrong.

I recommend taking a look at Tasyen's code, for example check out this resource of his: TasItemshop

It looks like he Hooks into some of Blizzard's functions like InitBlizzard() and has his functions run then.
Lua:
do
    local real = InitBlizzard
    function InitBlizzard()
        real()
        -- put your code here:
        print("Hello World")
    end
end

Again, he's Hooking into an existing Blizzard function MarkGameStarted(), he does this to avoid Desyncs that seem to happen when code is run before all players have finished loading:
Lua:
-- in 1.31 and up to 1.32.9 PTR (when I wrote this) Frames are not correctly saved and loaded, breaking the game.
-- This runs all functions added to it with a 0s delay after the game has loaded.
do
    local data = {}
    local real = MarkGameStarted
    local timer
 
    function FrameLoaderAdd(func)
        table.insert(data, func)
    end

    function MarkGameStarted()
        real()
        local trigger = CreateTrigger()
        timer = CreateTimer()
        TriggerRegisterGameEvent(trigger, EVENT_GAME_LOADED)
        TriggerAddAction(trigger, function()
            TimerStart(timer, 0, false, function()
                xpcall(function()
                for _,v in ipairs(data) do v() end
                end, print)
            end)
        end)
    end
end

I read some more about Global Initialization and I think it causes Desyncs at the moment due to an issue with Pairs. "Currently the iteration with pairs is not synced in multiplayer."

So I would try the above solutions that Taysen has been using in his resources (he seems to understand all of this better than anyone else). Or, maybe Bribe has a new version of Global Initialization that doesn't use Pairs.
 
Level 24
Joined
Jun 26, 2020
Messages
1,852
Not sure about Libraries but Lua uses Tables for just about everything. You should read through the Lua manual and look into it (v5.3).

Here's a Table that mimics a Struct:
Lua:
person = {
  age = 18,
  name = "bob",
  height = "5'10",
}

print(person.age, person.name, person.height) -- display our persons age, name, and height
Also, there's no reason you have to code in pure Lua, check this out: [General] - is JESP still a thing?

I'm currently coding my maps in c# -> Orden4/WCSharp
After learning a lot of things about lua, I couldn't find the way to do a struct like in vJass, and your example is a determinated table and not a struct that I can use as a variable, or Am I wrong?, if not, can you help me to know how to do a struct like vJass?
 
Level 18
Joined
Oct 17, 2012
Messages
821
You could code something like this for structs in Lua.
Lua:
object = {}

function object.new()
   -- Create a table
   local self = {}
 
   -- Methods
   function self.set(field, value)
       self[field] = value
   end
   function self.get(field)
       return self[field]
   end

   return self
end

-- Testing
spell = object.new()
spell.set('nfcc', 50)
print(spell.get('nfcc'))
Lua:
object = {}

-- Methods
function object.new()
   local o = {}
   setmetatable(o, self)
   self.stack[o] = o
   return o
end
function object.set(field, value)
   -- self here refers to the struct instance
   self[field] = value
end
function object.get(field)
   return self[field]
end

-- Test
spell = object.new()
spell.set('nfcc', 10)
print(spell.get('nfcc'))
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,852
@GhostHunter123 thank you, but I tried more and I got the way to do it and I translated an struct I made in vJass (I though I had posted explaining it but well) I will share you to you can check it if is ok.
JASS:
library Revive initializer Init requires TimerUtils

globals
    integer array Revive_Instance
endglobals

struct Revive
   
    unit hero
    timer revive
    timerdialog clock
   
    static method create takes unit hero returns thistype
        local thistype this=thistype.allocate()
        set this.hero=hero
        set this.revive=NewTimerEx(this)
        call TimerStart(this.revive,15.00+5.00*I2R(R2I(SquareRoot(I2R(GetHeroLevel(hero))))),false,function thistype.end_revive)
        set this.clock=CreateTimerDialog(this.revive)
        call TimerDialogSetTitle(this.clock,"You revive in: ")
        if LocalPlayer==GetOwningPlayer(hero) then
            call TimerDialogDisplay(this.clock,true)
        endif
        return this
    endmethod
   
    static method end_revive takes nothing returns nothing
        local Revive this=GetTimerData(GetExpiredTimer())
        call this.destroy()
    endmethod
   
    method destroy takes nothing returns nothing
        local location l
        call DestroyTimerDialog(this.clock)
        call ReleaseTimer(this.revive)
        if IsPlayerInForce(GetOwningPlayer(this.hero),udg_Grupo_Draeneanos) then
            set l=GetRandomLocInRect(udg_Draeneanos)
        else
            set l=GetRandomLocInRect(udg_Demonios)
        endif
        call ReviveHeroLoc(this.hero,l,true)
        call RemoveLocation(l)
        set l=null
        set this.hero=null
        set this.revive=null
        set this.clock=null
        call this.deallocate()
    endmethod
endstruct

private function Conditions_borrar takes nothing returns boolean
    return IsUnitType(udg_UDexUnits[udg_UDex],UNIT_TYPE_HERO)
endfunction

private function Actions_borrar takes nothing returns nothing
    local Revive t=Revive_Instance[udg_UDex]
    call t.destroy()
endfunction

//-- --

private function Conditions takes nothing returns boolean
    return IsUnitType(GetDyingUnit(),UNIT_TYPE_HERO)
endfunction

private function Actions takes nothing returns nothing
    set Revive_Instance[GetUnitUserData(GetDyingUnit())]=Revive.create(GetDyingUnit())
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerRegisterVariableEvent(t,"udg_UnitIndexEvent",EQUAL,2.00)
    call TriggerAddCondition(t,Condition(function Conditions_borrar))
    call TriggerAddAction(t,function Actions_borrar)
    set t=null
    //-- --
    set gg_trg_Si_mueres_Heroe=CreateTrigger()
    call DisableTrigger(gg_trg_Si_mueres_Heroe)
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Si_mueres_Heroe,EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(gg_trg_Si_mueres_Heroe,Condition(function Conditions))
    call TriggerAddAction(gg_trg_Si_mueres_Heroe,function Actions)
endfunction
endlibrary
Lua:
do
    gg_trg_Si_mueres_Heroe=CreateTrigger()
    Revive_Instance={}

    Revive={

        end_revive=function()
            Revive.destroy(GetTimerData())
        end,
       
        destroy=function(this)
            local l
            DestroyTimerDialog(this.clock)
            ReleaseTimer(this.revive)
            if IsPlayerInForce(GetOwningPlayer(this.hero),udg_Grupo_Draeneanos) then
                l=GetRandomLocInRect(udg_Draeneanos)
            else
                l=GetRandomLocInRect(udg_Demonios)
            end
            ReviveHeroLoc(this.hero,l,true)
            RemoveLocation(l)
            l=nil
            this.hero=nil
            this.revive=nil
            this.clock=nil
            this=nil
        end,
       
        create=function(hero)
            local this={}
            this.hero=hero
            this.revive=NewTimer(this)
            TimerStart(this.revive,15.00+5.00*I2R(R2I(SquareRoot(I2R(GetHeroLevel(hero))))),false,Revive.end_revive)
            this.clock=CreateTimerDialog(this.revive)
            TimerDialogSetTitle(this.clock,"You revive in: ")
            if GetLocalPlayer()==GetOwningPlayer(hero) then
                TimerDialogDisplay(this.clock,true)
            end
            return this
        end
    }
   
    --===========================================================================
    function Init_Revive()
        local trigger t=CreateTrigger()
        TriggerRegisterVariableEvent(t,"udg_UnitIndexEvent",EQUAL,2.00)
        TriggerAddCondition(t,Condition(function()
            return IsUnitType(udg_UDexUnits[udg_UDex],UNIT_TYPE_HERO)
        end))
        TriggerAddAction(t,function()
            Revive.destroy(Revive_Instance[udg_UDex])
        end)
        t=nil
        ---- --
        TriggerRegisterAnyUnitEventBJ(gg_trg_Si_mueres_Heroe,EVENT_PLAYER_UNIT_DEATH)
        TriggerAddCondition(gg_trg_Si_mueres_Heroe,Condition(function()
            return IsUnitType(GetDyingUnit(),UNIT_TYPE_HERO)
        end))
        TriggerAddAction(gg_trg_Si_mueres_Heroe,function()
            print("Si")
            Revive_Instance[GetUnitUserData(GetDyingUnit())]=Revive.create(GetDyingUnit())
        end)
        udg_Draeneanos=gg_rct_Region_001
        udg_Demonios=gg_rct_Region_000
    end
end
 
Level 20
Joined
Jul 10, 2009
Messages
477
If you mean to remove all elements from Table, while still being able to add new elements to it afterwards, you would normally write Table = {}, i.e. set the variable to a fresh new table (and the old table is garbage colleced automatically). Alternatively, you can loop over it and set all keys to nil.

If you mean to remove the table from memory (after you used it for the last time), then you rely on the garbage collector.
The garbage collector only works, if there is no reference left to the table.
That means, if Table is a global variable, yes, it needs to be nilled to make the garbage collector remove it from memory.
If it's used locally (using the local keyword inside any scope block, e.g. a function), then no, you don't need to write Table = nil, as the reference vanishes at the end of the scope block anyway.

Again, I'd like to recommend the same book as I did here.
It contains answers to all questions you ask.
 
Level 24
Joined
Jun 26, 2020
Messages
1,852
If you mean to remove all elements from Table, while still being able to add new elements to it afterwards, you would normally write Table = {}, i.e. set the variable to a fresh new table (and the old table is garbage colleced automatically). Alternatively, you can loop over it and set all keys to nil.

If you mean to remove the table from memory (after you used it for the last time), then you rely on the garbage collector.
The garbage collector only works, if there is no reference left to the table.
That means, if Table is a global variable, yes, it needs to be nilled to make the garbage collector remove it from memory.
If it's used locally (using the local keyword inside any scope block, e.g. a function), then no, you don't need to write Table = nil, as the reference vanishes at the end of the scope block anyway.

Again, I'd like to recommend the same book as I did here.
It contains answers to all questions you ask.
I nice know that, and I said I'm not much of read, but I will try, thanks.
Unfortunately no, only the normal loops "for i =0 do" type of stuff.
That's sad.
 
Trokkin did some testing regarding speed of natives vs Lua and shared his results LUA tests and benchmarks .

The trend for the Lua vm is. It is more taxing to communicate with the game then talking with itself.
But one does not change the game when Lua vm just talks with itself. If you code ever shall have any effect onto the game you will need to use a native even if that is slower.
 
Level 20
Joined
Jul 10, 2009
Messages
477
If the problem is for use k,v in pairs; so there is no problem, it is?
That's right. Using units as an index can only desync, if you iterate over the table via pairs.
If you just use the table to store and read data without iteration, it will not desync.

Also, SyncedTables prevent Desyncs and are safe to iterate, even when you use units as index.
 
Level 24
Joined
Jun 26, 2020
Messages
1,852
That's right. Using units as an index can only desync, if you iterate over the table via pairs.
If you just use the table to store and read data without iteration, it will not desync.

Also, SyncedTables prevent Desyncs and are safe to iterate, even when you use units as index.
Yes, that's why I said there is no problem, because I already use it.
 
Status
Not open for further replies.
Top