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

First attempt at converting Jass to Lua... FAIL

Status
Not open for further replies.
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there, I hope you are all fine.

So I decided to do the jump and start converting my map to Lua, trigger by trigger.

But before I even go further, I cannot even manage to get my triggers to run.

I have made a GUI trigger :
  • Lua Init
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Custom script: Init()
Note I have also tried using Map Initialization as event without any better luck.

Init() is my old Jass Map Initialization trigger that I converted :
Lua:
function Init()

    InitializeGlobals()

    -- Map configuration    
    DoNotSaveReplay()
    SetMapFlag(MAP_LOCK_SPEED, true)
    SetMapFlag(MAP_USE_HANDICAPS, true)
    SetFloatGameState(GAME_STATE_TIME_OF_DAY, 18.00)
    SuspendTimeOfDay(true)
    ClearMapMusic()
    SetCreepCampFilterState(false)
    SetPlayerName(BallsMaster, "Balls Master")
    SetPlayerName(Player(PLAYER_NEUTRAL_PASSIVE), "Ether")
    SetPlayerState(BallsMaster, PLAYER_STATE_GIVES_BOUNTY, 1)
    RemoveAllGuardPositions(BallsMaster)

    -- Hide standard UI
    BlzHideOriginFrames(true)
    BlzEnableUIAutoPosition(false)
    BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_BOTTOM, 0.0, -0.24)
    BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop", 0), false)
    CameraZoomSlider()

    -- Initialize Custom Triggers

    -- Game
    -- DifficultyDialogs()
    -- APlayerLeaves()
    -- HostAndPlayersControls()

    -- Levels
    -- EndLevel()

    -- Towers and Heroes
    -- Advanced_Sell()
    -- SFXOnUpgrade()
    -- RangeIndicate()
    -- SetupOnBuildOrUpgrade()
    -- BuildBlockingTowersWall()
    -- ReorderBallAfterTowerKilled()

    -- Custom Spells
    -- AutoBlinkSpell()
    -- FixRageEffect()
    -- SetAutoCastState()
    -- FlashForwardSpell()
    -- GoBackHomeSpell()
    -- ElectricalDisruptionSpell()
 
    HandleCount()

    -- End of Map Init
    -- TimerStart(MapInitFinished, 0.00, false, function AfterInit)
end
Of course I have commented out all the disabled triggers that I haven't converted yet.

But these 2 converted triggers do not run :
- CameraZoomSlider()
- HandleCount()

In fact the whole Init() function doesn't seem to run at all...

Here are these triggers :

CameraZoomSlider() :
Lua:
function CameraZoomSlider()
    local cameraZoomSlider=CreateTrigger()
    local verticalSlider=BlzCreateFrameByType("SLIDER", "distance", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "QuestMainListScrollBar", 0)
    local sliderToolTip=BlzCreateFrameByType("TEXT", "SliderTitle", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
    local color= BlzConvertColor(255, 255, 255, 0)

    -- Set parametees for the Slider...
    BlzFrameSetSize(verticalSlider, 0.016, 0.12)
    BlzFrameSetAbsPoint(verticalSlider, FRAMEPOINT_BOTTOMLEFT, 0.002, 0.0)
    BlzFrameSetMinMaxValue(verticalSlider, 100, 4000)
    BlzFrameSetValue(verticalSlider, 2600)
    BlzFrameSetStepSize(verticalSlider, 10)

    -- ... and for the sliderToolTip
    BlzFrameSetTextColor(sliderToolTip, color)
    BlzFrameSetText(sliderToolTip, "USE THIS SLIDER TO CONTROL THE CAMERA ZOOM")
    BlzFrameSetScale(sliderToolTip, 1.60)
    BlzFrameSetAbsPoint(sliderToolTip, FRAMEPOINT_BOTTOMLEFT, 0.0, 0.14)
    BlzFrameSetTooltip(verticalSlider, sliderToolTip)

    -- Set Initial camera distance for all players
    SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 2600, 0)
 
    -- Add event and actions in conditions to trigger for the Slider
    BlzTriggerRegisterFrameEvent(cameraZoomSlider, verticalSlider, FRAMEEVENT_SLIDER_VALUE_CHANGED)
    TriggerAddCondition(cameraZoomSlider, function()
        local distance=BlzGetTriggerFrameValue()
        local farZ=distance+2100
    
        if (GetTriggerPlayer()==GetLocalPlayer()) then
            SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, distance, 0)
            SetCameraField(CAMERA_FIELD_FARZ, farZ, 0 )
            SetTerrainFogEx(0, farZ, farZ, 0, 1.00, 1.00, 1.00)
        end
        return false
    end)

    -- niling
    verticalSlider=nil
    sliderToolTip=nil
    cameraZoomSlider=nil
end
HandleCount() :
Lua:
function HandleCount()
    local hC=CreateTrigger()

    TriggerRegisterTimerEvent(hC, 2.00, true)
    TriggerAddCondition(hC, function ()
        local t=CreateTimer()

        BJDebugMsg("Current handle count = "..(GetHandleId(t)-0x100000))
        DestroyTimer(t)
        t=nil
        return false
    end)
    hC=nill
end

So I have 2 questions :

- Why doesn't my custom script line "Init()" call the function Init() ???

- Are my 2 converted triggers OK when it comes to Lua syntax ?

Thanks for any help provided.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
1) You don't need to nil (I see you wrote nill in HandleCount) variables
2) Perhaps try changing the name of the Init() function
3) If you want to comment out multiple lines of code you can use:
Lua:
--[[
commented
code
goes
here
]]
4) Edit: I was half asleep or something when I posted that. Ignore this one.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there !
@Uncle : yes I know only some agents have to be nil-ed. I read attempts at custom garbage collectors can desync. Will sort this when my map will be in LUA and the time to optimize the code to make it a bit less "Jassy".
Thanks for the tip about multiple comment lines, I am still reaching the Lua reference manual but there is so much to read it is overwhelming. Especially as I am not yet fully accustomed to some of the advanced syntax features that are quite un-intuitive.
What do you mean about the event registration ? How could there be another way to register this specific event to a frame without the native ?
As for renaming Init() it seems @Planetary just checked that it was not the problem.

@Planetary : Yes indeed I should have done it.
So I followed your idea : I put a print("test") in every section of InitializeGlobals() and indeed thats where the problem is. So Init() is definitely executed

Now I can't find the problem with the faulty part of my globals initialization...
Here it is :
Lua:
    -- Spells and Incapacitating Buffs Setup
    SpellOrder={}
    SpellOrder[1]="roar"
    SpellOrder[2]="locustswarm"
    SpellOrder[3]="locustswarm"
    SpellOrder[4]="locustswarm"
    SpellOrder[5]="ensnare"
    SpellOrder[6]="web"
    SpellOrder[7]="faeriefire"
    SpellOrder[8]="curse"
    SpellOrderOn={}
    SpellOrderOn[6]="webon"
    SpellOrderOn[7]="faeriefireon"
    SpellOrderOn[8]="curseon"
    SpellOrderOff[6]="weboff"
    SpellOrderOff[7]="faeriefireoff"
    SpellOrderOff[8]="curseoff"
    SpellID={}
    SpellID[1]=FourCC("A00H") -- Rage
    SpellID[2]=FourCC("A0SG") -- Summon Gargoylings
    SpellID[3]=FourCC("A0SB") -- Swarm Of Butterglies
    SpellID[4]=FourCC("A0SP") -- Squad Of Pixies
    SpellID[5]=FourCC("A00J") -- Ensnare
    BuffID={}
    BuffID[1]=FourCC("B00D") -- Dazed
    BuffID[2]=FourCC("B00S") -- Shocked
    BuffID[3]=FourCC("BPSE") -- Electrocuted
    BuffID[4]=FourCC("Bcyc") -- In a Laredo Tornado
    BuffID[5]=FourCC("Bcy2") -- In a Laredo Tornado Extra
    BuffID[6]=FourCC("Bmlt") -- Magic Leash
    BuffID[7]=FourCC("Bena") -- Ensnared
    BuffID[8]=FourCC("Bwea") -- Webbed
    BuffID[9]=FourCC("BGBH") -- Go Back Home
    BuffID[10]=FourCC("Bcrs") -- Recently Flash Forwarded, this one is not incapacitating!

    FreeBlinkRange=800.00

Anyone sees what I have done wrong (the last test message displays just before that part) ?

EDIT : lol of course I had to forget one table initialization. Forget it i'll keep digging :)

EDIT 2 : Ok initialization of globals now work. But the 2 triggers i posted further up are not working at all. So @Uncle your are probably right about the event registration, although I don't know how to do it then.
Except.... I have no idea why the HandleCount trigger fails.

EDIT 3 :
I don't understand, I grabbed this line from on of Tasyen's tutorials in Lua:
Lua:
BlzTriggerRegisterFrameEvent(trigger, frameA, ConvertFrameEventType(index))
So thats definitely the right way the register a frame event. Now I don't know, is Lua unable to add actions in conditions ? If it is able to, what prevents both my triggers to work ?

EDIT 4 :
Oh well I ended up finding the problem :

Unlike in Jass, to add a function as a trigger condition, you can't use the function directly, but the Condition(function()....) instead.
Seems like I will hit many small walls like this in this enterprise.

This said, the ability to pass variables directly in the callback functions when putting them in the trigger registration will make many of my triggers so much easier. I won't need to save some agents or integers in hashtables anymore for a start.
I reckon it will be worth the effort !!!

Anyways guys thank you, your input helped a lot, once again !!!
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
I want to avoid recreating a thread, but I have an important question about passing variables to callback functions.
Would a code like this be acceptable ? :
Lua:
function StoppedBallActions(ballNb)
    local ball=nil
    local dest

    if (GetIssuedOrderId()==OrderId("stop")) then
        ball=GetOrderedUnit()
        dest=Destination[ballNb]
        IssuePointOrder(ball, "attack", WPx[dest], WPy[dest])
        ball=nil
    end
    return false
end

------------------------

-- action in another long trigger

    TriggerAddCondition(StoppedBall[ballNb], Condition(StoppedBallActions(ballNb)))

------------------------

I want to avoid putting the callback function directly in the other trigger as it is really big, and I have 3 callback functions to use, and this one is the only tiny one, both others are quite big.

So can I use this syntax or can I only use Condition(StoppedBallActions()) and retrieve that ballNb value the way I used to in Jass ?

Thanks for letting me know, this is an important point as I use really many callbacks in my map.

Problem is I can't test my map before all triggers have been converted from now on, because all these triggers strongly intricate.
 
Last edited:
Level 14
Joined
Feb 7, 2020
Messages
386
I want to avoid recreating a thread, but I have an important question about passing variables to callback functions.
Would a code like this be acceptable ? :
Lua:
function StoppedBallActions(ballNb)
    local ball=nil
    local dest

    if (GetIssuedOrderId()==OrderId("stop")) then
        ball=GetOrderedUnit()
        dest=Destination[ballNb]
        IssuePointOrder(ball, "attack", WPx[dest], WPy[dest])
        ball=nil
    end
    return false
end

------------------------

-- action in another long trigger

    TriggerAddCondition(StoppedBall[ballNb], Condition(StoppedBallActions(ballNb)))

------------------------

I want to avoid putting the callback function directly in the other trigger as it is really big, and I have 3 callback functions to use, and this one is the only tiny one, both others are quite big.

So can I use this syntax or can I only use Condition(StoppedBallActions()) and retrieve that ballNb value the way I used to in Jass ?

Thanks for letting me know, this is an important point as I use really many callbacks in my map.

Problem is I can't test my map before all triggers have been converted from now on, because all these triggers strongly intricate.
I don't think there's anything wrong with that as I've seen similar things done in JASS systems before. I suppose it depends on how modular you want all of the elements to be (which are going to be called in the stack anyways, so it probably comes down to an efficiency or efficiency trade-off argument that we probably don't need to worry about in modding WC3).

However, I don't think passing parameters within Condition() works in Lua (for reasons I don't yet understand).

In my experience, things aren't in context unless you use an anonymous function: Condition( function() return yourFunction(yourArgument) end )

I tested to verify:

Lua:
function TestCondition()

    local trig = CreateTrigger()
    local func = function() print("condition passed, test call") end
    local cond = function(name) return name == 'pass' end
    local name = 'pass'

    TriggerRegisterPlayerUnitEvent(trig, Player(0), EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
    TriggerAddCondition(trig, Condition( function() return cond(name) end ) )
    TriggerAddAction(trig, func)

    print("setup success, use flamestrike")

end
Attempting to use Condition( cond(name) ) would break the setup trigger at that line. Maybe someone else can explain what is needed to get back to terser code.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Thanks for testing this. This is really worthy information !

I have already converted 75% of my triggers, I expect a few basic mistakes I made and didn't notice yet, but overall I think I followed most of Lua's basic syntax.
Once done and all the mistakes fixed, I will probably see if I can improve anything with more advanced syntax or structures.

This language definitely has incredible flexibility, especially knowing how many of its features I am still unaware of.

And even if the Condition( cond(name) ) does not work as we'd like, to be honest, the simple fact of being able to remove all my hashtables as I can define my functions within TriggerAddCondition(), and also being able to use real for loops that Jass has deprived us from for years, is making my code so much cleaner it is a real pleasure.

Now @Tasyen has warned me about the downside of Lua's extreme type flexibility (some types not matching but of course Lua eats it all with no warning). But even If I spend several more days before getting the map back to full functionality, I am convinced the effort was worth it.

When all triggers will be finished, I will experiment more with functions and their arguments and see if I learn anything interesting.

Oh and of course thanks again for the simple idea of debug messages, sometimes the answer to your problems are so obvious that you miss them :) Thats exactly how I plan to trace potential problems with my code after the conversion.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
I don't think there's anything wrong with that as I've seen similar things done in JASS systems before. I suppose it depends on how modular you want all of the elements to be (which are going to be called in the stack anyways, so it probably comes down to an efficiency or efficiency trade-off argument that we probably don't need to worry about in modding WC3).

However, I don't think passing parameters within Condition() works in Lua (for reasons I don't yet understand).

In my experience, things aren't in context unless you use an anonymous function: Condition( function() return yourFunction(yourArgument) end )

I tested to verify:

Lua:
function TestCondition()

    local trig = CreateTrigger()
    local func = function() print("condition passed, test call") end
    local cond = function(name) return name == 'pass' end
    local name = 'pass'

    TriggerRegisterPlayerUnitEvent(trig, Player(0), EVENT_PLAYER_UNIT_SPELL_EFFECT, nil)
    TriggerAddCondition(trig, Condition( function() return cond(name) end ) )
    TriggerAddAction(trig, func)

    print("setup success, use flamestrike")

end
Attempting to use Condition( cond(name) ) would break the setup trigger at that line. Maybe someone else can explain what is needed to get back to terser code.
Wouldn't you need to declare name before referencing it in cond?
Also, @Macadamia, ignore what I was saying in my post about #4.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Oh don't worry, it still helped as it pushed me to read more about registering events and adding conditions with Lua.

I will see how many unexpected problems I will get once conversion is done, hoping not to hit too many "walls" in the process.

This said, having problems and ending up solving them is, by far, the best way to learn :)
 
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there !

Well I have finished the conversion.
Of course it does not work yet. Using your earlier advice, @Planetary , I am using debug prints everywhere.

First obstacle I hit is the fact that I realized some of my globals had not been initialized.

As the globals declaration is useless in Lua, I had removed my Init Globals section from my Jass script. But of course as I did not init all declared globals, some where left out ^^

I have also fixed quite a few other "hidden" errors.

Now I am hitting a strange blockage as I can't seem to get a function from Lua string library to work. here is the line of code :
Lua:
            PlayerName[i], bnetID=GetPlayerName(thePlayer).match("(.+)#(.+)")

I am trying to remove the # and battlenet ID from the player names to avoid polluting the multiboard. In Jass, I was using a loop to scan the name until I found the #.
I thought with Lua it would be much better so I grabbed this sort of regex to use with the string.match() function. As it returns 2 values, I used a local string called bnetID to retrieve the second part.

But using this line crashes the trigger thread. Any idea why ?

EDIT : I tried to alter the pattern, but it still crashes :
Lua:
    PlayerName[i]=GetPlayerName(thePlayer).match("(.+)#")

It has been a while I haven't worked with regular expressions, and these patterns are quite confusing to be honest...

EDIT 2 :
Wow ! The string library is not accessible from wc3 ???
this does also crash the thread :
Lua:
            bnetID="Macadamia#2166"
            print(bnetID.find("#"))
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
Have you tried using string.find and string.sub?

Here's something I threw together. Plug in the Player's Id (Indexed starting at 0) to "fix" their name. This cuts off everything starting from the #.
Lua:
function FixPlayerName(id)
    local s = GetPlayerName(Player(id))
    local hashtag = string.find(s, "#")
    local playername = string.sub (s, 1, hashtag-1)
    SetPlayerName(Player(id), playername)
    print("fixed name:", playername)
end
string.find returns an integer for the first "#" found (or whatever character you use)
string.sub returns a string starting from X and ending at Y

So in Uncle#1000, hashtag = 6, and playername = Uncle

Note that this will fail if you aren't on your bnet name since it NEEDS a # to work.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Yes, thats basically what I aimed to do when I realized the pattern was a bit of a pain in the neck

It seems we can't use the .find() directly on the variable, so I will try your way. I bet it will work this time ^^

I haven't yet faced the variable comparison wall Tasyen mentioned, I just hope things won't become too messy.
I believe converting from Jass to Lua is in fact quite problematic (but also a good way to learn) and it would probably be much better to start a project in Lua directly.
This said, Lua can become very complex syntax wise, so I suppose going through that process won't be a waste of time :)

Thanks, I'll come back to post an update on my progress, I really want this conversion to work 100% when completed, and I am aware I am far from being done with the potential problems.

EDIT :
Yes that was it, now I need to replace all my occurrences of myString.sub() .find() etc.. with the string.sub() .find() etc...
Thanks !!!!
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Some Update, now the string functions seem to work in the map.

But I have hit a wall in a totally different manner this time.
This is not too surprising as I converted all my triggers at once (or nearly), and sometimes adding debug messages only help partially.

I have noticed that some triggers crash and then no debug message can be displayed (even if they were executed before).

Now I have identified a trigger that behaves this way, but that does not crash the game though (I have already fixed a few crashing triggers).

Here is this trigger :
Lua:
function Citadel()
    print("test")
    TriggerRegisterEnterRegion(ReachCitadel, Citadel, nil)
    print("test")
    TriggerAddCondition(ReachCitadel, Condition(function ()
        local ball=GetEnteringUnit()
        local lostLives
        local b
        local p
     
        if (GetOwningPlayer(ball)==BallsMaster) then
            b=GetUnitUserData(ball)
            if (Destination[b]==50) then
                p=math.floor(((b-1)/BallsPerLevel)+1)
                TBallsLeft=TBallsLeft-1
                PBallsLeft=PBallsLeft-1
                -- DisableBallTriggers(b)
                -- PlayCleanSFX(SFXReachCitadel , GetUnitX(ball), GetUnitY(ball), 2.00, 0.00, 0, 1.00)
                -- PlayGlobalSound(LeakSnd,LeakSndDur)
                PauseUnit(ball, true)
                SetUnitInvulnerable(ball, true)
                ShowUnit(ball, false)
                SetUnitPosition(ball, StoreX, StoreY)
                SetWidgetLife(ball, GetUnitState(ball, UNIT_STATE_MAX_LIFE))
                SetUnitOwner(ball, Player(PLAYER_NEUTRAL_PASSIVE), true)
                Destination[b]=0
                lostLives=1
                if IsGiant then
                    lostLives=4
                end
                if IsGiantFlying then
                    lostLives=5
                end
                if (Level>(MaxLevel-2)) then
                    lostLives=3
                end
                PlayerLives=PlayerLives-lostLives
                if (PlayerLives<=0) then
                    PlayerLives=0
                    -- DefeatPlayer(Player(p-1))
                end
                -- GameboardInit()
            end
        end
        ball=nil
        return false
    end))
end
And here are the portions of my globals initialization that are related to this trigger, just to make sure they are properly initialized :
Lua:
    local wpRect
............
    SFXReachCitadel="Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
    ReachCitadel=CreateTrigger()
    Citadel=CreateRegion()
    wpRect=Rect(- 1024.0,-1024.0, 1024.0, 1024.0)
    RegionAddRect(Citadel, wpRect)
    MaxLevel=50
    TBallsLeft=0
    BallsPerLevel=6
    PlayerLives={}
    Destination={}
    IsFlying=false
    IsGiant=false
    IsGiantFlying=false
    StoreX=-7600.0
    StoreY=7600.0
..........
I have commented out the other functions called.

Note : the two print("test") at the beginning of the trigger don't show anything.
Yet, the one just before the call prints the expected message on screen :
Lua:
............
    print("test")
    Citadel()
............
I honestly don't understand how the debug message can work before calling the trigger, and not work in the first line of the trigger !?

Seems like I 2-3 triggers like that, and I fail to see what I am doing wrong.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
The default value of table entries is nil, so PlayerLives[Whatever] = nil. Your function will fail when it tries to reduce Nil by Nil - lostLives. Unless you set this to an Integer somewhere else, of course.

To define a default value for your table entries you can use:
Lua:
PlayerLives = __jarray(0) --plug in whatever default value you want in the parenthesis

Also, and I believe this is the main reason why it doesn't work, you need to change the name of the Citadel function as it shares the same name as the Citadel variable.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there !

This is some more precious information.
I have used this for all my integer tables.
Now can I use this on other objects as well or do other tables need another constructor ?

With all the help you guys provided, I have made much progress. Many functions work perfectly, some others tend to create indirect bugs or crashes so I have temporary disabled them. I tracked a few bugs due to duplicate trigger initialization with CreateTrigger() or CreateTimer() : it seems Lua does not tolerate creating an object that already exists, unlike Jass.

So thank you !!!

Right now I am facing an upmost troubling crash : for some reasons that I can not fathom, my difficulty dialog triggers will crash if I add a debug message in the callbacks functions !

I mean if I remove the print("test') line it works, if I leave it there the entire game crashes :
Lua:
function DisplayHPChoiceDialog()
    DialogSetMessage(HPDialog, "Choose Balls Hit Points :")
    DifficultyButton[0]=DialogAddButton(HPDialog, ChoiceDifficulty[0], 80)
    DifficultyButton[1]=DialogAddButton(HPDialog, ChoiceDifficulty[1], 76)
    DifficultyButton[2]=DialogAddButton(HPDialog, ChoiceDifficulty[2], 78)
    DifficultyButton[3]=DialogAddButton(HPDialog, ChoiceDifficulty[3], 72)
    DifficultyButton[4]=DialogAddButton(HPDialog, ChoiceDifficulty[4], 67)
    HPModifier=0.00
    DialogDisplay(Player(HostNumber-1), HPDialog, true)
end


function DifficultyDialogs()
    TriggerRegisterDialogEvent(ChooseBallsSpeedMenu, HPDialog)
    TriggerRegisterDialogEvent(DifficultyChoosen, SpeedDialog)
    TriggerAddCondition(ChooseBallsSpeedMenu, Condition(function ()
        DialogDestroy(HPDialog)
        HPDialog=nil
        HPModifier=BaseHPModifier+1.0
        Chosendficulty=ChoiceDifficulty[2]
        if (GetClickedButton()==DifficultyButton[0]) then
            HPModifier=BaseHPModifier
            Chosendficulty=ChoiceDifficulty[0]
        end
        if (GetClickedButton()==DifficultyButton[1]) then
            HPModifier=BaseHPModifier+0.5
            Chosendficulty=ChoiceDifficulty[1]
        end
        if (GetClickedButton()==DifficultyButton[3]) then
            HPModifier=BaseHPModifier+1.5
            Chosendficulty=ChoiceDifficulty[3]
        end
        if (GetClickedButton()==DifficultyButton[4]) then
            HPModifier=BaseHPModifier+2.00
            Chosendficulty=ChoiceDifficulty[4]
        end
        DialogDisplay(Player(HostNumber-1), HPDialog, false)
        DialogSetMessage(SpeedDialog, "Choose Balls Speed :")
        DifficultyButton[0]=DialogAddButton(SpeedDialog, ChoiceSpeed[0], 83)
        DifficultyButton[1]=DialogAddButton(SpeedDialog, ChoiceSpeed[1], 65)
        DifficultyButton[2]=DialogAddButton(SpeedDialog, ChoiceSpeed[2], 70)
        DifficultyButton[3]=DialogAddButton(SpeedDialog, ChoiceSpeed[3], 68)
        DifficultyButton[4]=DialogAddButton(SpeedDialog, ChoiceSpeed[4], 73)
        DialogDisplay(Player(HostNumber-1), SpeedDialog, true)
        print("test") --  crashes the game if not commented out
        return false
   end))
    TriggerAddCondition(DifficultyChoosen, Condition(function ()
        DialogDestroy(SpeedDialog)
        SpeedDialog=nil
        TimeBetweenBalls=BaseSpawnInterval
        ChosenSpeed=ChoiceSpeed[2]
        if (GetClickedButton()==DifficultyButton[0]) then
            TimeBetweenBalls=BaseSpawnInterval+2.00
            ChosenSpeed=ChoiceSpeed[0]
        end
        if (GetClickedButton()==DifficultyButton[1]) then
            TimeBetweenBalls=BaseSpawnInterval+1.00
            ChosenSpeed=ChoiceSpeed[1]
        end
        if (GetClickedButton()==DifficultyButton[3]) then
            TimeBetweenBalls=BaseSpawnInterval-1.00
            ChosenSpeed=ChoiceSpeed[3]
        end
        if (GetClickedButton()==DifficultyButton[4]) then
            TimeBetweenBalls=BaseSpawnInterval-2.00
            ChosenSpeed=ChoiceSpeed[4]
        end
        print("test") -- crashes the game if not commented out
        ClearTextMessages()
        Message="|c00ff0000"..PlayerName[HostNumber].."|r".." |c0000ff00is hosting the game,"..NL.." and has chosen these difficulty settings :|r"..NL..".."..Chosendficulty..NL.."-"+ChosenSpeed
        DisplayMessageTimed(30.00)
        PauseTimer(TickLoop)
        TimerStart(GameStart, 0.00, false, GameStarted)
        return false
   end))
end

Notes :
- I call DifficultyDialogs() at Map Init to setup the dialog button clicked triggers
- I call the DisplayHPChoiceDialog() at the end of my GameSetuo trigger (the one that runs after Map Init)
- NL is just a string containing "/n"
- PlayerName array values have already been setup,
- TickLoop and GameStart timers work fine
- the Dialog and the buttons are properly initialized and display properly. The button clicked events also work.

This is really hard to understand what could adding just a print in a callback function crash the game.

I am starting to wonder if all these problems are not related to adding actions in conditions with Lua. I mean, it definitely gave the map a performance boost when using Jass, but now I am starting to wonder if it it worth all the trouble in Lua, considering it prevents us (unless some Lua genius around here gives us a solution) to pass variables inside a Condition callback. Maybe going back to actions would help, unless you manage to see anything else I would have done wrong in these triggers.

I guess learning Lua takes more time than I have spent so far :D
 
Level 12
Joined
Jan 30, 2020
Messages
875
Well, you clearly have sharp eyes !
Indeed, it was not the right syntax. This kills me, languages like Jass are so strict with variables names that I have this bad habit of letting the parser tell me when I make such mistakes.

Finding this gave me some hope, but in vain :
Once I fixed this typos, replaced print with BJDebugMsg and commented out ClearTextMessages(), the game still crashes. Note that the message does not display (the fiunction works as it displays a message just before showing the dialog.

the function is just nearly a wrapper :
Lua:
function DisplayMessageTimed(time)
    DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.56, time, Message)
end

This is really confusing, but to be honest, I didn't expect to master Lua in days, thinking it was just another better Jass.

Although I must admit it is giving me a hard time, I trust I will never regret the switch :)

EDIT :

There is something wrong somewhere that might be related, or at least I think so.
I had disabled my HandleCount trigger as it used to work and didn't need to check it.

Just for testing purposes, I removed the debug messages from my dialog callbacks, and re enabled the HandleCount, and... it crashes the game after clicking on the second dialog button (speed dialog).

here is the trigger :
Lua:
function HandleCount()
    local hC=CreateTrigger()

    TriggerRegisterTimerEvent(hC, 2.00, true)
    TriggerAddCondition(hC, Condition(function ()
        local t=CreateTimer()

        BJDebugMsg("Current handle count = "..I2S(GetHandleId(t)-0x100000))
        DestroyTimer(t)
        t=nil
        return false
    end))
    hC=nil
end

When enabled, it is called at map init, and used to work fine.

This means there must be another problem somewhere, although I have no idea where to look, I'll simply disable other triggers one by one until it worls.

EDIT 2 : found the guilty ! It's my UISetup function. But I don't understand why it confictis and make the HandleCount trigger to crash :

Lua:
function UISetup()
    local fh=nil
    local log=nil
    local help=nil
    local miniMap=nil

    -- System buttons
    fh=BlzGetFrameByName("UpperButtonBarFrame", 0)
    BlzFrameSetVisible(fh, true)
    fh=BlzGetFrameByName("UpperButtonBarAlliesButton", 0)
    BlzFrameSetVisible(fh, false)
    fh=BlzGetFrameByName("UpperButtonBarMenuButton",0)
    log=BlzGetFrameByName("UpperButtonBarChatButton",0)
    help=BlzGetFrameByName("UpperButtonBarQuestsButton",0)
    BlzFrameSetVisible(fh, true)
    BlzFrameSetVisible(log, true)
    BlzFrameSetVisible(help, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameClearAllPoints(log)
    BlzFrameClearAllPoints(help)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, -0.11, 0.60)
    BlzFrameSetPoint(log, FRAMEPOINT_TOP, fh, FRAMEPOINT_BOTTOM, 0.0, 0.0)
    BlzFrameSetPoint(help, FRAMEPOINT_TOP, log, FRAMEPOINT_BOTTOM, 0.0, 0.0)
    BlzFrameSetSize(help, 0.12, 0.026)

    -- Hero top left Icon and Indicator
    fh=BlzGetOriginFrame(ORIGIN_FRAME_HERO_BAR, 0)
    BlzFrameSetVisible(fh, true)
    fh=BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, -0.13, 0.42)
    fh=BlzGetOriginFrame(ORIGIN_FRAME_HERO_BUTTON_INDICATOR, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, -0.13, 0.42)

    -- Minimap
    miniMap=BlzGetFrameByName("MiniMapFrame", 0)
    BlzFrameSetVisible(miniMap, true)
    BlzFrameClearAllPoints(miniMap)
    BlzFrameSetAbsPoint(miniMap, FRAMEPOINT_TOPLEFT, 0.0, 0.60)
    BlzFrameSetAbsPoint(miniMap, FRAMEPOINT_BOTTOMRIGHT, 0.14, 0.46)
    fh=BlzGetFrameByName("MinimapSignalButton", 0)
    BlzFrameSetVisible(BlzFrameGetParent(fh), true)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetPoint(fh, FRAMEPOINT_LEFT, miniMap, FRAMEPOINT_RIGHT, 0.0, 0.0)
    fh=BlzGetFrameByName("MiniMapTerrainButton", 0)
    BlzFrameSetVisible(fh, false)
    fh=BlzGetFrameByName("MiniMapAllyButton", 0)
    BlzFrameSetVisible(fh, false)
    fh=BlzGetFrameByName("MiniMapCreepButton", 0)
    BlzFrameSetVisible(fh, false)
    fh=BlzGetFrameByName("FormationButton", 0)
    BlzFrameSetVisible(fh, false)
 
    -- Minimap border
    fh=BlzCreateFrameByType("BACKDROP", "MinimapBorder", miniMap, "", 0)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.0, 0.60)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMRIGHT, 0.14, 0.46)
    BlzFrameSetTexture(fh, "ui\\minimap-border.blp",0, true)

    -- Resources bar
    fh=BlzGetFrameByName("ResourceBarFrame", 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetPoint(fh, FRAMEPOINT_TOPLEFT, miniMap, FRAMEPOINT_BOTTOMRIGHT, 0.01, 0.14)

    -- Resources bar border
    fh=BlzCreateFrameByType("BACKDROP", "ResourcesBorder", miniMap, "", 0)
    BlzFrameSetSize(fh, 0.33828125, 0.0218)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.15, 0.60)
    BlzFrameSetTexture(fh, "ui\\resourcesbar-border.blp",0, true)

    -- Chat
    fh=BlzGetOriginFrame(ORIGIN_FRAME_CHAT_MSG, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, -0.13, 0.40)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMRIGHT, 0.14, 0.13)

    -- Messages
    fh=BlzGetOriginFrame(ORIGIN_FRAME_UNIT_MSG, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.15, 0.44)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMRIGHT, 0.50, 0.12)

    -- Tooltips
    fh=BlzGetOriginFrame(ORIGIN_FRAME_TOOLTIP, 0)
    BlzFrameSetVisible(fh, true)
    fh=BlzGetOriginFrame(ORIGIN_FRAME_UBERTOOLTIP, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.64, 0.0)

    -- Command buttons
    fh=BlzGetFrameByName("CommandBarFrame", 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.22, 0.0)

    -- Command buttons border
    fh=BlzCreateFrameByType("BACKDROP", "CommandBorder", miniMap, "", 0)
    BlzFrameSetSize(fh, 0.1745, 0.131)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.218, 0.0)
    BlzFrameSetTexture(fh, "ui\\commandbar-border.blp",0, true)

    -- InfoPanel and Multiple Unit Selection
    fh=BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail", 0))
    BlzFrameSetVisible(fh, true) -- The parent of SimpleUnitInfoPanel manages the multiple unit selection UI
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.025, 0.0)

    -- InfoPanel border
    fh=BlzCreateFrameByType("BACKDROP", "CommandBorder", miniMap, "", 0)
    BlzFrameSetSize(fh, 0.192, 0.12)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.022, 0.0)
    BlzFrameSetTexture(fh, "ui\\infopanel-border.blp",0, true)

    -- Portrait
    fh=BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT, 0)
    BlzFrameSetVisible(fh, true)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.0, 0.0)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPRIGHT, 0.10, 0.12)

    --[[ Portrait HP Text, currently not working when Origin Frames are hidden
    Selector=CreateUnit(Player(0), FourCC('nSEL'), 0, 0, 0)
    SelectUnit(Selector, true)
    TimerStart(CreateTimer(), 0.30, false, function ()
        local f=nil

        DestroyTimer(GetExpiredTimer())
        f=BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_HP_TEXT, 0)
        BlzFrameClearAllPoints(f)
        BlzFrameSetAbsPoint(f, FRAMEPOINT_CENTER, 0.20, 0.20)
        RemoveUnit(Selector)
    end)]]
end
I have temporarily disabled the Custom UI (except for the Camera Zoom Slider and the Timer Dialogs) because it seems that Lua has issues dealing messages printed on the screen for players and Console UI alterations.

EDIT 3 :

Nope... it was not the UISetup function. It was using it with UI Origin Frames hidden.
Now I am running into strange cases of simple functions that crash if I call them after a 0.00 expired timer, but that don't crash if i call them directly inline.

There are so many things that seem to behave in a weird manner in Lua that it is sometimes discouraging. I start to seriously doubt I will ever manage to convert this map into a Lua version, especially as it is quite complex, with many intricate elements and modifications to the standard game behavior...
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
UPDATE

Ok, I decided not to let this put me down, and decided to not centralize global variables anymore when it is not required.

Also, I realized my difficulty dialog triggers were really messy, so I rewrote them completely. And they work !

For anyone following this thread and wondering how I solved this, here is the new working version :
Lua:
function ShowHPDialog()

    -- Initialize dialogs and their buttons
    HPDialog=DialogCreate()
    HPChoice={}
    HPChoice[1]="|c008080ffP|ransy HP"
    HPChoice[2]="|c008080ffL|row HP"
    HPChoice[3]="|c008080ffN|rormal HP"
    HPChoice[4]="|c008080ffH|righ HP"
    HPChoice[5]="|c008080ffC|rrazy HP"
    HPButton={}
    HPButton[1]=DialogAddButton(HPDialog, HPChoice[1], 80)
    HPButton[2]=DialogAddButton(HPDialog, HPChoice[2], 76)
    HPButton[3]=DialogAddButton(HPDialog, HPChoice[3], 78)
    HPButton[4]=DialogAddButton(HPDialog, HPChoice[4], 72)
    HPButton[5]=DialogAddButton(HPDialog, HPChoice[5], 67)
    DialogSetMessage(HPDialog, "Choose Balls Hit Points :")
    SpeedDialog=DialogCreate()
    SpeedChoice={}
    SpeedChoice[1]="|c00ff8080S|rlow spawn speed"
    SpeedChoice[2]="|c00ff8080A|rverage spawn speed"
    SpeedChoice[3]="|c00ff8080F|rast spawn speed"
    SpeedChoice[4]="|c00ff8080D|ramn Fast spawn speed"
    SpeedChoice[5]="|c00ff8080I|rnsane spawn speed"
    SpeedButton={}
    SpeedButton[1]=DialogAddButton(SpeedDialog, SpeedChoice[1], 83)
    SpeedButton[2]=DialogAddButton(SpeedDialog, SpeedChoice[2], 65)
    SpeedButton[3]=DialogAddButton(SpeedDialog, SpeedChoice[3], 70)
    SpeedButton[4]=DialogAddButton(SpeedDialog, SpeedChoice[4], 68)
    SpeedButton[5]=DialogAddButton(SpeedDialog, SpeedChoice[5], 73)
    DialogSetMessage(SpeedDialog, "Choose Balls Spawn Speed :")

    -- Setting default values, also used if the host runs out of time and AutoChoose is triggered
    BaseHPModifier=2.50
    HPModifier=BaseHPModifier+1.0
    ChosenHP=HPChoice[3]
    BaseSpawnInterval=3.00
    TimeBetweenBalls=BaseSpawnInterval
    ChosenSpeed=SpeedChoice[3]

    -- Now creating the related triggers
    SetHP=CreateTrigger()
    TriggerRegisterDialogEvent(SetHP, HPDialog)
    TriggerAddAction(SetHP, HPChosen)
    SetSpeed=CreateTrigger()
    TriggerRegisterDialogEvent(SetSpeed, SpeedDialog)
    TriggerAddAction(SetSpeed, SpeedChosen)

    -- Now both dialogs and their triggers are ready, we can display the first one
    DialogDisplay(Player(HostNumber-1), HPDialog, true)
end


function HPChosen()
    print("okHP")
    Choice=GetClickedButton()
    DialogDestroy(HPDialog)
    HPDialog=nil
    if (Choice==HPButton[1]) then
        HPModifier=BaseHPModifier
        ChosenHP=HPChoice[1]
    end
    if (Choice==HPButton[2]) then
        HPModifier=BaseHPModifier+0.5
        ChosenHP=HPChoice[2]
    end
    if (Choice==HPButton[4]) then
        HPModifier=BaseHPModifier+1.5
        ChosenHP=HPChoice[4]
    end
    if (Choice==HPButton[5]) then
        HPModifier=BaseHPModifier+2.00
        ChosenHP=HPChoice[5]
    end
    DestroyTrigger(SetHP)
    DialogDisplay(Player(HostNumber-1), SpeedDialog, true)
end


function SpeedChosen()
    PauseTimer(GameTimer)
    BlzFrameSetVisible(TimerDialogFH, false)
    Choice=GetClickedButton()
    DialogDestroy(SpeedDialog)
    SpeedDialog=nil
    ChosenSpeed=SpeedChoice[3]
    Choice=GetClickedButton()
    if (Choice==SpeedButton[1]) then
        TimeBetweenBalls=BaseSpawnInterval+2.00
        ChosenSpeed=SpeedChoice[1]
    end
    if (Choice==SpeedButton[2]) then
        TimeBetweenBalls=BaseSpawnInterval+1.00
        ChosenSpeed=SpeedChoice[2]
    end
    if (Choice==SpeedButton[4]) then
        TimeBetweenBalls=BaseSpawnInterval-1.00
        ChosenSpeed=SpeedChoice[4]
    end
    if (Choice==SpeedButton[5]) then
        TimeBetweenBalls=BaseSpawnInterval-2.00
        ChosenSpeed=SpeedChoice[5]
    end
    ClearTextMessages()
    print("okSpeed")
    Message="|cffffff00Difficulty settings were chosen by the host :|r"..NL.."-"..ChosenHP..NL.."-"..ChosenSpeed
    DisplayMessageTimed(30.00)
    PauseTimer(TickLoop)
    TimerStart(GameStart, 0.00, false, GameStarted)
end


function AutoChoose()
    PauseTimer(TickLoop)
    PauseTimer(GameTimer)
    BlzFrameSetVisible(TimerDialogFH, false)
    DialogDestroy(HPDialog)
    HPDialog=nil
    DialogDestroy(SpeedDialog)
    SpeedDialog=nil
    DestroyTrigger(SetHP)
    DestroyTrigger(SetSpeed)
    ClearTextMessages()
    Message="|cffffff00Difficulty settings were chosen automatically :|r"..NL.."-"..ChosenHP..NL.."-"..ChosenSpeed
    DisplayMessageTimed(35.00)
    TimerStart(GameStart, 0.00, false, GameStarted)
end

You will notice the Autochoose function. It only works in multiplayer because in singleplayer, the dialogs displayed pause the game.
This prevents the host from sitting for ages on the difficulty dialog while other players are waiting.
If the host hangs out for more than 20 seconds, the difficulties will be chosen automatically

Now to understand how these dialogs are started, here are the "actions" from the GameSetup trigger that initiate the dialogs and Autochoose :
Lua:
    -- other actions before that have no direct impact
    InitGameTimer()
    StartGameTimer(20.00, "Time before automatic difficulties", AutoChoose)
    ShowHPDialog()

I need to explain that InitGameTimer and StartGameTimer are semi-wrappers to just create the timer and start it but with UI functions that change the position of the timer dialog.
You can simply replace it with a TimerStart function.



Now, @Uncle or @Planetary : if you guys still are around and not too tired with my constant questions, I have made incredible progress, many things start to work.

This said, I am still facing the problem with passing arguments to a callback function, in a timer or in a trigger (action/condition).
So far we can't really do it in a trigger condition, so for these triggers I will use Actions rather than Conditions0. But the problem is, even in these, I think I misunderstood something as I can't pass a value to the callback directly.

Here is a simple example of my PlayCleanSFX function that is supposed to play an effect at a position with a size, color and angle :
Lua:
function PlayCleanSFX(sfxString, x, y, scale, angle, colorindex, duration)
    local sfx=AddSpecialEffect(sfxString, x, y)

    BlzSetSpecialEffectScale(sfx, scale)
    BlzSetSpecialEffectRoll(sfx, angle)
    if (colorindex~=0) then
        BlzSetSpecialEffectColor(sfx, Red[colorindex], Green[colorindex], Blue[colorindex])
    end
    TimerStart(CreateTimer(), duration, false, function ()
        DestroyTimer(GetExpiredTimer())
        DestroyEffect(sfx)
   sfx=nil
    end)
    sfx=nil
end

Unfortunately, the effect is not removed when the timer expires.
I tried adding the local variable sfx as a parameter inside the brackets TimerStart(CreateTimer(), duration, false, function (sfx) , but it sill doesn't work.

So what is the proper syntax to get this to work ?

small edit : there is no BBCode for In line code other than Jass ?
Also i tried TimerStart(CreateTimer(), duration, false, DestroyEffect(sfx)) but the effect is destroyed instantly

small edit 2 :
As comparing handles in Lua can be buggy, and player type is an extension of agent that is a handle, do I need to use GetHandleId to compare players ?

In other words, would if (aPlayer==GetLocalPlayer()) then ... bug, and then shall I use if (GetHandleId(aPlayer)==GetHandleId(GetLocalPlayer())) then ... in Lua ??

If that is the case, I think it would be better to store the GetHandleId(GetLocalPlayer()) in a global variable to make code choke a bit less, is it still possible to do that ?
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Ok thanks for letting me know, I was about to use it and it would have been a terrible mistake.

There has to be a way because if there isn't, it would render Lua useless, or nearly because if you cannot compare handles in a reliable way, many things cannot be done anymore. Of course, you don't often need to compare the handles themselves, most of the time people check the unit Id or ability id to check them, but once you need to make sure a specific handle triggered an event or is the subject of an action, there is no alternative than comparing handles.

Well thats the case for "if (aPlayer==GetLocalPlayer()) then" and many other specific checks. If these are not reliable, Lua is not an option anymore.

But I am confident a workaround does exist, if not no experienced map maker would use Lua.

On a side note, nobody has an idea about my earlier question (passing an argument to a callback function for a timer or a trigger add action) ?
 
Level 14
Joined
Feb 7, 2020
Messages
386
I would install [Lua] TimerUtils so you can pass data in timers and reference it in the context of that timer. Makes things a whole lot easier to develop. I have not run into issues with destroying effects this way.

TimerStart( NewTimer(effect), 3.0, true, function() DestroyEffect(effect) end )

As for your argument question, arguments require the function to be anonymous Lua - Anonymous functions | lua Tutorial. I think function() in this instance does count. However, as far as I understand, timers are separate routines which means the argument you are trying to pass is being called out of scope (which is why TimerUtils by Bribe or the older variant by Vexorian were created). Otherwise, 'effect' needs to be a global variable so it can be referenced.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Hello guys, thanks for the answers, much appreciated !

I was out today, haven't had time to progress much, but I still did.

@Planetary : thanks again for the extra and very important information, you show me how little I know for the moment.
I understand what you mean with anonymous function, but I will read that tutorial.
I will try to make all the functions I use inside triggers anonymous when I need to pass arguments (in this case I also need to use Actions rather than conditions).
As for Timer Units, I'll have a look. If it uses tables or hashtables, it would be easier for me to just take my Jass code back as I had a whole personal mini-library that was pretty optimized and efficient to counter this flaw. It is a slight disappointment Lua also has restrictions in the matter, but as far as I remember thats the case with every single language (or nearly). If that library uses more efficient methods, I will use it.
I am not a fan of adding other's work in my map, as this is for me an excuse to slowly become a good map maker again like I was such a long time ago.
In other words, I need to learn a lot from this new community that has so much more knowledge than mine back in 2004.
When I know I can't achieve something, and I find others amazing work at the same time I don't hesitate !

@LazZ :Well it's just that I have been told quite many times that Lua direct handle comparison was unreliable . Unfortunately, I don't have the slightest idea of the extent of the issue. If you tell me a local player comparison is reliable, I will then keep using them. Actually as I am now warned about handle id comparison (because they are local), I don't have much choice for the other situations. So I will try and see what happens... crossing fingers ^^

@Uncle Seems like you have a sharp eye too. Although these functions now all work, It can't hurt to avoid some names to avoid collisions.



Now I have another question (how can that be ! :D) :

I have that custom spell exclusively driven by trigger.
That spell is cast by my TD's mazing towers, and will build a line of towers in the direction of a targeted point.
To calculate the angle, I was using a single Jass math function.

I tried to change them for Lua's math library equivalent, but there is one that definitely doesn't work like in Jass :

I replaced the Atan2 in this angle=bj_RADTODEG * Atan2(tY - oY, tX - oX) toangle=bj_RADTODEG * math.atan2(tY - oY, tX - oX), but that crashed the thread.
Now I put back the Jass Atan2, and the trigger works great, but I wonder why I could not use the Lua version of the arc tangent.

Thats about it at the moment, thanks to the constant pointers you guys all gave me, I can now manually "debug" Jass muc better, and I have already fixed many triggers. I should be finished within couple of days if IRL does not keep me too busy ^^

Once the map is 100% functional, it will mean that I understand the basic concepts, and that I will then be able to dig deeper in the language !

Again, thanks !!!

EDIT : Timer Utils is much smarter and shorter than I expected, I will look deeper and adopt it, it is definitely more efficient than my old system by a considerable scale.
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Ohh thanks, very helpful again !!!

small edit : well WE does not agree, and throws an error if i use (s,c)
Will have to chose s/c

It works, of course I now have to make sure c is not =0 because it freezes the game (not a surprise ^^)

....

And I was wrong again, it was of course a type, it indeed takes (s,c), but the angles are not right that way.
I will just manage the case when c is 0
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there !

Just a quick word to say I have not "installed" Timer Units. It is not really what I would call a library.
All it does is save a variable in a table with the timer as the table index. Thats basics in a language that allows this kind of indexing.
Moreover, it only allows passing one variable (but that could be anything, so not really a limitation).

I plan to just save a list of values in a timer array, that would do something similar as Timer units, but with the ability to save as many variables as needed.
I have not tested it yet, but it should look like this, with TimerData initialized at map init :
Lua:
function IssueDelayedPointOrder(caster, orderID, x, y, duration)
    local t=CreateTimer()

    set TimerData[t]={caster, orderID, x, y}
    TimerStart(t, duration, false, function ()
        local t=GetExpiredTimer()
        local caster=TimerData[t][1]
        local orderID=TimerData[t][2]
        local x=TimerData[t][3]
        local y=TimerData[t][4]
    
        DestroyTimer(t)
        IssuePointOrderById(caster, orderID, x, y)
    end)
end
I am confident this will work, and if it doesn't, it's just a syntax issue as I am not sure yet about my Lua syntax knowledge.

EDIT : ok

using {} or even [] doesn't work unlike what I seem to have read on Lua documentation.
I wonder what is wrong with my syntax...

EDIT 2 : honestly, I laugh at myself. this damn "Set" shows how much Jass has traumatized me :D
 
Last edited:
Status
Not open for further replies.
Top