[Snippet] Dialog Wrapper

PurgeandFire

Spell Moderator
Level 43
Joined
Nov 11, 2006
Messages
7,501
This is a wrapper to ease the creation of dialogs. It does not offer bonus functionality, but it will ease the process of creating dialogs by giving you a clear API with nice documentation. That is the only goal of this snippet.

I realized shortly after I made this that The_Witcher already submitted a similar system that even has a similar API. His also does more than mine. But mine is cool--it does just the right amount of things. It is kind of like the little bear's soup in goldilocks.

Anyway, there are multiple ways to approach handling a dialog. Personally, I like consolidating the functions since they usually have similar behavior. Other people may prefer to have a separate callback per dialog button (in which case, use The_Witcher's system).

Check out the sample code for an example of its usage. Enjoy.

JASS:
library Dialog /* v.1.1.0.0
**************************************************
*
*   A struct wrapper for easy dialog creation.
*
**************************************************
*
*/  requires Table  /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
* 
**************************************************
*
*   struct Dialog 
*
*       static method create() returns thistype
*           
*           Creates a new dialog and returns its instance.
*
*       method operator title= takes string s
*
*           Sets the title message for the dialog.
*
*       method addButton( string text, integer hotkey ) returns button
*
*           Adds a button to the dialog that reads <text>.
*           The hotkey serves as a shortcut to press a dialog 
*           button. Example input: 'a', 'b', 512 (esc).
*
*       method display( player p, boolean flag )
*           
*           Shows/hides a dialog for a particular player. 
*
*       method displayAll( boolean flag )
*
*           Shows/hides a dialog for all players. 
*
*       method clear()
*   
*           Clears a dialog of its message and buttons.
*
*       method destroy()
*
*           Destroys a dialog and its instance.
*
*   -- Event API --
*
*       method registerClickEvent( boolexpr b )
*
*           Registers when a dialog button is clicked.
*
*       static method getClickedDialog() returns Dialog
*       static method getClickedButton() returns button
*       static method getPlayer() returns player
*
*            Event responses.
*
**************************************************
*
*   Constants - Credits to DysfunctionaI for the list!
*/
    globals
        constant integer KEY_SPACEBAR          = 32
        
        /* Number Pad */
        constant integer KEY_NUMPAD_0          = 257
        constant integer KEY_NUMPAD_1          = 258
        constant integer KEY_NUMPAD_2          = 259
        constant integer KEY_NUMPAD_3          = 260
        constant integer KEY_NUMPAD_4          = 261
        constant integer KEY_NUMPAD_5          = 262
        constant integer KEY_NUMPAD_6          = 263
        constant integer KEY_NUMPAD_7          = 264
        constant integer KEY_NUMPAD_8          = 265
        constant integer KEY_NUMPAD_9          = 266
        constant integer KEY_NUMPAD_PLUS       = 267
        constant integer KEY_NUMPAD_MINUS      = 268
        constant integer KEY_NUMPAD_ASTERISK   = 269 
        constant integer KEY_NUMPAD_SLASH      = 270
        constant integer KEY_NUMPAD_PERIOD     = 271
        
        constant integer KEY_EQUALS            = 272
        constant integer KEY_MINUS             = 273
        constant integer KEY_LEFT_BRACKET      = 274
        constant integer KEY_RIGHT_BRACKET     = 275
        constant integer KEY_BACKSLASH         = 276
        constant integer KEY_SEMICOLON         = 277
        constant integer KEY_APOSTROPHE        = 278
        constant integer KEY_COMMA             = 279
        constant integer KEY_PERIOD            = 280   
        constant integer KEY_SLASH             = 281
        constant integer KEY_ESCAPE            = 512
        constant integer KEY_BACKSPACE         = 514
        
        /* Arrows */
        constant integer KEY_LEFT_ARROW        = 516
        constant integer KEY_UP_ARROW          = 517
        constant integer KEY_RIGHT_ARROW       = 518
        constant integer KEY_DOWN_ARROW        = 519
        
        constant integer KEY_INSERT            = 520
        constant integer KEY_DELETE            = 521
        constant integer KEY_HOME              = 522
        constant integer KEY_END               = 523
        constant integer KEY_PAGE_UP           = 524
        constant integer KEY_PAGE_DOWN         = 525
        constant integer KEY_CAPS_LOCK         = 526
        constant integer KEY_NUM_LOCK          = 527
        constant integer KEY_SCROLL_LOCK       = 528
        constant integer KEY_PAUSE             = 529
        
        /* Function Buttons */
        constant integer KEY_F1                = 768
        constant integer KEY_F2                = 769
        constant integer KEY_F3                = 770
        constant integer KEY_F4                = 771
        constant integer KEY_F5                = 772
        constant integer KEY_F6                = 773
        constant integer KEY_F7                = 774
        constant integer KEY_F8                = 775
        constant integer KEY_F9                = 776
        constant integer KEY_F10               = 777
        constant integer KEY_F11               = 778
        constant integer KEY_F12               = 779
    endglobals
/*
**************************************************/

    globals
        private Table instance = 0
    endglobals
    
    private module DialogInit 
        private static method onInit takes nothing returns nothing 
            set instance = Table.create()
        endmethod
    endmodule

    struct Dialog
        private dialog dg
        private trigger click
        private string msg
        
        static method getClickedDialog takes nothing returns thistype 
            return instance[GetHandleId(GetClickedDialog())]
        endmethod
        static method getClickedButton takes nothing returns button 
            return GetClickedButton()
        endmethod
        static method getPlayer takes nothing returns player 
            return GetTriggerPlayer()
        endmethod
        
        method operator title= takes string text returns nothing 
            call DialogSetMessage(this.dg, text)
            set this.msg = text
        endmethod
        
        method addButton takes string text, integer hotkey returns button
            return DialogAddButton(this.dg, text, hotkey)
        endmethod
        
        method display takes player p, boolean flag returns nothing
            call DialogDisplay(p, this.dg, flag)
            call DialogSetMessage(this.dg, this.msg)
        endmethod
        method displayAll takes boolean flag returns nothing
            call DialogDisplay(GetLocalPlayer(), this.dg, flag)
            call DialogSetMessage(this.dg, this.msg)
        endmethod
        
        method clear takes nothing returns nothing 
            call DialogClear(this.dg)
            set this.msg = ""
        endmethod
        
        method registerClickEvent takes boolexpr b returns nothing
            if this.click == null then
                set instance[GetHandleId(this.dg)] = this
                set this.click = CreateTrigger()
                call TriggerRegisterDialogEvent(this.click, this.dg)
            endif
            call TriggerAddCondition(this.click, b)
        endmethod
        
        method destroy takes nothing returns nothing 
            debug if this.dg == null then
            debug     call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "[Dialog] Attempted to destroy null dialog.")
            debug endif
            
            if this.click != null then
                call DestroyTrigger(this.click)
                call instance.remove(GetHandleId(this.dg))
                set this.click = null
            endif
            
            call DialogClear(this.dg)
            call DialogDestroy(this.dg)
            set this.dg = null
            call this.deallocate()
        endmethod
    
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.dg = DialogCreate()
            return this
        endmethod
        
        implement DialogInit
    endstruct
    
endlibrary

Sample:
JASS:
scope Testing initializer Init 
    // Creates a dialog with buttons for different fruit

    // When you click one, it will make an item that has absolutely 
    // nothing to do with the fruit you chose

    globals
        private button array buttons
    endglobals
    
    private function DialogEvent takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        
        call BJDebugMsg("Clicked: " + I2S(log))
        call BJDebugMsg("Player Name: " + GetPlayerName(GetTriggerPlayer()))
        
        if b == buttons[0] then
            call CreateItem('afac', 0, 0)
        elseif b == buttons[1] then
            call CreateItem('spsh', 0, 0)
        elseif b == buttons[2] then
            call CreateItem('ajen', 0, 0)
        elseif b == buttons[3] then
            call CreateItem('bgst', 0, 0)
        endif
                
        call log.destroy()
        set b = null
        return false 
    endfunction
    
    private function AppendHotkey takes string source, string hotkey returns string
        return "|cffffcc00[" + hotkey + "]|r " + source
    endfunction

    private function DialogTest takes nothing returns nothing 
        local Dialog log = Dialog.create()
        
        set log.title  = "Fruit"
        set buttons[0] = log.addButton(AppendHotkey("Apple",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("Banana",    "B"), 'B')
        set buttons[2] = log.addButton(AppendHotkey("Orange",    "C"), 'C')
        set buttons[3] = log.addButton(AppendHotkey("Hyoi Pear", "D"), 'D')
        
        call log.displayAll(true)
        call log.registerClickEvent(Condition(function DialogEvent))
    endfunction

    private function Init takes nothing returns nothing 
        call TimerStart(CreateTimer(), 0, false, function DialogTest)
    endfunction
endscope
 
Last edited:
Level 30
Joined
Jul 10, 2007
Messages
6,307
If Alloc was optional, he'd be writing identical code to Alloc :\. I don't personally see the point to writing identical code when that code already exists in a resource :p. Alloc also supports things like leak checking.

The same goes for ErrorMessage. It kind of sets a style that was influenced by mag's tutorial on error messages.

Writing special internal resources for a resource isn't a very good practice ^)^


but w/e, I'm a noob, so don't mind me : )


My only comment is: why you no use event? : (. You are missing some functionality as a result, like registering triggers :\. Please add a register trigger function if you are very averse to using Event ;). Ofc, if you used Event, it'd all be written and the API would be provided ^_^.

edit
I'm not sure if this would work or not, by try using private keyword allocate/deallocate to hide the methods from Alloc. You should probably keep the leak checking API open tho. If that wouldn't work, you should try putting it in a private struct and using that struct purely for instancing ;). This is just for proper encapsulation ofc.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307

TriggerHappy

Spell Moderator
Level 37
Joined
Jun 23, 2007
Messages
4,027
Level 22
Joined
Sep 24, 2005
Messages
4,820
Would be nice if you could add a list of integer hotkeys too, maybe on the main post or as a comment.
 

PurgeandFire

Spell Moderator
Level 43
Joined
Nov 11, 2006
Messages
7,501
TriggerHappy said:
I like it but I really think Alloc and ErrorMessage should be optional and the members db and click should be accessible (with better names).

That should be doable. I think the one I use in my map doesn't use either of them.

And I guess you mean the dialog (dg), right (instead of db)?

Would be nice if you could add a list of integer hotkeys too, maybe on the main post or as a comment.

For most of them, you can just use 'A', 'B', 'C', 'D', etc. But with this thread here, maybe there are others. We'll see. :thumbs_up:
 
Last edited:

PurgeandFire

Spell Moderator
Level 43
Joined
Nov 11, 2006
Messages
7,501
Gonna update this?

One feature I would like is to have constants representing unusual hotkeys (that can't be defined through raw codes) like escape, home, end ect...

Done. Updated to 1.1.0.0. Special thanks to DysfunctionaI and his awesome list of hotkeys:
http://www.hiveworkshop.com/forums/miscellaneous-tutorials-456/extended-hotkeys-spacebar-etc-245278/

I added those hotkeys and I removed some unnecessary error checking, and removed the dependency on Alloc and ErrorMessage.

Shift doesn't seem to work, but I tested all the other ones and they all work. I can't guarantee what will happen in multiplayer if you use a key that is already bound to something (because dialogs have different behavior in multiplayer-they don't pause the game), so idk whether it will press the dialog button or do what it is bound to (or if it does both). But they work fine in single player without any issues.
 
Level 15
Joined
Aug 7, 2013
Messages
1,338
One of the great parts of this library is the globals you provide for the key values, which of course you get some credit for for compiling them into a resource, but many thanks to DysfuncionaI's hard work at finding these values.

This definitely does well at doing one well-defined task well, namely simplifying the interface / API to create dialogs in WC3 via JASS.

The other alternative, Bannar's, complements this one I think, it's a lot heavier and does a lot more stuff, going so far as to create a wrapper for the button native.

Thanks again for this resource!
 
Level 23
Joined
Mar 19, 2008
Messages
3,133
My resource is not meant to deprecate PnF' wrapper. My intention was to extend functionality of dialog and button natives because they clearly lack some. In op I've posted why/when you should use it. It it still usefull as simple wrapper, but in situation when at least you are playing with connect methods to link multiple dialog pages e.g to create some kind of book/questions and answers template.

In short: you want simple, more convenient way of dealing with dialogs: you use this one. However, when you want some additional features and easy event handling, my snippet should be perfect for you.

I've not added hotkeys for reason similar to order ids. In my opinion hotkeys/orders are usefull in many cases, thus its better to just have standalone library for such ^)^
 

PurgeandFire

Spell Moderator
Level 43
Joined
Nov 11, 2006
Messages
7,501
@sethmachine: Thanks for the kind words. :)

@Bannar: Yep. This is purely a wrapper, so it doesn't offer any special functionality. Yours is geared for pages and button control.

As for hotkeys, I considered having it separate, but I don't think any other natives use those integers. You can use them in the object editor though. I included them in this script partially as documentation/a reference. If anyone needs the codes in the future, they can refer back to this. :)
 
Level 12
Joined
Mar 24, 2013
Messages
1,105
So I'm still rather new to vJass so go easy :p.

I'm trying to use this to let players select their game mode preferences.

However, after a timeout I want to destroy/hide all the dialogs that have yet to be clicked. My issue is that I'm not entirely sure how to reference them.

Here's my dialog code just in case it's somehow pertinent.


JASS:
library DialogSetUp requires MessageAll

    globals
        private button array buttons
        integer array voteRound
        integer array voteTime
        integer array votePower
        integer array voteType
        boolean tooLate = false
    endglobals
   
   
private function AppendHotkey takes string source, string hotkey returns string
        return "|cffffcc00[" + hotkey + "]|r " + source
endfunction
   
private function TypeButton takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        local player p = Dialog.getPlayer()
        local string s
       
        if b == buttons[0] then
            set s = "Solo"
            set voteType[0] = voteType[0] + 1
        elseif b == buttons[1] then
            set s = "Teams"
            set voteType[1] = voteType[1] + 1
        endif
       
        call Message(GetRealName(p) + " voted for " + s)
        call log.destroy()
        set b = null
        set p = null
        return false
endfunction        
   
private function TypeDialog takes player p returns nothing 
        local Dialog log = Dialog.create()
       
        set log.title  = "Game Type?"
        set buttons[0] = log.addButton(AppendHotkey("Standard (Solo)",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("Reds versus Blues (Teams)",    "B"), 'B')
       
        call log.display(p, true)
        call log.registerClickEvent(Condition(function TypeButton))
endfunction    

private function PowerUpButton takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        local player p = Dialog.getPlayer()
        local string s
       
        if b == buttons[0] then
            set s = "Enable"
            set votePower[0] = votePower[0] + 1
        elseif b == buttons[1] then
            set s = "Disable"
            set votePower[1] = votePower[1] + 1
        endif
       
        call Message(GetRealName(p) + " voted to " + s + " Power-Ups")
        call log.destroy()
        set b = null
        call TypeDialog(p)
        set p = null
        return false
endfunction  
   
private function PowerUpDialog takes player p returns nothing 
        local Dialog log = Dialog.create()
       
        set log.title  = "Power-Ups?"
        set buttons[0] = log.addButton(AppendHotkey("Enable",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("Disable",    "B"), 'B')
       
        call log.display(p, true)
        call log.registerClickEvent(Condition(function PowerUpButton))
endfunction      
   
private function TimeButton takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        local player p = Dialog.getPlayer()
        local string s
       
        if b == buttons[0] then
            set s = "3"
            set voteTime[0] = voteTime[0] + 1
        elseif b == buttons[1] then
            set s = "5"
            set voteTime[1] = voteTime[1] + 1
        elseif b == buttons[2] then
            set s = "7"
            set voteTime[2] = voteTime[2] + 1
        elseif b == buttons[3] then
            set s = "10"
            set voteTime[3] = voteTime[3] + 1
        endif
       
        call Message(GetRealName(p) + " voted for " + s + " minutes per round")
        call log.destroy()
        set b = null
        call PowerUpDialog(p)
        set p = null
        return false
endfunction      
   
 private function TimePerRoundDialog takes player p returns nothing 
        local Dialog log = Dialog.create()
       
        set log.title  = "Time per Round?"
        set buttons[0] = log.addButton(AppendHotkey("3 minutes",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("5 minutes",    "B"), 'B')
        set buttons[2] = log.addButton(AppendHotkey("7 minutes",    "C"), 'C')
        set buttons[3] = log.addButton(AppendHotkey("10 minutes", "D"), 'D')
       
        call log.display(p, true)
        call log.registerClickEvent(Condition(function TimeButton))
endfunction  
   
private function RoundsButton takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        local player p = Dialog.getPlayer()
        local string s
        local string s2
       
        if b == buttons[0] then
            set s = "1"
            set s2 = " round"
            set voteRound[0] = voteRound[0] + 1
        elseif b == buttons[1] then
            set s = "3"
            set s2 = " rounds"
            set voteRound[1] = voteRound[1] + 1
        elseif b == buttons[2] then
            set s = "5"
            set s2 = " rounds"
            set voteRound[2] = voteRound[2] + 1
        elseif b == buttons[3] then
            set s = "10"
            set s2 = " rounds"
            set voteRound[3] = voteRound[3] + 1
        elseif b == buttons[4] then
            set s = "100"
            set s2 = " rounds"
            set voteRound[4] = voteRound[4] + 1
        endif
       
        call Message(GetRealName(p) + " voted for " + s + s2)
        call log.destroy()
        set b = null
        call TimePerRoundDialog(p)
        set p = null
        return false
endfunction    
   
private function RoundsDialog takes nothing returns nothing 
        local Dialog log = Dialog.create()
       
        set log.title  = "Number of Rounds?"
        set buttons[0] = log.addButton(AppendHotkey("1",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("3",    "B"), 'B')
        set buttons[2] = log.addButton(AppendHotkey("5",    "C"), 'C')
        set buttons[3] = log.addButton(AppendHotkey("10", "D"), 'D')
        set buttons[4] = log.addButton(AppendHotkey("100", "E"), 'E')
       
        call log.displayAll(true)
        call log.registerClickEvent(Condition(function RoundsButton))
endfunction

 function CreateDialog takes nothing returns nothing 
         call TimerStart(CreateTimer(), 0, false, function RoundsDialog)
endfunction  
   
endlibrary
 

PurgeandFire

Spell Moderator
Level 43
Joined
Nov 11, 2006
Messages
7,501
@pOke If you call log.destroy(), it will destroy it for all players. So you have to be a bit careful. If you test your code now, once one person clicks a button, it will destroy/clear the dialog for everyone.

You probably don't want that. :p So what should you do? First, you'll probably want to count the number of active players, and store it in a global. Something like this:
JASS:
globals 
    private integer numberOfPlayers = 0

    // ... other variables ...

endglobals

// ... 

function CreateDialog takes nothing returns nothing
    local integer i = 0
    set numberOfPlayers = 0
    loop
        exitwhen i == 12
        if GetPlayerController(Player(i)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
            set numberOfPlayers = numberOfPlayers + 1
        endif
        set i = i + 1
    endloop

    call TimerStart(CreateTimer(), 0, false, function RoundsDialog)
endfunction

All this does is it counts the number of players who are a user (not a computer/observer) and are playing (the player slot is used).

Next, to store the dialog, you can also create a global. Since this is being used for all players, you don't need to worry about instanceability or anything. Just add it like so:
JASS:
globals
    private Dialog roundsDialog = 0
endglobals

And then in function RoundsDialog, just assign our global to your locally created one (or use it directly):
JASS:
private function RoundsDialog takes nothing returns nothing 
    local Dialog log = Dialog.create()
       
    set log.title = "Number of Rounds?"
    set buttons[0] = log.addButton(AppendHotkey("1",   "A"), 'A')
    set buttons[1] = log.addButton(AppendHotkey("3",   "B"), 'B')
    set buttons[2] = log.addButton(AppendHotkey("5",   "C"), 'C')
    set buttons[3] = log.addButton(AppendHotkey("10", "D"), 'D')
    set buttons[4] = log.addButton(AppendHotkey("100", "E"), 'E')
       
    call log.displayAll(true)
    call log.registerClickEvent(Condition(function RoundsButton))
       
    // Add this line!
    set roundsDialog = log
endfunction

Now back to our first issue. You don't want to destroy it until everyone has casted a response (or when your timer expires, which we will add later). So let's create another global to keep track of the number of players who have voted. When that vote count is equal to the number of players, we know we can destroy it:
JASS:
globals
    private integer roundsVoteCount = 0
endglobals

// ...

private function RoundsButton takes nothing returns boolean
    // ... your code

    set roundsVoteCount = roundsVoteCount + 1

    // check if all players have voted
    if roundsVoteCount == numberOfPlayers then
        call log.destroy()
    endif
endfunction

With all this in place, it is actually really easy to make a timer! Something like this would work:
JASS:
private function RoundsDialogExpired takes nothing returns nothing
    // if someone has not voted yet, destroy the timer
    if roundsVoteCount < numberOfPlayers then
        call roundsDialog.destroy()
       
        // do other stuff
    endif

    call DestroyTimer(GetExpiredTimer())
endfunction

private function RoundsDialog takes nothing returns nothing 
    local Dialog log = Dialog.create()
       
    set log.title = "Number of Rounds?"
    set buttons[0] = log.addButton(AppendHotkey("1",   "A"), 'A')
    set buttons[1] = log.addButton(AppendHotkey("3",   "B"), 'B')
    set buttons[2] = log.addButton(AppendHotkey("5",   "C"), 'C')
    set buttons[3] = log.addButton(AppendHotkey("10", "D"), 'D')
    set buttons[4] = log.addButton(AppendHotkey("100", "E"), 'E')
       
    call log.displayAll(true)
    call log.registerClickEvent(Condition(function RoundsButton))
       
    set roundsDialog = log

    // Add this line!
    // In this example, we start a timer for 30 seconds to expire the vote
    call TimerStart(CreateTimer(), 30, false, function RoundsDialogExpired)
endfunction

That should be it! There are other ways to do it (e.g. one dialog per player), but this seemed like a pretty intuitive solution to me. Let me know if you have more questions! :)
 
Level 12
Joined
Mar 24, 2013
Messages
1,105
Thanks so much for the very detailed response Purge.

My intent was actually to do a dialog per player but I suppose syncing each players selection is just fine.

I think I've got it working, I did 2 things slightly different, and to make it even more difficult I named things slightly different :p. But I think it shouldn't be too hard to follow.

But I have a question about these 2 functions you showed.

JASS:
private function RoundsButton takes nothing returns boolean
   // ... your code

   set roundsVoteCount = roundsVoteCount + 1

   // check if all players have voted
   if roundsVoteCount == numberOfPlayers then
       call log.destroy()
   endif
endfunction

So in this one, I think it probably doesn't matter, but rather destroy here I'm assuming we can just clear the global (which you saved as roundsDialog and I renamed below as whichDialog as I plan to reuse and I wanted a more generic name) in the timer expiration function named RoundsDialogExpired.

JASS:
private function RoundsDialogExpired takes nothing returns nothing
   // if someone has not voted yet, destroy the timer
   if roundsVoteCount < numberOfPlayers then
       call roundsDialog.destroy()
     
       // do other stuff
   endif

   call DestroyTimer(GetExpiredTimer())
endfunction

I'm not entirely sure I understand this one, because if the timer has expired we want to destroy roundsDialog regardless. (Again this is renamed to whichDialog below) Meaning that if we're executing this function then it should always be clearing roundsDialog.

If you didn't follow my confusion, here is my code with my revisions.

JASS:
private function TimePerRoundDialog takes nothing returns nothing
        local Dialog log = Dialog.create()
     
        set log.title  = "Time per Round?"
        set buttons[0] = log.addButton(AppendHotkey("3 minutes",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("5 minutes",    "B"), 'B')
        set buttons[2] = log.addButton(AppendHotkey("7 minutes",    "C"), 'C')
        set buttons[3] = log.addButton(AppendHotkey("10 minutes", "D"), 'D')
     
        call log.displayAll(true)
        call log.registerClickEvent(Condition(function TimeButton))

        // start new timers/ reset vote count
endfunction
 
private function RoundsDialogExpired takes nothing returns nothing
        call whichDialog.destroy()
// do other stuff //
        call DestroyTimer(t)
        call DestroyTimerDialog(td)
        call TimePerRoundDialog()
endfunction      
 
private function RoundsButton takes nothing returns boolean
        local Dialog  log = Dialog.getClickedDialog()
        local button  b   = Dialog.getClickedButton()
        local player p = Dialog.getPlayer()
        local string s
        local string s2
     
        if b == buttons[0] then
            set s = "1"
            set s2 = " round"
            set voteRound[0] = voteRound[0] + 1
        elseif b == buttons[1] then
            set s = "3"
            set s2 = " rounds"
            set voteRound[1] = voteRound[1] + 1
        elseif b == buttons[2] then
            set s = "5"
            set s2 = " rounds"
            set voteRound[2] = voteRound[2] + 1
        elseif b == buttons[3] then
            set s = "10"
            set s2 = " rounds"
            set voteRound[3] = voteRound[3] + 1
        elseif b == buttons[4] then
            set s = "100"
            set s2 = " rounds"
            set voteRound[4] = voteRound[4] + 1
        endif
     
        call Message(GetRealName(p) + " voted for " + s + s2)
        set voteCount = voteCount + 1
        if voteCount == numberOfPlayers then
            call DestroyTimer(t)
            set t = CreateTimer()
            call TimerStart(t, 0, false, function RoundsDialogExpired)
        endif
        set b = null
        set p = null
        return false
endfunction  
 
private function RoundsDialog takes nothing returns nothing
        local Dialog log = Dialog.create()
     
        set log.title  = "Number of Rounds?"
        set buttons[0] = log.addButton(AppendHotkey("1",     "A"), 'A')
        set buttons[1] = log.addButton(AppendHotkey("3",    "B"), 'B')
        set buttons[2] = log.addButton(AppendHotkey("5",    "C"), 'C')
        set buttons[3] = log.addButton(AppendHotkey("10", "D"), 'D')
        set buttons[4] = log.addButton(AppendHotkey("100", "E"), 'E')
     
        call log.displayAll(true)
        call log.registerClickEvent(Condition(function RoundsButton))
        set whichDialog = log
     
        set t = CreateTimer()
        set td = CreateTimerDialog(t)
        call TimerStart(t, 30, false, function RoundsDialogExpired)
endfunction
 
Top