• 🏆 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!

GUITrack v1.3

Haven't really uploaded stuff in ages but here goes an attempt :p

Basically a GUI API for trackables, supports player only trackables and as many trackable triggers per trackable as you wish (well within hashtable and integer limtis anyway).
Trackables created without specifying a player default to player 12 but are interactable by everyone.

Dunno if anyone finds this usefull but i couldn't find a general purpose trackable system for GUI users in the spell section so here is one.

vJass users should use Track:
http://www.hiveworkshop.com/forums/jass-resources-412/system-track-205760/index8.html

The test map contains leaks in the example triggers, but they aren't the system itself so if it does become an issue I will fix it.

Pros:
* GUI friendly
* Trackables in GUI
* As much trigger on click / hover as you want
* Player only trackables supported
* Works for multiplayer

Cons:
* Slower than vJass version (obviously :p)

JASS:
/**************************************************************************************************************
***
***     How to use this?
***     1. Enable Generate Unknown variables in the File->Preferences->General
***     2. Copy the GTrack VarInit trigger to your map (this should generate all variables)
***     3. Copy the GTrack API trigger to your map
***     4. Use the system as shown in examples
**************************************************************************************************************
***
***     Troubleshooting
***
***     1. I get errors like "unknown variable udg_XYZ"?
***     All the needed variables haven't been generated, make sure to follow the instructions
***
***     2. I get errors like "unknown function xyz?"?
***     This usually happens if the library is placed in the wrong order in the trigger treeview. Please perform the following steps:
***
***     Deactivate all triggers that use that function and save the map
***     Make sure that the GTrack API trigger is above all triggers that use the system in your map
***     Now save your map again, exit the World Editor and start it again
***     Re-activate your triggers, they should work now
***
**************************************************************************************************************/

JASS:
function GetTrackableX takes integer trackId returns real
    return LoadReal( udg_GTrack_Table, trackId, 0)
endfunction

function GetTrackableY takes integer trackId returns real
    return LoadReal( udg_GTrack_Table, trackId, 1)
endfunction

function GetTrackableFacing takes integer trackId returns real
    return LoadReal( udg_GTrack_Table, trackId, 2)
endfunction

function GetTrackableModel takes integer trackId returns string
    return LoadStr( udg_GTrack_Table, trackId, 3)
endfunction

function GetTrackablePlayer takes integer trackId returns player
    if (HaveSavedHandle( udg_GTrack_Table, trackId, 6) ) then
        return LoadPlayerHandle( udg_GTrack_Table, trackId, 6)
    endif
    return Player(15)
endfunction

function AddTrackableHitTrigger takes integer trackId, trigger t returns nothing
    local integer i = LoadInteger( udg_GTrack_Table, trackId, 4)
    call SaveTriggerHandle( udg_GTrack_Table, trackId, 100+i, t)
    call SaveInteger( udg_GTrack_Table, trackId, 4, i+1 )
endfunction

function AddTrackableTrackTrigger takes integer trackId, trigger t returns nothing
    local integer i = LoadInteger( udg_GTrack_Table, trackId, 5)
    call SaveTriggerHandle( udg_GTrack_Table, trackId, -1-i, t)
    call SaveInteger( udg_GTrack_Table, trackId, 5, i+1 )
endfunction

function FlushTrackableTriggers takes integer trackId returns nothing
    local real x = LoadReal( udg_GTrack_Table, trackId, 0)
    local real y = LoadReal( udg_GTrack_Table, trackId, 1)
    local real facing = LoadReal( udg_GTrack_Table, trackId, 2)
    local string model = LoadStr(udg_GTrack_Table, trackId, 3)
    local player p = LoadPlayerHandle( udg_GTrack_Table, trackId, 6)
   
    call FlushChildHashtable( udg_GTrack_Table, trackId)
   
    call SaveReal( udg_GTrack_Table, trackId, 0, x)
    call SaveReal( udg_GTrack_Table, trackId, 1, y)
    call SaveReal( udg_GTrack_Table, trackId, 2, facing)
    call SaveStr( udg_GTrack_Table, trackId, 3, model)
   
    call SaveInteger( udg_GTrack_Table, trackId, 4, 0)
    call SaveInteger( udg_GTrack_Table, trackId, 5, 0)
   
    call SavePlayerHandle( udg_GTrack_Table, trackId, 6, p)
endfunction

function TrackableCreateEx takes player p returns integer
    local string model = ""
    local trackable t
    local integer pkey
    if(GetLocalPlayer() == p ) then
        set model = udg_GTrack_Model
    endif
    set t = CreateTrackable( model, udg_GTrack_X, udg_GTrack_Y, udg_GTrack_Facing * bj_DEGTORAD)
    set pkey = GetHandleId(t)
    call TriggerRegisterTrackableHitEvent( udg_GTrack_HitEvent, t)
    call TriggerRegisterTrackableTrackEvent( udg_GTrack_TrackEvent, t)
   
    call SaveReal( udg_GTrack_Table, pkey, 0, udg_GTrack_X)
    call SaveReal( udg_GTrack_Table, pkey, 1, udg_GTrack_Y)
    call SaveReal( udg_GTrack_Table, pkey, 2, udg_GTrack_Facing)
    call SaveStr( udg_GTrack_Table, pkey, 3, udg_GTrack_Model)
   
    call SaveInteger( udg_GTrack_Table, pkey, 4, 0)
    call SaveInteger( udg_GTrack_Table, pkey, 5, 0)
   
    call SavePlayerHandle( udg_GTrack_Table, pkey, 6, p)
    set t = null
    return pkey
endfunction

function TrackableCreate takes nothing returns integer
    local trackable t = CreateTrackable( udg_GTrack_Model, udg_GTrack_X, udg_GTrack_Y, udg_GTrack_Facing * bj_DEGTORAD )
    local integer pkey = GetHandleId(t)
    call TriggerRegisterTrackableHitEvent( udg_GTrack_HitEvent, t)
    call TriggerRegisterTrackableTrackEvent( udg_GTrack_TrackEvent, t)
   
    call SaveReal( udg_GTrack_Table, pkey, 0, udg_GTrack_X)
    call SaveReal( udg_GTrack_Table, pkey, 1, udg_GTrack_Y)
    call SaveReal( udg_GTrack_Table, pkey, 2, udg_GTrack_Facing)
    call SaveStr( udg_GTrack_Table, pkey, 3, udg_GTrack_Model)
   
    call SaveInteger( udg_GTrack_Table, pkey, 4, 0)
    call SaveInteger( udg_GTrack_Table, pkey, 5, 0)
    set t = null
    return pkey
endfunction

function TrackableHitEvent takes nothing returns boolean
    local trackable t = GetTriggeringTrackable()
    local integer i = 0
    local integer count
    local trigger trig
    local integer pkey = GetHandleId(t)
    if (HaveSavedInteger( udg_GTrack_Table, pkey, 4 ) ) then
        set count = LoadInteger( udg_GTrack_Table, pkey, 4)
        loop
            exitwhen i >= count
                set trig = LoadTriggerHandle( udg_GTrack_Table, pkey, 100+i)
                set udg_GTrack_HandleId = pkey
                if (TriggerEvaluate(trig)) then
                    call TriggerExecute(trig)
                endif
            set i = i +1
        endloop
    endif
    set t = null
    return false
endfunction

function TrackableTrackEvent takes nothing returns boolean
    local trackable t = GetTriggeringTrackable()
    local integer i = 0
    local integer count
    local trigger trig
    local integer pkey = GetHandleId(t)
    if (HaveSavedInteger( udg_GTrack_Table, pkey, 5 ) ) then
        set count = LoadInteger( udg_GTrack_Table, pkey, 5)
        loop
            exitwhen i >= count
                set trig = LoadTriggerHandle( udg_GTrack_Table, pkey, -1-i)
                set udg_GTrack_HandleId = pkey
                if (TriggerEvaluate(trig)) then
                    call TriggerExecute(trig)
                endif
            set i = i +1
        endloop
    endif
    set t = null
    return false
endfunction

function InitTrig_GTrack_API takes nothing returns nothing
    set udg_GTrack_Table = InitHashtable()
   
    set udg_GTrack_HitEvent = CreateTrigger()
    set udg_GTrack_TrackEvent = CreateTrigger()
   
    call TriggerAddCondition( udg_GTrack_HitEvent, function TrackableHitEvent )
    call TriggerAddCondition( udg_GTrack_TrackEvent, function TrackableTrackEvent )
endfunction


Instructions:
Copy the GTrack VarInit trigger to initialize all variables needed by system (make sure you have this option turned on)
Copy the GTrack API trigger for the system after the variables are initialized.


v1.0 - initial release
v1.1 - small performance boost, added readme, category now only JASS no longer gui
v1.2 - now uses only 1 hashtable, limit of 10000 hit triggers per trackable added as result
v1.3 - updated hashtable keys


Keywords:
track, trackable, gui, system, model, interface, ui
Contents

GUITrack v1.3 (Map)

Reviews
11:14, 19th Feb 2016 BPower: Useful snippet for properly organizing trackables. Does what it's intended to do. Approved. 03:24, 10th Feb 2016 IcemanBo: Have read your post - will wait for update.

Moderator

M

Moderator

11:14, 19th Feb 2016
BPower: Useful snippet for properly organizing trackables.
Does what it's intended to do. Approved.

03:24, 10th Feb 2016
IcemanBo: Have read your post - will wait for update.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Forgot teh code in the description, updating...

Edit:

Interested in opinion of mods if this is usable in this state or if it needs further expanding or whether it is usable at all.

Edit2:

I realized I could use TriggerAddCondition instead of TriggerAddAction for the global trackable events for more performance, will do so in next update I guess.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Level 25
Joined
Jun 5, 2008
Messages
2,572
I'm quite keen on the one Flux recently made: http://www.hiveworkshop.com/forums/spells-569/gui-friendly-trackables-system-v1-01-a-273684/

His allows users to specify a region to fill and the spacing between trackables. I prefer that to having to use custom script. This could be a vanilla JASS substitute for Track, but it is not GUI due to its reliance on custom script.

Yes i was aware, but it requires a region which I do not really like.
It is used for mass producing of trackables in a region with the same model, this one is on a lower level, meant for precision creation of trackables.
Also this one allows for a variable amount of triggers to attach to hit / hover events compared to only one.

The GUI part was more of it that it is GUI friendly since it allows the user to use shadowed globals in triggers.
As such the only custom script parts are minimal as they are used only to initialize locals with same name as the global vars in the hit / track triggers.

Will remove the resource if you think it is redundant.

Chaosy said:
I did not read through the code but quite frankly I don't know why you would need three hashtables.

One for trackable data, two for hit and hover triggers, thus allowing a variable amount of triggers for execution.
You just set a local variable which shadows a global to your desired trackable id and do the same with a trigger variable and call a custom script to attach a trigger to be executed on the desired event.

As such multiple trackables can share same trigger executions, and you can do rebinding of trackable triggers because you can flush the child hashtables for the hit / track events.

I might add specific removal of a trigger but it would have a O(n) complexity since i do not wish for yet another hashtable usage, but I suppose people wouldn't use a ton of trigger executions per trackable.
 
Last edited:
I'm not sure why we need those locals/shadowing.
Couldn't be just directly the globals be set?

Also I'm not very sure of how multiple trigger registration may be relevant for user.
I see the demo, but it seems like all actions could be merged just into one trigger.
Could you, or someone else, describe an example of useful usage of it?
It's like having multiple triggers with same event in one system. (which should be avoided in general)
However, if is reasonable, it is a good feature of course.

A trigger needs to run instead of calling functions.
That's because to call a function it must be declared above the caller function.
And it can't be ensured at this moment.

Flux's one allows to use rects for register trackables, which is obiously
it's strength, especialy for GUI user, but is in same a potential critique point.
Your's need's a bit more user-work, but standalone trackables can be placed anywhere where user want.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
If i use the variable shadowing then the user can potentially use waits in the trigger actions, thus the user doesn't have to worry about it and is less prone to errors and race condition applying.

Multiple triggers are useful if you need your trackable actions to behave mostly the same but deviate in certain aspects.
Then you could attach a generalized trigger handler to all of them, and then attach the extra trigger handlers for specific trackables without code multiplication.

It also allows for dynamic code interactions without complicated if/then/else branching, you just attach more and more handlers to certain trackables as the game state evolves (hero picks up item, levels up ability, unit dies, timer expires etc).

Not sure what this part of your response regards to:
A trigger needs to run instead of calling functions.
That's because to call a function it must be declared above the caller function.
And it can't be ensured at this moment.

Do you suggest I move the custom scripts calling functions to running triggers? That is the only thing that comes to mind with this sentence.

Also, pretty sure all trackable related stuff is mostly not of generic type. You want the system to be as flexible as possible for customization, which is the point of this submission.
It doesn't force anything on the end user (object editor data / usage of regions/rects) and as such is the easiest to customize, as much as a vanilla JASS system which is GUI friendly allows that is.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
The functions are not in a library and so they can't be called from everywhere.
The fact that the demo code is running was "luck", because the system trigger
leaf was created first: http://www.hiveworkshop.com/forums/lab-715/trigger-evaluation-order-251908/

._.

I was hoping it would do a double pass during compiling but I guess you can't expect that from blizz.

Guess I will add another var (operation code) and just run the trigger and call a trigger function based on that var, would that be an appropriate solution? Or maybe make the trigger manually set global vars of triggers to local created triggers, that would be a more elegant solution i guess? Then just set their conditions to call the functions.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Triggers for GUI usage should be fired like:
JASS:
if TriggerEvaluate(trig) then
    call TriggerExecute(trig)
endif

trigger trig could be nulled after the loop.

This is the best trackable wrapper we have ( link ). Everything is stored in one Table.
I guess you can change your code to fit into at least one hashtable.
After all vJass is also JASS code.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Triggers for GUI usage should be fired like:
JASS:
if TriggerEvaluate(trig) then
    call TriggerExecute(trig)
endif

trigger trig could be nulled after the loop.

This is the best trackable wrapper we have ( link ). Everything is stored in one Table.
I guess you can change your code to fit into at least one hashtable.
After all vJass is also JASS code.

Noted, I will try to reduce the number of hashtables to 1 if possible.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Update 1.2:

- Reduced number of hashtables to 1
- Limit of 10000 hit triggers per trackable imposed as a result (offsets in hashtable)
- Added TriggerEvaluate before TriggerExecute as BPower suggested

Hashtable offsets per trackable:
- Offsets 0 - 6 reserved by system
- Offsets 7 - 99 in table reserved for user specific custom data for the trackable
- Offsets 100 - 10099 reserved by trackable hit triggers
- Offsets 10100 and higher reserved by trackable track triggers
 
Top