• 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.

Displaying a null dialog

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,338
Hi,

What happens if we display a null dialog, or a dialog without buttons?

For some reason when I display my dialog, it just pauses the game and it's impossible to get out of (nothing shows up as a dialog/button).

Is this because it's null?

Here's the code where I initialize my dialog, called "intro". No matter where I try to access it, it just pauses the game but brings nothing up. What's wrong here?

JASS:
private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    set intro = DialogCreate()
    call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "here22")
    set reviveBttn = DialogAddButton(intro, "Revive a monster.", 1)
    set quitBttn = DialogAddButton(intro, quitMessage, 2)
    call DialogSetMessage(intro, introMessage)
    //call DialogDisplay(Player(0), intro, true)
    //set up the basic trigger
    call TriggerRegisterDialogEvent(t, intro)
    call TriggerAddCondition(t, Condition(function introMain))
endfunction
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
so the "here22" shows, the code proceeds... but when you trigger it, it doesn't work?

Well whenever I make reference to "intro" it just pauses the game but shows nothing. I also checked to see if it was null, but it isn't, so I have no idea what's going on.

And yes that "debug" message displays (just to make sure "intro" actually is getting set up).

So is it because of where I am referencing "intro?"
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
Here's the full code, what could be wrong with it?

Also I put in several debug messages, but they all printed, so no crash. It must be how I'm referencing "intro." Basically I reference it in data structures but actually initialize it here first.

JASS:
library PriestStruct initializer init requires NPCStruct

globals
    constant integer REVIVE_COST_FACTOR = 100 //the player pays gold equal to 100x the monster's level
    dialog intro
    private button reviveBttn //the priest can revive a player's dead monster
    private button quitBttn //option to leave the dialog
    private string introMessage = "Priest: How may the Gods be of service?"
endglobals


struct Priest extends NPC
    static method create takes integer unitType returns thistype
        local thistype this = thistype.allocate(unitType)
        return this
    endmethod

    //creates the triggers necessary to run the priest's actions
    method interact takes integer pid returns nothing
        if intro == null then
        else
        call DisplayTimedTextToPlayer(Player(pid), 0, 0, 10, "here")
        call DialogDisplay(Player(pid), intro, true)
        endif
    endmethod
endstruct

private function reviveMon takes nothing returns boolean
    local button b = GetClickedButton()
    local integer pid = GetPlayerId(GetTriggerPlayer())
    local integer i = 0 //counter
    local MonsterGroup m
    set m = getPlayerParty(pid)
    loop
        exitwhen i == m.size
        if b == m.bttns[i] then
            call DisplayTimedTextToPlayer(Player(pid), 0, 0, 10, m.monsters[i].toString())
        endif
        set i = i + 1
    endloop
    return false
endfunction

private function introMain takes nothing returns boolean
    local button b = GetClickedButton()
    local integer i = GetPlayerId(GetTriggerPlayer())
    local MonsterGroup m
    local dialog d
    local trigger t
    if b == quitBttn then
        call DialogClear(intro)
    elseif b == reviveBttn then //we need to call the player's monsterGroup for now we'll cheat
        set m = getPlayerParty(i)
        set t = CreateTrigger()
        call TriggerRegisterDialogEvent(t, m.mDialog)
        call TriggerAddCondition(t, Condition(function reviveMon))
        call m.displayGroup(true, "Which monster shall I restore to life?")
    endif
    return false
endfunction


//set up all the initial buttons and intro triggers
private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    set intro = DialogCreate()
    call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "here22")
    set reviveBttn = DialogAddButton(intro, "Revive a monster.", 1)
    set quitBttn = DialogAddButton(intro, quitMessage, 2)
    call DialogSetMessage(intro, introMessage)
    //call DialogDisplay(Player(0), intro, true)
    //set up the basic trigger
    call TriggerRegisterDialogEvent(t, intro)
    call TriggerAddCondition(t, Condition(function introMain))
endfunction
    
endlibrary
 
btw, registering a new event everytime is not a good idea... multiples of same events will cause the conditions and actions to run that same number of times too

BTW when do you actually show the dialog??? (when does interact begins?) because for all I see, the only function you have that shows the dialog is interact, and I can't see where you call interact

you should add debug messages to the functions that run for that dialog... there are too many things that might be running wrong in here... especially when you seem to use more code that we don't see (like the parent struct of that struct, or the required library)
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
I don't think you can create dialog at map init. use a timer to wait 0.00 seconds then show it.

So where do I put the initialization stuff for the dialog, and how do I register it to a trigger then?

@Adiktuz

Well if I later delete the trigger will not not get rid of the event?
 
if you delete the trig, then of course it will remove the events too...

but really, it's inefficient to destroy triggers and recreate them unless you really need to (like in case of DDS in which we needed to since we cannot delete events, so instead we delete the trig to get rid of the now useless events (damage events for already dead units))

if an event will stay the same for the rest of the game, better leave it be...

also

Adiktuz said:
BTW when do you actually show the dialog??? (when does interact begins?) because for all I see, the only function you have that shows the dialog is interact, and I can't see where you call interact

you should add debug messages to the functions that run for that dialog... there are too many things that might be running wrong in here... especially when you seem to use more code that we don't see (like the parent struct of that struct, or the required library)
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
if you delete the trig, then of course it will remove the events too...

but really, it's inefficient to destroy triggers and recreate them unless you really need to (like in case of DDS in which we needed to since we cannot delete events, so instead we delete the trig to get rid of the now useless events (damage events for already dead units))

if an event will stay the same for the rest of the game, better leave it be...

also

Well those bits are not so relevant, because it seems the problem is like death said dialogs can't be initialized in the init like this guy, who had the same exact problem. Only thing is I don't understand what the fix was (it's not even said how exactly to fix it here >_<)

http://www.hiveworkshop.com/forums/triggers-scripts-269/problem-dialog-158007/
 
JASS:
private function Setup takes nothing returns nothing
    local trigger t = CreateTrigger()
    set intro = DialogCreate()
    call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "here22")
    set reviveBttn = DialogAddButton(intro, "Revive a monster.", 1)
    set quitBttn = DialogAddButton(intro, quitMessage, 2)
    call DialogSetMessage(intro, introMessage)
    //call DialogDisplay(Player(0), intro, true)
    //set up the basic trigger
    call TriggerRegisterDialogEvent(t, intro)
    call TriggerAddCondition(t, Condition(function introMain))
    set t = null
endfunction

private function init takes nothing returns nothing
    local timer tmr = CreateTimer()
    call TimerStart( tmr, 0.00, function Setup)
    set tmr = null
endfunction
also make sure to null all local handles. Don't forget to get expired timer and destroy it in the setup function.
I am on my phone so couldn't set up everything correctly it may have errors.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
JASS:
private function Setup takes nothing returns nothing
    local trigger t = CreateTrigger()
    set intro = DialogCreate()
    call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "here22")
    set reviveBttn = DialogAddButton(intro, "Revive a monster.", 1)
    set quitBttn = DialogAddButton(intro, quitMessage, 2)
    call DialogSetMessage(intro, introMessage)
    //call DialogDisplay(Player(0), intro, true)
    //set up the basic trigger
    call TriggerRegisterDialogEvent(t, intro)
    call TriggerAddCondition(t, Condition(function introMain))
    set t = null
endfunction

private function init takes nothing returns nothing
    local timer tmr = CreateTimer()
    call TimerStart( tmr, 0.00, function Setup)
    set tmr = null
endfunction
also make sure to null all local handles. Don't forget to get expired timer and destroy it in the setup function.
I am on my phone so couldn't set up everything correctly it may have errors.

Ah why do you set the trigger t to be null?

Should I null all local variables that aren't primitives in all my functions/methods when they are done being used?
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
Adiktuz said it pretty good.

Here is another way to understand if that didn't help.

Variables are only pointers to data in memory.
So when nulling the local you simply remove the pointer not the data in the memory.
Destroying / Removing function calls remove that data in memory.

Ah that makes sense.

But what about structs? If we have local structs, do we need to do anything?

Would you call these functions leakless?

JASS:
private function reviveMon takes nothing returns boolean
    local button b = GetClickedButton()
    local player p = GetTriggerPlayer()
    local integer pid = GetPlayerId(p)
    local integer i = 0 //counter
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer cost = 0
    local MonsterGroup m
    set m = getPlayerParty(pid)
    loop
        exitwhen i == m.size
        if b == m.bttns[i] then
            if m.monsters[i].isDead() then
                set cost = (m.monsters[i].heroLvl * REVIVE_COST_FACTOR)
                if gold >= cost then
                    call ReviveHeroLoc(m.monsters[i].u, GetUnitLoc(playerDatum[pid].u), true)
                    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold - cost)
                    call StartSound(gg_snd_Church)
                    call DisplayTimedTextToPlayer(p, 0, 0, 10, "|cff00ffffPriest|r: Let light restore your " + m.monsters[i].toString() + "!")
                else
                    call DisplayTimedTextToPlayer(p, 0, 0, 10, "|cff00ffffPriest|r: You need at least " + I2S(cost) + " gold to restore your " + m.monsters[i].toString() + ".")
                endif
            else
                call DisplayTimedTextToPlayer(Player(pid), 0, 0, 10, "|cff00ffffPriest|r: Your " + m.monsters[i].toString() + " is well and alive already.")
            endif
        endif
        set i = i + 1
    endloop
    call DestroyTrigger(playerDatum[pid].npcTrig)
    set b = null
    return false
endfunction

private function introMain takes nothing returns boolean
    local button b = GetClickedButton()
    local integer pid = GetPlayerId(GetTriggerPlayer())
    local MonsterGroup m
    local trigger t
    if b == quitBttn then
        call DialogClear(intro)
    elseif b == reviveBttn then //we need to call the player's monsterGroup for now we'll cheat
        set m = getPlayerParty(pid)
        call m.displayGroup(true, "Which monster shall I restore to life?")
        set t = CreateTrigger()
        call TriggerRegisterDialogEvent(t, m.mDialog)
        call TriggerAddCondition(t, Condition(function reviveMon))
        set playerDatum[pid].npcTrig = t
    endif
    set b = null
    set t = null
    return false
endfunction
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
It does, that's why you need to destroy the instance when you don't need it already, but the one on your example seems to be a global struct instance, and it's not using the default allocator so it's a different case.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
That doesn't make sense to me.
I was also told by Nes / Mag / Bribe and many others that All local variables that are handles need to be nulled.

It's okay, since you only use them once, that would just leak one reference pointer per trigger anyway, but if you don't want to leak the references... Use a global variable haha xD.

I'd still null them though.
 
For example local timer t = GetExpiredTimer() requires no nulling if you do not destroy it.

I just tried that with this simple trigger and the memory keeps rising.
That is a leak same with not nulling a local player variable.

JASS:
function Trig_Untitled_Trigger_002_Actions takes nothing returns nothing
    //local player p = Player(0)
    local timer t = GetExpiredTimer()
    call BJDebugMsg( "Running")
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_002 takes nothing returns nothing
    local timer t = CreateTimer()
    set gg_trg_Untitled_Trigger_002 = CreateTrigger(  )
    call TimerStart( t, 0.01, true, function Trig_Untitled_Trigger_002_Actions)
    call TriggerAddAction( gg_trg_Untitled_Trigger_002, function Trig_Untitled_Trigger_002_Actions )
endfunction

It goes up very slow. Only about 4kb per second.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
I just tried that with this simple trigger and the memory keeps rising.
That is a leak same with not nulling a local player variable.

JASS:
function Trig_Untitled_Trigger_002_Actions takes nothing returns nothing
    //local player p = Player(0)
    local timer t = GetExpiredTimer()
    call BJDebugMsg( "Running")
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_002 takes nothing returns nothing
    local timer t = CreateTimer()
    set gg_trg_Untitled_Trigger_002 = CreateTrigger(  )
    call TimerStart( t, 0.01, true, function Trig_Untitled_Trigger_002_Actions)
    call TriggerAddAction( gg_trg_Untitled_Trigger_002, function Trig_Untitled_Trigger_002_Actions )
endfunction

It goes up very slow. Only about 4kb per second.

Is there an automatic tool that we can run which will tell us if there are memory leaks, like a parser that checks for "unsafe" code? How are you monitoring the rate of memory usage increase (native functions tell you this?)?
 
Is there an automatic tool that we can run which will tell us if there are memory leaks, like a parser that checks for "unsafe" code? How are you monitoring the rate of memory usage increase (native functions tell you this?)?

I use task manager as suggested above by Adiktuz.

The memory usage increases and never drops means a leak. It takes a long time to get a significant leak when dealing with locals but it is still a leak.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,255
It is not possible to create dialogs at map initialization. Doing so will produce all manner of error. All shown dialog will pause single player sessions but leave multi player sessions running.

Generally the result for dialog created at map initialization is a empty dialog box (despite buttons being added) that will logically freeze up single player sessions as there is no way to resume the game. Any form of wait seems sufficient as long as it is not created as a direct result of map initialization.
 
Status
Not open for further replies.
Top