• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] Track

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:
JASS:
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.

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.
 

Attachments

  • Track.w3x
    36 KB · Views: 330
Last edited:
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. :)
 
Level 7
Joined
Dec 3, 2006
Messages
339
Sweet demo. Kinda fun actually. A worthwhile improvement to make sure the system doesn't generate too much all at once.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
JASS:
set thistype.player = Player(TrackTable[GetHandleId(GetTriggeringTrackable())])
set thistype.object = GetTriggeringTrackable()
Swap those two lines:

JASS:
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).
 
Make the struct extend an array and add these:

JASS:
private static integer ic = 0
private static integer ir = 0
private thistype rn

Then, instead of thistype.allocate:

JASS:
if ir == 0 then
    set ic = ic + 1
    set this = ic
else
    set this = ir
    set ir = .rn
endif

and deallocate like this:

JASS:
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
 
Switch33 said:
Sweet demo. Kinda fun actually. A worthwhile improvement to make sure the system doesn't generate too much all at once.
Adiktuz said:

Ty :D

BBQ said:
naming stuff like that just doesn't look right to me

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.

Bribe said:
That needs to be this:

Fixed.

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

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

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

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
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.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
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.

JASS:
                    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
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
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.

JASS:
        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:

JASS:
        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.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
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.
 
JASS:
/*        function RegisterAnyClickEvent takes code c returns nothing
*            - The code will be executed every time a trackable is clicked.
*        function RegisterAnyHoverEvent takes code c returns nothing
*            - The code will be executed every time a trackable is hovered.
*        function RegisterClickEvent takes Track obj, code c returns nothing
*            - The code will be executed every time a trackable of the instance
*            - "obj" is clicked.
*        function RegisterHoverEvent takes Track obj, code c returns nothing
*            - The code will be executed every time a trackable of the instance
*            - "obj" is hovered. 
*/

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.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I found quite a severe error(not really lol), API is outdated

JASS:
*
*        method registerOnClick takes code c returns nothing
*        method registerOnHover takes code c returns nothing
*



        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

Why Im reporting this: I actually had to go to the struct definition to see the names :D and I tried compiling registerOnClick multiple times, and it always said no such member
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
microoptimization, but still, you set values regardless if it is needed or not

JASS:
        private static method onInteract takes nothing returns boolean
            local thistype  temp = instance
            local trackable tr   = object
            local player    p    = tracker
           
            set instance = TrackTable[GetHandleId(GetTriggeringTrigger())]
            set object   = GetTriggeringTrackable()
            set tracker  = Player(TrackTable[GetHandleId(object)])
           
            if instance.flag then
                if GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK then
                    call TriggerEvaluate(instance.onHover)
                    call TriggerEvaluate(anyHover)
                else
                    call TriggerEvaluate(instance.onClick)
                    call TriggerEvaluate(anyClick)
                endif
            endif
           
            set instance = temp
            set tracker  = p
            set object   = tr
            set tr = null
            set p  = null
            return false
        endmethod

into

JASS:
        private static method onInteract takes nothing returns boolean
            local thistype  temp = instance
            local trackable tr   = object
            local player    p    = tracker
           
            set instance = TrackTable[GetHandleId(GetTriggeringTrigger())]

            if instance.flag then
                set object   = GetTriggeringTrackable()
                set tracker  = Player(TrackTable[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
                set tracker = p
                set object = tr
            endif
            
            set intance = temp

            set tr = null
            set p  = null
            return false
        endmethod

or even

JASS:
        private static method onInteract takes nothing returns boolean
            local thistype  temp = instance
            local trackable tr
            local player    p
           
            set instance = TrackTable[GetHandleId(GetTriggeringTrigger())]
           
            if instance.flag then
                set tr = object
                set p = tracker
                set object   = GetTriggeringTrackable()
                set tracker  = Player(TrackTable[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
                set tracker = p
                set object = tr

                set tr = null
                set p  = null
            endif
            set instance = temp
            return false
        endmethod

small edit
 
Last edited:
Looks good and I spent more than 10 minutes playing that awesomeeee demo map :).
Why don't you null the 3 triggers in method destroy, especially .reg?

Hehe thanks. I always go back to that map every now and then. I wish I made a minigame with it.

About the nulling, perhaps I'll add that later (maybe). All in all, nulling globals is a trivial leak, which is why it isn't really "required" (at least in my book).
This is how I rank memory leaks:
  1. Handles that aren't removed/destroyed - take up a lot of memory.
  2. Reference/handle ID leaks from not nulling locals - this was more problematic before hashtables existed, but they still end up eating some memory and can screw up most handle counters (and can screw over quite a few outdated systems).
  3. String leaks - you can't do anything about them, but they are still something to be conscious of when coding things involving strings (that rhymes).
  4. Nulling globals
There might be more types, I don't remember. But in general, I hardly consider it a leak. I suppose it is easy to fix, but it is a matter of a couple bytes of memory vs. a couple bytes of map size + a couple of microseconds, and we all know we gotta save 'dem microseconds. :grin:
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Thank you, I already considered making a thread to discuss and agree what must, could and must not be nulled. Lately this subject came up quite often.
Personally I always null everything, because I think it is good practise and in so doing I don't miss one of the more important cases by accident.

In general I agree with your ranking and hence won't raise the subject again until new information exists.
and we all know we gotta save 'dem microseconds.
Damn right!!!!!! :)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I was thinking of doing Inventory system with this, but there is no easy way to say which [X, Y] was clicked and I thought of rewriting it for myself, but this could possibly be helpful to others, and a bit of extra funcionality never hurts.

JASS:
function TrackAttachData1 takes Track obj, integer data1 returns nothing
function TrackAttachData2 takes Track obj, integer data2 returns nothing

//and, or

function TrackAttachData takes Track obj, integer data1, integer data2 returns nothing

and coresponding into struct:

JASS:
method attachData1 takes integer data1 returns nothing
method attachData2 takes integer data2 returns nothing

//and, or

method attachData takes integer data1, integer data2 returns nothing

Also, coresponding readers:

JASS:
function TrackGetData1 takes Track obj returns integer
function TrackGetData2 takes Track obj returns integer

and coresponding methods in struct, which could be either operators, member functions or member data:

JASS:
method getData1 takes nothing returns integer
method getData2 takes nothing returns integer

so you can attach 2 integers to it, which would represent X and Y, or possibly other custom data

Name could be possibly changed to things like Data1 -> X, Data2 -> Y, but Data1/2 is more generic, which shows more general purpose than for XY storing.

Possible implementations:


Only showing things to be added, not all the code!

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

struct Track extends array
    private static Table /* Undefined, TrackName in example */= 0
   
    method destroy takes nothing returns nothing
        /* TrackTable.remove-s  */

        call TableName.remove(GetHandleId(.object))
        call TableName.remove(-GetHandleId(.object))

        /* custom deallocation   */
    endmethod

    method attachData1 takes integer data returns nothing
        set TableName[GetHandleId(.object)] = data
    endmethod
    
    method attachData2 takes integer data returns nothing
        set TableName[-GetHandleId(.object)] = data
    endmethod
    
    method attachData takes integer data1, integer data2 returns nothing
        set TableName[GetHandleId(.object)] = data
        set TableName[-GetHandleId(.object)] = data
    endmethod
    
    method getData1 takes nothing returns integer
        return TableName[GetHandleId(.object)] = data
    endmethod
    
    method getData2 takes nothing returns integer
        return TableName[-GetHandleId(.object)] = data
    endmethod
endstruct
    
function TrackAttachData1 takes Track obj, integer data returns nothing
    call obj.attachData1(data)
endfunction

function TrackAttachData2 takes Track obj, integer data returns nothing
    call obj.attachData2(data)
endfunction

function TrackAttachData takes Track obj, integer data1, integer data2 returns nothing
    call obj.attachData(data1, data2)
endfunction

function TrackGetData1 takes Track obj returns integer
    return obj.getData1()
endfunction

function TrackGetData2 takes Track obj returns integer
    return obj.getData2()
endfunction



Only showing things to be added, not all the code!

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

struct Track extends array
    readonly integer /* Undefined */ //data1 in example
    readonly integer /* Undefined */ //data2 in example

    /*     Optional    */
    method destroy takes nothing returns nothing
        /* TrackTable.remove-s  */

        set .data1 = 0
        set .data2 = 0

        /* custom deallocation   */
    endmethod

    method attachData1 takes integer data returns nothing
        set data1 = data
    endmethod
    
    method attachData2 takes integer data returns nothing
        set data2 = data
    endmethod
    
    method attachData takes integer data1, integer data2 returns nothing
        set .data1 = data1
        set .data2 = data2
    endmethod
    
    method getData1 takes nothing returns integer
        return .data1
    endmethod
    
    method getData2 takes nothing returns integer
        return .data2
    endmethod
endstruct
    
function TrackAttachData1 takes Track obj, integer data returns nothing
    call obj.attachData1(data)
endfunction

function TrackAttachData2 takes Track obj, integer data returns nothing
    call obj.attachData2(data)
endfunction

function TrackAttachData takes Track obj, integer data1, integer data2 returns nothing
    call obj.attachData(data1, data2)
endfunction

function TrackGetData1 takes Track obj returns integer
    return obj.getData1()
endfunction

function TrackGetData2 takes Track obj returns integer
    return obj.getData2()
endfunction


names are open to change, because yes data1 and data2 is not really awesome name for method nor for function
 
using this system, is that possible to make a fullscreen inventory where we have to click and drag the item out from the inventory area to drop that item? I mean the item's icon following the cursor, not following actually but immedietly move to cursor's location..

It is definitely possible to make a full-screen system with this (it is actually really useful for that, and it is why I made this system :grin:). The icon dragging part depends on what you mean. Most icons in full-screen systems are destructables, and there aren't natives to move them. You can remove them and create them in a new spot, or you can abuse the war club ability to move them with a unit (iirc. look into DGUI for more info).

But yeah, if you just made a bunch of trackables in the full-screen system, you could have the icon follow the cursor. You might have to add a lot of tiny trackables to make it look good (the bigger the trackables, the more choppy it'll look).
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
For a real drag&drop you need to detect mouse button pressed/release, trackables only give you a clicked event. Im not sure if there is some hacky way (maybe with ForceUIKey and items) to do it, but there is no simple solution.
 
Updated to 3.0.2.3 with a bug-fix. The Z portion of trackables didn't work because I typed 'Otip' instead of 'OTip' for the platform rawcode. Silly me. I believe that bug was introduced in 3.0.2.1 when I rewrote the documentation, so if you are using 3.0.2.1 or 3.0.2.2 then I recommend updating.

Thanks to Dalvengyr for reporting this.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I also noticed that the facing of trackable are not correct even I input the z is 0.. see the pic I posted on your vm? it also occurs when z is 0.. the white track did not face exactly 270 degree

As for the facing, it seems to work for me. I tried it with two peasants with two different angles, and it worked.
uhm.. sorry I will test it again..

EDIT: I forgot to report, everything works fine now :grin:
 
Last edited:
Level 10
Joined
Dec 15, 2012
Messages
650
Help !

Is there a way to know the mouse cursor is inside a region ??

I'm bored with the selection event so I tried to learn this.
I need it !! I want to learn(then master) this system, please teach me :grin:
(silly boy is dreaming again)

Thank you very much
 
Top