1. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

[System] Track

Discussion in 'JASS Resources' started by PurgeandFire, Oct 21, 2011.

  1. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Track

    Yes, another remake of an old system. :jd:

    Although, I believe this one is a bit warranted. It is basically Trackable2 but improved to be more efficient in terms of handles (only creates for active players) and to allow for mass-generation without freezing. (because Trackable2 would create a platform destructable regardless of whether or not the z value was 0)

    It was a great system that I used a lot, so props to him, just thought it could use a little remake to make things more current.

    I didn't use Trackable2 as the system name because the API's differ a little bit. So I named it "Track".

    Here is the code:
    Code (vJASS):
    library Track /* v3.1.0.0
    *****************************************************************************
    *
    *  Manages trackable objects, allowing for easy event registrations, data
    *  retrieval, and the capability of retrieving which player interacted with
    *  the trackable.
    *
    *****************************************************************************
    *
    *   */
    uses/*
    *
    *       */
    Table /*   hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
    *
    *****************************************************************************
    *
    *  SETTINGS
    */

    globals
        private constant integer PLATFORM = 'OTip'
    endglobals
    /*
    *****************************************************************************
    *
    *  FUNCTIONS
    *
    *      CreateTrack( string modelPath, real x, real y, real z, real facing ) returns Track
    *        - Creates a trackable of modelPath at coordinates ( x, y, z ) with
    *        - "facing" in radians. Returns the trackable instance.
    *
    *      CreateTrackForPlayer( string modelPath, real x, real y, real z, real facing, player who ) returns Track
    *        - Same as function above, but creates it for one player.
    *
    *      RegisterAnyClickEvent( code c ) returns nothing
    *      RegisterAnyHoverEvent( code c ) returns nothing
    *        - Fires "c" when any trackable is clicked or hovered.
    *
    *      RegisterClickEvent( Track obj, code c ) returns nothing
    *      RegisterHoverEvent( Track obj, code c ) returns nothing
    *        - Fires "c" when the trackable of "obj" is clicked or hovered respectively.
    *      RegisterInteractEvent( Track obj, code c ) returns nothing
    *        - Fires "c" when the trackable of "obj" is clicked or hovered.
    *        - Use GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK
    *          to differentiate between the two event occurrences. This
    *          method is more efficient on handles than the two above.
    *
    *      EnableTrackInstance( Track obj, boolean flag ) returns nothing
    *        - A disabled Track instance will not fire its events.
    *        - Track instances are enabled by default.
    *      IsTrackInstanceEnabled( Track obj ) returns boolean
    *        - Returns whether an instance is enabled.
    *
    *  EVENT RESPONSES
    *
    *      GetTriggerTrackInstance() returns Track
    *        - Returns the Track instance that had a player interaction.
    *      GetTriggerTrackable() returns trackable
    *        - Returns the trackable object that had a player interaction.
    *      GetTriggerTrackablePlayer() returns player
    *        - Returns the player that interacted with the trackable object.
    *
    *****************************************************************************
    *
    *   struct Track
    *
    *        static Track instance
    *           - The triggering instance of the event.
    *        static trackable object
    *           - The triggering trackable object of the event.
    *        static player tracker
    *           - The player who interacted with the trackable object.
    *
    *        readonly real x
    *        readonly real y
    *        readonly real z
    *        readonly real facing
    *        readonly string model
    *           - Instance properties.
    *           - Warning: the invisible platform has a default z-value
    *             of 2.94794. So if you input 0 into the system, it'll
    *             end up ~3 units above the ground. For an alternative model,
    *             see: [url]http://www.hiveworkshop.com/forums/2661555-post54.html[/url]
    *
    *        method operator enabled= takes boolean flag returns nothing
    *        method operator enabled takes nothing returns boolean
    *        
    *        static method create takes string modelPath, real x, real y, real z, real facing returns Track
    *        static method createForPlayer takes string modelPath, real x, real y, real z, real facing, player p returns Track
    *
    *        static method registerAnyClick takes code c returns nothing
    *        static method registerAnyHover takes code c returns nothing
    *
    *        method registerClick takes code c returns nothing
    *        method registerHover takes code c returns nothing
    *        method registerInteract takes code c returns nothing
    *
    *            - All equivalent to their function counterparts.
    *
    *****************************************************************************
    *    
    *    Credits
    *       - Azlier: Trackable2 (inspiration)
    *       - Arhowk: bugfix
    *       - Dalvengyr: bugfix from a typo.
    *       - Uberplayer: bugfix; info on the invisible platform Z issue.
    *
    ****************************************************************************/

       
        private module Init
            private static method onInit takes nothing returns nothing
                set thistype.TrackTable = Table.create()
            endmethod
        endmodule

        struct Track extends array
            private static trigger anyClick = CreateTrigger()
            private static trigger anyHover = CreateTrigger()
            private static Table TrackTable = 0
           
            static thistype  instance = 0
            static trackable object   = null
            static player    tracker  = null
           
            private static integer ic = 0
            private static integer ir = 0
            private thistype rn
           
            readonly real    x
            readonly real    y
            readonly real    z
            readonly real    facing
            readonly string  model
           
            private trigger  reg
            private trigger  onClick
            private trigger  onHover
            private Table    playerIndex
           
            boolean  enabled
           
            static method registerAnyClick takes code c returns nothing
                call TriggerAddCondition(.anyClick, Filter(c))
            endmethod
            static method registerAnyHover takes code c returns nothing
                call TriggerAddCondition(.anyHover, Filter(c))
            endmethod
           
            method registerClick takes code c returns nothing
                if .onClick == null then
                    set .onClick = CreateTrigger()
                endif
                call TriggerAddCondition(.onClick, Filter(c))
            endmethod
            method registerHover takes code c returns nothing
                if .onHover == null then
                    set .onHover = CreateTrigger()
                endif
                call TriggerAddCondition(.onHover, Filter(c))
            endmethod
            method registerInteract takes code c returns nothing
                call TriggerAddCondition(.reg, Filter(c))
            endmethod
           
            method destroy takes nothing returns nothing
                call TrackTable.remove(GetHandleId(.reg))
                call DestroyTrigger(.reg)
                call DestroyTrigger(.onClick)
                call DestroyTrigger(.onHover)
                call .playerIndex.destroy()
                set .rn = ir
                set ir  = this
            endmethod
           
            private static method onInteract takes nothing returns boolean
                set instance = TrackTable[GetHandleId(GetTriggeringTrigger())]
               
                if instance.enabled then
                    set object  = GetTriggeringTrackable()
                    set tracker = Player(instance.playerIndex[GetHandleId(object)])
                   
                    if GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK then
                        call TriggerEvaluate(instance.onHover)
                        call TriggerEvaluate(anyHover)
                    else
                        call TriggerEvaluate(instance.onClick)
                        call TriggerEvaluate(anyClick)
                    endif
                endif
               
                return false
            endmethod
           
            private static method createTrack takes string modelPath, real x, real y, real z, real facing, player j returns thistype
                local destructable dest = null
                local thistype     this = ir
                local integer      i    = 11
                local trackable tr
                local player p
                local string s
               
                /* Allocate */
                if this == 0 then
                    set ic   = ic + 1
                    set this = ic
                else
                    set ir = .rn
                endif
               
                /* Create platform to give the trackable a z-offset */
                if z != 0 then
                    set dest = CreateDestructableZ(PLATFORM, x, y, z, 0, 1, 0)
                endif
                if j != null then
                    set i = GetPlayerId(j)
                endif
               
                set .x = x
                set .y = y
                set .z = z
                set .enabled = true
                set .facing  = facing
                set .model   = modelPath
                set .reg     = CreateTrigger()
                set .onClick = null
                set .onHover = null
                set .playerIndex = Table.create()
               
                set TrackTable[GetHandleId(.reg)] = this
                call TriggerAddCondition(.reg, Condition(function thistype.onInteract))
               
                /* Create a separate trackable for each player playing */
                loop
                    set p = Player(i)
                    if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then
                        if GetLocalPlayer() == p then
                            set s = modelPath
                        else
                            set s = ""
                        endif
                        set tr = CreateTrackable(s, .x, .y, .facing)
                        call TriggerRegisterTrackableHitEvent(.reg, tr)
                        call TriggerRegisterTrackableTrackEvent(.reg, tr)
                        set .playerIndex[GetHandleId(tr)] = i
                        exitwhen j != null
                    endif
                    exitwhen i == 0
                    set i = i - 1
                endloop
               
                /* Remove the platform if it exists */
                if dest != null then
                    call RemoveDestructable(dest)
                    set dest = null
                endif
                set p  = null
                set tr = null
               
                return this
            endmethod
           
            static method create takes string modelPath, real x, real y, real z, real facing returns thistype
                return thistype.createTrack(modelPath, x, y, z, facing, null)
            endmethod
           
            static method createForPlayer takes string modelPath, real x, real y, real z, real facing, player p returns thistype
                if not (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER) then
                    return 0
                endif
                return thistype.createTrack(modelPath, x, y, z, facing, p)
            endmethod
           
            implement Init
        endstruct
       
        /* Function Wrappers */
       
        function CreateTrack takes string modelPath, real x, real y, real z, real facing returns Track
            return Track.create(modelPath, x, y, z, facing)
        endfunction
       
        function CreateTrackForPlayer takes string modelPath, real x, real y, real z, real facing, player who returns Track
            return Track.createForPlayer(modelPath, x, y, z, facing, who)
        endfunction
       
        function EnableTrackInstance takes Track instance, boolean flag returns nothing
            set instance.enabled = flag
        endfunction
       
        function IsTrackInstanceEnabled takes Track instance returns boolean
            return instance.enabled
        endfunction
       
        function RegisterAnyClickEvent takes code c returns nothing
            call Track.registerAnyClick(c)
        endfunction
       
        function RegisterAnyHoverEvent takes code c returns nothing
            call Track.registerAnyHover(c)
        endfunction
       
        function RegisterClickEvent takes Track obj, code c returns nothing
            call obj.registerClick(c)
        endfunction
       
        function RegisterHoverEvent takes Track obj, code c returns nothing
            call obj.registerHover(c)
        endfunction
       
        function RegisterInteractEvent takes Track obj, code c returns nothing
            call obj.registerInteract(c)
        endfunction
       
        function GetTriggerTrackInstance takes nothing returns Track
            return Track.instance
        endfunction
       
        function GetTriggerTrackable takes nothing returns trackable
            return Track.object
        endfunction
       
        function GetTriggerTrackablePlayer takes nothing returns player
            return Track.tracker
        endfunction
    endlibrary


    Everything is kind of self-explanatory or explained in the documentation. Here is a fun little demo map, feel free to check it out. It isn't anything special, but the effects look nice for cheap thrills.

    Changelog
    3.1.0.0
    • Updated documentation to note the imprecision in Z-values when using the invisible platform (thanks to Uberplayer).
    • Updated Table instancing, and fixed .destroy() so that it properly flushes all the data created by that instance.
    • Removed event recursion--there is no way for the trigger to re-evaluate in the trigger callbacks (since you can't force a user to click a trackable), so I removed the recursion support.
    • Removed operators for enabled--added a simple "enabled" boolean. This change does not change the API or functionality in any way.
    • Added registerInteract and RegisterInteractEvent, which are more efficient in terms of triggers/triggerconditions compared to registerClick and registerHover. (registerAnyClick and registerAnyHover is fine)
    • Updated demo, updated thread attachment.
    3.0.2.3
    • Fixed a bug with the Z of the trackable (due to a typo in the rawcode of the platform). Thanks to Dalvengyr for the report.
    3.0.2.2
    • Rewrote documentation, minor optimizations.
    3.0.2.1
    • Made an update to the struct API, replacing the
      .enable()
      and
      .disable()
      functions with a enabled= operator. Also updated the documentation.
    3.0.1.0
    • Fixed a bug where I used .model instead of the variable s to create the trackable. That made it so that the local "creation" would not work properly. Thanks to Arhowk for finding this.
    3.0.0.0.
    • Made changes to the API.
    • No longer requires the Event library.
    2.0.0.0
    • Made changes to the API.
    1.0.1.2
    • Minor optimizations.
    1.0.1.1
    • Optimizations made and renamed the system to "Track" instead of "Trackable3", and renamed some functions + one global.
    1.0.1.0
    • Added more to the API and made some reworkings.
    1.0.0.0
    • Initial Release


    Enjoy.
     

    Attached Files:

    Last edited: Feb 16, 2016
  2. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,086
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Purge, don't you think that naming library '(...)#3' is a bit boring?
    I'd suggest just 'Track'. Ofcourse, your name isn't so bad at all, I gave just a suggestion.
     
  3. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Kk, I'll rename it later. I just used Trackable3 because Azlier's was named Trackable2.

    Anyway, I updated to 1.0.1.0; just a simple reordering of the functions. The way it was before made it so that they would add prototype functions and triggers to execute the struct methods from the normal functions. I have now placed the functions beneath the struct so it should be fine now. :)
     
  4. Switch33

    Switch33

    Joined:
    Dec 3, 2006
    Messages:
    334
    Resources:
    0
    Resources:
    0
    Sweet demo. Kinda fun actually. A worthwhile improvement to make sure the system doesn't generate too much all at once.
     
  5. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    niceee
     
  6. BBQ

    BBQ

    Joined:
    Jun 7, 2011
    Messages:
    97
    Resources:
    0
    Resources:
    0
    Code (vJASS):
    set thistype.player = Player(TrackTable[GetHandleId(GetTriggeringTrackable())])
    set thistype.object = GetTriggeringTrackable()

    Swap those two lines:

    Code (vJASS):
    set thistype.object = GetTriggeringTrackable()
    set thistype.player = Player(TrackTable[GetHandleId(thistype.object)])


    Otherwise, this is quite nice (you may want also to think of a better name for the
    static player player
    ... naming stuff like that just doesn't look right to me - not to mention that the highlighting messes it up even more).
     
  7. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Code (vJASS):
                call TrackTable[GetHandleId(.reg)].flush()
                call TrackTable[GetHandleId(.object)].flush()


    That needs to be this:

    Code (vJASS):
                call TrackTable.remove(GetHandleId(.reg))
                call TrackTable.remove(GetHandleId(.object))
     
  8. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,005
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Make the struct extend an array and add these:

    Code (vJASS):
    private static integer ic = 0
    private static integer ir = 0
    private thistype rn


    Then, instead of thistype.allocate:

    Code (vJASS):
    if ir == 0 then
        set ic = ic + 1
        set this = ic
    else
        set this = ir
        set ir = .rn
    endif


    and deallocate like this:

    Code (vJASS):
    set .rn = ir
    set ir = this


    That should do it.
    Things like this should be coded as efficiently as possible.

    edit
    Also, you could've called the system TrackableX ;D
     
  9. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Ty :D

    Fixed.

    Yeah, I agree. I named it "tracker" instead (as I couldn't find a more suitable name), but if you have any other suggestions for names, feel free to suggest away.

    Fixed.

    Fixed. (although, I put it as a module and implemented it because I don't like seeing that stuff in the way of my code)

    As for the name, yeah I thought about making it TrackableX, however it requires you to press shift at the end and the "X" just looks like it is hanging out there. For example, TrackableX.create() looks weirder to me than Track.create(). However, TrackableX is a bit more explicit, so I'm still open to suggestions. But for now, Track works perfectly unless anyone has a better name. :)

    Updated to 1.0.1.1.
    Changelog
    1.0.1.1
    • Optimizations made and renamed the system to "Track" instead of "Trackable3", and renamed some functions + one global.
    1.0.1.0
    • Added more to the API and made some reworkings.
    1.0.0.0
    • Initial Release


    EDIT: Bribe could ya change the title?
     
    Last edited: Oct 22, 2011
  10. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    This seems really helpful, I guess I might try working with trackables in the near future...
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I like the name better as Track.

    I am concerned about this line: "if z > 0 then". Real numbers in wc3 make me nervous, if this was using the "!=" operator it would be less prone to errors. A negative value shouldn't be used anyway.

    Also, there is a lot of code in the create methods that could be delegated to a third, more generic function to avoid creating so many instances of completely copied text.
     
  12. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Updated for that. :D
    Changelog
    1.0.1.2
    • Minor optimizations.
     
  13. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,005
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Code (vJASS):
                local thistype this = 0
                if ir == 0 then
                    set ic = ic + 1
                    set this = ic
                else
                    set this = ir
                    set ir = .rn
                endif


    ->

    Code (vJASS):
                local thistype this = ir
                if ir == 0 then
                    set ic = ic + 1
                    set this = ic
                else
                    set ir = .rn
                endif


    Kudos to you Bribe ;)
     
  14. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    lol done.
     
  15. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    You have this huge identical block in two different functions, what I meant by centralizing was to move them to a different function so they can be called via proxy.

    Code (vJASS):
                        if GetLocalPlayer() == p then
                            set s = modelPath
                        else
                            set s = ""
                        endif
                        set tr = CreateTrackable(modelPath, x, y, facing)
                        call TriggerRegisterTrackableHitEvent(.reg, tr)
                        call TriggerRegisterTrackableTrackEvent(.reg, tr)
                        set TrackTable[GetHandleId(tr)] = i
     
  16. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
  17. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    This should either use the Event library or the boolexpr evaluation. Since Nestharus deleted dynamic event registry Event is useless as a dynamic alternative. Also, since boolexpr can safely "return nothing" now without crashing Mac, re-structure your code.

    Code (vJASS):
            method registerOnClick takes boolexpr cond returns nothing
                if .onClick == null then
                    set .onClick = CreateTrigger()
                endif
                call TriggerAddCondition(.onClick, cond)
            endmethod
           
            method registerOnHover takes boolexpr cond returns nothing
                if .onHover == null then
                    set .onHover = CreateTrigger()
                endif
                call TriggerAddCondition(.onHover, cond)
            endmethod


    Change those to:

    Code (vJASS):
            method registerOnClick takes code cond returns nothing
                if .onClick == null then
                    set .onClick = CreateTrigger()
                endif
                call TriggerAddCondition(.onClick, Filter(cond))
            endmethod
           
            method registerOnHover takes code cond returns nothing
                if .onHover == null then
                    set .onHover = CreateTrigger()
                endif
                call TriggerAddCondition(.onHover, Filter(cond))
            endmethod


    This eliminates a large amount of verbosity on the user's part.
     
  18. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    K, updated to use solely Event. I may change it to use Advent later in case I want to allow someone to destroy their instance. (they really shouldn't need to but I might as well allow support for it)

    Note: API changed slightly with v.2.0.0.0.

    Changelog
    2.0.0.0
    • Made changes to the API.
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    So now we have to use "Track.ANY_CLICK.register(Filter(function blah))" instead of "RegisterTrackClick(function blah)", no thanks to that. Please don't make your users do that, it is caked in ugliness.

    What I was trying to hint at was to use your built-in API which also saves a library requirement and of course much horrible ugly syntax.
     
  20. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    I already have that, right?

    Although, I can remove the library requirement if you want. I'll do that tomorrow.

    EDIT: Although, I agree that Track.ANY_CLICK.register(Filter(function blah)) looks far worse than something along the lines of Track.registerAnyClick(function blah), so I'll update the struct interface.