1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Welcome to the new Hive! Be advised that we're still working on the site. There are still many rough edges, so please bear with us.
    Dismiss Notice
  3. The member Kam is making HIVE coasters. Take a look. For every coaster you buy, Hive gets $1.
    Dismiss Notice
  4. The poll for Icon Contest #14 - Seven Deadly Sins is out!
    Dismiss Notice
  5. The 25th Texturing Contest has started! Contestants are to create a skin representing a dark elf person/being or any construct related to it using the vanilla models or the custom ones found on the site.
    Dismiss Notice
  6. Buy it, use it, break it, fix it, trash it, change it, mail - upgrade it. Join (Optionally) Paired Techtree Contest #11 - Techno Magic now!
    Dismiss Notice
  7. Voting squad, line up! Cast your vote on the poll for Modeling Contest #29 - Squads!
    Dismiss Notice
  8. Hero Contest #8 is up and running! This time it's a joint contest between artists and coders. Go here for team matchmaking.
    Dismiss Notice
  9. The poll for the theme of our StarCraft II Terraining Contest is up. Cast your note now!
    Dismiss Notice
  10. The ninth Concept Art Contest has launched. Enter now!
    Dismiss Notice
  11. The music contest has been revived! Vote for your favourite theme in the Music Contest #5 Theme Poll
    Dismiss Notice

[System] Dialog

Discussion in 'Graveyard' started by The_Witcher, Apr 5, 2012.

  1. The_Witcher

    The_Witcher
    Joined:
    Dec 29, 2008
    Messages:
    228
    Code (vJASS):
    library Dialog requires Table
    // Dialog System by The_Witcher
    //
    //  Some simple wrappers for easier dialog handling
    //
    //  Feel free to use!
    //
    //      requires Table by Bribe: [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
    //
        globals
            private constant integer MAX_BUTTONS = 12 //Max amount of buttons per dialog
        endglobals
    //  New Things:
    //
    //      function GetTriggerButton takes nothing returns DialogButton
    //
    //
    //      struct DialogButton
    //          operator [] takes button b returns dialogButton    (useful to get the data of the clicked button)
    //          string text        [read-only]                     (the text on the button)
    //          integer hotkey     [read-only]                     (the hotkey for the button)
    //          integer index      [read-only]                     (the button's position in the dialog, first is 0)
    //          integer data                                       (user data for you)
    //
    //          method create takes Dialog dlg, string title, integer hotkey returns DialogButton
    //
    //          method registerClick takes trigger t returns nothing
    //
    //
    //      struct Dialog
    //          operator [] takes integer i returns dialogButton
    //          string text                                                                (the dialog's title, set this to another value to change the dialogs title aswell)
    //          integer data                                                               (user data for you)
    //
    //          method create takes string title returns Dialog                            (creates a new dialog with the given title)
    //          method addButton takes string text, integer hotkey returns dialogButton    (adds a new button and returns it)
    //          method clear takes nothing returns nothing                                 (clears the dialog of all buttons)
    //
    //          method display takes player toPlayer, boolean flag returns nothing
    //          method displayToAll takes boolean flag returns nothing
    //
    //          method register takes code func returns nothing                            (fires the given function whenever a button for this dialog is clicked)
    //
    //
    //==============================================================================
    //========================= System Code ========================================
    //==============================================================================

        private module InitM
            private static method onInit takes nothing returns nothing
                set .buttons = Table.create()
            endmethod
        endmodule

        struct DialogButton
            private static Table buttons
            private button b
            readonly string text
            readonly integer hotkey
            readonly integer index
            integer data
           
            static method operator [] takes button b returns DialogButton
                return .buttons[GetHandleId(b)]
            endmethod
       
            method registerClick takes trigger t returns nothing
                call TriggerRegisterDialogButtonEvent(t, .b)
            endmethod
       
            static method create takes Dialog dlg, string title, integer hotkey returns DialogButton
                local DialogButton this = DialogButton.allocate()
                set .b = DialogAddButton(dlg.d, title, hotkey)
                set .hotkey = hotkey
                set .text = title
                set .index = dlg.count
                set .buttons[GetHandleId(.b)] = this
                call dlg.add(this)
                return this
            endmethod
           
            method destroy takes nothing returns nothing
                call .buttons.remove(GetHandleId(.b))
            endmethod
           
            implement InitM
        endstruct

        struct Dialog extends array
            private static integer instanceCount = 0
            private static Dialog recycle = 0
            private Dialog recycleNext
            private static DialogButton array buttons
            readonly dialog d
            readonly integer count
            private string s
            private trigger t
            integer data

            method operator [] takes integer i returns DialogButton
                return .buttons[this * MAX_BUTTONS + i]
            endmethod
       
            method operator text takes nothing returns string
                return .s
            endmethod
            method operator text= takes string s returns nothing
                set .s = s
                call DialogSetMessage(.d, s)
            endmethod
       
            method clear takes nothing returns nothing
                loop
                    exitwhen .count == 0
                    set .count = .count - 1
                    call .buttons[this * MAX_BUTTONS + .count].destroy()
                endloop
                call DialogClear(.d)
            endmethod
       
            method display takes player toPlayer, boolean flag returns nothing
                call DialogDisplay(toPlayer, .d, flag)
            endmethod
            method displayToAll takes boolean flag returns nothing
                call DialogDisplay(GetLocalPlayer(), .d, flag)
            endmethod
       
            method register takes code func returns nothing
                call TriggerAddAction(.t, func)
            endmethod
           
            method add takes DialogButton b returns nothing
                if .count < MAX_BUTTONS then
                    set .buttons[this * MAX_BUTTONS + .count] = b
                    set .count = .count + 1
                else
                    debug call BJDebugMsg("DIALOG ERROR: More than 12 buttons were added to a dialog.")
                endif
            endmethod
       
            method addButton takes string text returns DialogButton
                return .addHotkeyButton( text, 0)
            endmethod
           
            method addHotkeyButton takes string text, integer hotkey returns DialogButton
                return DialogButton.create(this, text, hotkey)
            endmethod
       
            static method create takes string title returns Dialog
                local Dialog this
                if (.recycle == 0) then
                    set .instanceCount = .instanceCount + 1
                    set this = .instanceCount
                else
                    set this = .recycle
                    set .recycle = .recycle.recycleNext
                endif
                set .d = DialogCreate()
                set .count = 0
                set .s = title
                call DialogSetMessage(.d, title)
                set .t = CreateTrigger()
                call TriggerRegisterDialogEvent(.t, .d)
                return this
            endmethod
           
            method destroy takes nothing returns nothing
                call .clear()
                call DestroyTrigger(.t)
                call DialogDestroy(.d)
                set .recycleNext = .recycle
                set .recycle = this
            endmethod
        endstruct
       
        function GetTriggerButton takes nothing returns DialogButton
            return DialogButton[GetClickedButton()]
        endfunction

    endlibrary


    //Code indented using The_Witcher's Script Language Aligner
    //Download the newest version and report bugs at [url]www.hiveworkshop.com[/url]
     
     
    Last edited: Nov 21, 2012
  2. Magtheridon96

    Magtheridon96
    Joined:
    Dec 12, 2008
    Messages:
    6,027
    Nice.

    Bro-tips:
    • dialogButton -> DialogButton
    • GetTriggerButton()
      >
      DialogButton[GetClickedButton()]
    • Rename onDestroy to destroy and move it to the top of the struct to remove unnecessary trigger evaluations.
     
  3. Adiktuz

    Adiktuz
    Joined:
    Oct 16, 2008
    Messages:
    9,699
    Pretty neat... :)

    Please add a method to the dialog struct such that we can add our own buttons that we created using the DialogButton struct...
     
  4. The_Witcher

    The_Witcher
    Joined:
    Dec 29, 2008
    Messages:
    228
    Fixed all these things :D thank you!

    Sorry but this won't make sense, because a button is always bound to a dialog!
    Nevertheless I improved the DialogButton struct for you, so create is now different and instantly adds the created button to the dialog:
    Code (vJASS):
    create takes Dialog dlg, string title, integer hotkey returns DialogButton
     
  5. Adiktuz

    Adiktuz
    Joined:
    Oct 16, 2008
    Messages:
    9,699
    nice... that's just the same way as the suggestion I guess... It will lead to the same result I'm trying to obtain... XD...
     
  6. Bribe

    Bribe

    Code Moderator

    Joined:
    Sep 26, 2009
    Messages:
    7,336
    Please use JASS precendence when dealing with structs. Calling .clear from .destroy should throw a syntax error if you had your JassHelper properly configured, because .clear needs to be above the .destroy method.
     
  7. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    You sure "fixed" it. Just do whatever people tell you.
    You replacement of
    onDestroy
    with
    destroy
    just destroyed the the recycle ability of your struct.

    Here have an example.
    Code (vJASS):

    library Yoink initializer init
        private struct foo
            method destroy takes nothing returns nothing
                call BJDebugMsg("foo destroy")
            endmethod
           
            static method create takes nothing returns thistype
                local thistype this = allocate()
                call BJDebugMsg("foo new: "+ I2S(integer(this)))
                return this
            endmethod
        endstruct
       
        private struct bar
            method onDestroy takes nothing returns nothing
                call BJDebugMsg("bar destroy")
            endmethod
       
            static method create takes nothing returns thistype
                local thistype this = allocate()
                call BJDebugMsg("bar bew: "+ I2S(integer(this)))
                return this
            endmethod
        endstruct

        private function init takes nothing returns nothing
            call TriggerSleepAction(0)
           
            call foo.create().destroy()
            call bar.create().destroy()
           
            call foo.create().destroy()
            call bar.create().destroy()
           
            call foo.create().destroy()
            call bar.create().destroy()
           
            call foo.create().destroy()
            call bar.create().destroy()
        endfunction
    endlibrary
     


    Now guess what this will print.

    foo new: 1
    foo destroy
    bar new: 1
    bar destroy
    foo new: 2
    foo destroy
    bar new: 1
    bar destroy
    foo new: 3
    foo destroy
    bar new: 1
    bar destroy
    foo new: 4
    foo destroy
    bar new: 1
    bar destroy

    Just for the record: the Dialog create/destroy methods:
    Code (vJASS):

    struct Dialog
            method destroy takes nothing returns nothing
                call .clear()
                call DestroyTrigger(.t)
                call DialogDestroy(.d)
            endmethod
    // [...]        
            static method create takes string title returns Dialog
                local Dialog this = Dialog.allocate()
                set .d = DialogCreate()
                set .count = 0
                set .s = title
                call DialogSetMessage(.d, title)
                set .t = CreateTrigger()
                call TriggerRegisterDialogEvent(.t, .d)
                return this
            endmethod
    endstruct

     
     
  8. Adiktuz

    Adiktuz
    Joined:
    Oct 16, 2008
    Messages:
    9,699
    its because you didn't call .deallocate on the destroy method...

    since it overwrites the default destroy method you should have a .deallocate call there, just as the create method has a .allocate call...
     
  9. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    That would fuck up other mechanisms.
    Take this example:
    Code (vJASS):

    library Nah initializer init
     private struct super1
            method destroy takes nothing returns nothing
                call BJDebugMsg("super1 destroy")
                call deallocate()
            endmethod
        endstruct
       
        private struct sub1 extends super1
            method destroy takes nothing returns nothing
                call BJDebugMsg("sub1 destroy")
                // the following don't change anything being printed
                // see below for reason
                // in fact, they get compiled to the same code
                // and one of them is necessary for recycling
                // call this.deallocate()
                // call super.destroy()

            endmethod
        endstruct
       
        private struct super2
            method onDestroy takes nothing returns nothing
                call BJDebugMsg("super2 destroy")
            endmethod
        endstruct
       
        private struct sub2 extends super2
            method onDestroy takes nothing returns nothing
                call BJDebugMsg("sub2 destroy")
            endmethod
        endstruct

        private function init takes nothing returns nothing
            local super1 x
            local super2 y

            call TriggerSleepAction(0)

            set x = sub1.create()
            // this calls super1.destroy, which does -of course- not know about other instances.
            // the mechanism for that is, you say it, the onDestroy method
            call x.destroy()

            set y = sub2.create()
            call y.destroy()
        endfunction
    endlibrary
     


    Guess again what this will print.
    super1 destroy
    sub2 destroy
    super2 destroy
     
  10. Adiktuz

    Adiktuz
    Joined:
    Oct 16, 2008
    Messages:
    9,699
    if it only happens when you extend the struct, I guess that is fine... just make a note that says, never extend this struct...
     
  11. Bribe

    Bribe

    Code Moderator

    Joined:
    Sep 26, 2009
    Messages:
    7,336
    It's not a struct that you'd want to extend anyway.

    Polymorphism in vJass is not a good coding practice when you look at how it compiles.
     
  12. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    That's a dumb and arbitrary restriction with no real gain.
     
  13. Adiktuz

    Adiktuz
    Joined:
    Oct 16, 2008
    Messages:
    9,699
    yeah, I don't see the use of extending the Dialog struct...

    I think its an issue of efficiency versus functionality... Efficiency coz of the less evaluations due to not using onDestroy and functionality in terms of extension and maybe recycling...
     
  14. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    I have some imagination left.

    And you know, killing type safety. Not following conventions leading to strange behavior (either make extending a compile time error or allow extending).

    Don't tell what structs i extend.
    It's the cleanest way to add functionality to my dialogs.
    Want player-colored buttons: just extend and change the text field.
    Want more than 12 buttons via pages: fucking extend and change the addButton method.

    And holy fuck, why should i care how my dialogs (!!) are compiled?!
    They neither add megs of jasscode nor have any performance penalty.
    And if they do, i'll fix it, as soon and not before i've got problems.
    I'm pretty sure Dialogs don't fall into the 3%.


    I, as a programmer, want to have nice code to work with instead of nice compiled code.
    In a perfect world we would have both, but we aren't so i choose the former.
     
  15. Magtheridon96

    Magtheridon96
    Joined:
    Dec 12, 2008
    Messages:
    6,027
    Just for the record, my map went down by 600KB when I stopped extending structs.
     
  16. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    That pretty much falls under premature optimization.
    If, and only if, these 600kB stop your map from working it's perfectly fine to optimize it.
     
  17. Magtheridon96

    Magtheridon96
    Joined:
    Dec 12, 2008
    Messages:
    6,027
    Well, honestly, you made some pretty good points here.
    But come on, who would need to extend a Dialog struct? :/

    I know you're being imaginative and thinking of all these scenarios in which you can actually do something pretty cool with it, but.. well, I don't know what to say.

    Maybe he can include a textmacro and use delegates so you can have the results obtained from extending without actually extending ^.^
     
  18. LeP

    LeP
    Joined:
    Feb 13, 2008
    Messages:
    286
    I already gave examples why you would extend this in particular.
    But it's not only about a Dialog but about easy extendability in general.
    Why restrict it without any good reason? And if you do, do it via extends array, so that i atleast get a compile-time error.

    Textmacro + delegate is no real option.
    Textmacros are for repetitive structures of code.
    Delegates are cool, but too restricted in vJass. They are good for reusing but since they don't fulfill interface requirements you can't really use them in your polymorph code without giving up some of the good things you get from delegates. Modules work better in that regard under vJass (that is, reusing code which fulfills interface rules).

    But both have there uses. They aren't exclusive.
     
    Last edited: Apr 6, 2012
  19. The_Witcher

    The_Witcher
    Joined:
    Dec 29, 2008
    Messages:
    228
    Come on guys :D
    I will go back to the onDestroy method to keep the structure simple!
    There won't be spammings of dialog destroys i think,
    So this should be a solution everybody can live with^^

    @Bribe: what does jass precendence mean? Sorry i am from germany^^
    and i didn't change any configuration for jasshelper... I simply updated to cohadars mod...
     
  20. Bribe

    Bribe

    Code Moderator

    Joined:
    Sep 26, 2009
    Messages:
    7,336
    Precedence is when you can call function "foo" from above function "bar", but "bar" can't call "foo" because "bar" is below "foo".

    Code (vJASS):

    function foo...

    function bar...