1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

SmartTrack v1.05.1

Submitted by Spellbound
This bundle is marked as approved. It works and satisfies the submission rules.


Code (vJASS):

library SmartTrack requires RegisterPlayerUnitEvent optional TimerUtils

/*
    SmartTrack v1.05.1
    By Spellbound
   
   
    ==== Description ====
   
    SmartTrack will track when a unit is ordered to right-click on another unit. For this to
    happen, the 'Tracker' will have to first be identified with CreateSmartOrderTracker, which
    takes the unit itself (usually a building) and the range with which it will detect a unit
    that right-clicked it.
   
    SmartTrack can thus be used in conjunction with other systems like a custom waygate system
    or docking system.
   
   
    ==== Requirements ====
   
    RegisterPlayerUnitEvent (by Bannar)
    https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
   
    Any custom value unit indexer such as:
    Unit Event: https://www.hiveworkshop.com/threads/gui-unit-event-v2-5-0-0.201641/ or
    UnitDex: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
    Optionally uses TimerUtils for the SmartTrack.stop for the interruption boolean.
   
    ==== API ====
   
    function GetSmartUnit() returns the unit that did the right-clicking
   
    function GetTracker() is the building/unit that tracks right-clickers.
   
    function CreateSmartOrderTracker takes unit source, real range returns nothing
        ^ this sets up a unit as a Tracker that will fire an event whenever another unit that
        has right-clicked on it comes into range.
   
    function CancelSmartOrderTracker takes unit source returns nothing
        ^ this undoes the setup. Call this function when the unit dies or is de-indexed.
   
    function RegisterNativeEvent takes whichEvent, function code returns nothing
        ^ this uses RegisterNativeEvent's event-creation to register SmartTrack's many events. They
        are as followed:
        EVENT_SMART_TRACK_IN_RANGE ---- will detect when a unit is in range. Range is determined by
                                        the function CreateSmartOrderTracker()
        EVENT_SMART_TRACK_STARTED ----- will fire when tracking has begun. If a unit is already in
                                        range it will fire first, then EVENT_SMART_TRACK_IN_RANGE will fire.
        EVENT_SMART_TRACK_TERMINATED -- will fire when tracking has ended, either from cancellation
                                        or a SmartUnit has come within range of its Tracker.
                               
    function RegisterIndexNativeEvent takes integer playerNumber, integer whichEvent, function code returns nothing
        ^ Same as above, but per-player. If you wish SmartTrack to only work for specific players,
        use this instead.
       
    SmartTrack.stop( takes unit smarty, boolean interruptOrders )
        ^ call this method when you want to prevent units from carrying out the right-click
        on EVENT_SMART_TRACK_STARTED event. This is useful for when you want to filter out units for
        whatever reasons. For example, if you are using DockingSystem you'll want to prevent
        some units from docking into the building. You can prevent that from happening by
        calling the above method.
       
        smarty is the unit you wish to interrupt
        interruptOrders will tell the system whether you want the unit to stop moving.
   
*/


globals
    constant integer ORDER_SMART = 851971
   
    private unit eventSmarty = null
    private unit eventTracker = null
    private boolean array stopTracking
    public boolean ignoreOrders = false // This boolean is used by other libraries that have SmartTrack as a dependency.
   
    integer EVENT_SMART_TRACK_IN_RANGE
    integer EVENT_SMART_TRACK_STARTED
    integer EVENT_SMART_TRACK_TERMINATED
   
    private hashtable TRIGGER_TRACK = InitHashtable()
    private hashtable TimerHash
    private trigger SmartyTrack = CreateTrigger()
    private integer TrackerCount = 0
    private integer MaxSmarties = 0
    private integer array SmartySlot
    private unit array Smarty
    private unit array Tracker
    private real array TrackerRange
    private trigger array TrackerTrig
    private trigger array EventTrig
    public boolean array IsATracker
endglobals

native UnitAlive takes unit u returns boolean

// Getter function
function GetSmartUnit takes nothing returns unit
    return eventSmarty
endfunction

function GetTracker takes nothing returns unit
    return eventTracker
endfunction

// This is the function that controls what trigger is run.
private function FireEvent takes unit tracker, unit u, integer ev returns nothing
    local integer playerId = GetPlayerId(GetOwningPlayer(u))
    local integer id = GetUnitUserData(u)
    local unit prevSmarty = eventSmarty
    local unit prevTracker = eventTracker
   
    // If SmartTrack.stop is called, stopTracking[id] will be set to true to prevent custom events from firing.
    if not stopTracking[id] then
        set eventSmarty = u
        set eventTracker = tracker
        call TriggerEvaluate(GetNativeEventTrigger(ev))
        if IsNativeEventRegistered(playerId, ev) then
            call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, ev))
        endif
        set eventSmarty = prevSmarty
        set eventTracker = prevTracker
    endif
   
    set prevSmarty = null
    set prevTracker = null
endfunction


// This function handles removing right-clickers from the Smarty[] list. It checks where the
// right-clicker is on the list and then move all elements above it in the list down by 1.
private function RemoveSmarty takes unit u returns nothing
    local integer id = GetUnitUserData(u)
    local integer i = SmartySlot[id]
    local unit tracker = Tracker[id]
   
    if i != 0 then
        set Tracker[id] = null
        set SmartySlot[id] = 0
        loop
            set Smarty[i] = Smarty[i + 1]
            set SmartySlot[GetUnitUserData(Smarty[i])] = i
            set i = i + 1
            exitwhen i > MaxSmarties
        endloop
        set MaxSmarties = MaxSmarties - 1
        call FireEvent(tracker, u, EVENT_SMART_TRACK_TERMINATED)
    endif
   
    set tracker = null
endfunction


// This function fires when a unit comes in range of a tracker. It goes through the Smart[] list
// and if the unit correspond to a Smarty in the list as well as if the tracker is the one the
// unit had right-clicked on, a EVENT_SMART_TRACK_IN_RANGE event will fire.
private function FilterSmarties takes nothing returns boolean
    local unit tracker = LoadUnitHandle(TRIGGER_TRACK, GetHandleId(GetTriggeringTrigger()), 1)
    local unit u = GetTriggerUnit()
    local integer idU = GetUnitUserData(u)
    local integer i = 0
   
    loop
        set i = i + 1
        exitwhen i > MaxSmarties
        if u == Smarty[i] and Tracker[idU] == tracker then
            // Events
            call FireEvent(tracker, u, EVENT_SMART_TRACK_IN_RANGE)
            call RemoveSmarty(u)
        endif
    endloop
   
    set u = null
    set tracker = null
   
    return false
endfunction


// This is the main function that tracks all right-clicks on trackers.
private function TrackOrders takes nothing returns boolean
    local unit smarty = GetTriggerUnit()
    local unit tracker = GetOrderTargetUnit()
    local integer idSmarty = GetUnitUserData(smarty)
    local integer idTracker = GetUnitUserData(tracker)
   
    if not ignoreOrders then
        // if tracker is not null that means you have right-clicked on a unit.
        if tracker != null and GetIssuedOrderId() == ORDER_SMART then
            if IsATracker[idTracker] then
                if Tracker[idSmarty] == null then // If the right-clicker has no tracker identified...
                    if IsUnitInRange(smarty, tracker, TrackerRange[idTracker]) then
                        // Events
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_IN_RANGE)
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_TERMINATED)
                    else
                        // If not in range, add right-clicker to the Smarty[] list.
                        set MaxSmarties = MaxSmarties + 1
                        set Smarty[MaxSmarties] = smarty
                        set SmartySlot[idSmarty] = MaxSmarties
                        set Tracker[idSmarty] = tracker
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
                    endif
                else
                    // If the right-clicker was already being tracked by another tracker buy the
                    // right-clicked on another tracker, check if that new tracker is in range.
                    if IsUnitInRange(smarty, tracker, TrackerRange[idTracker]) then
                        call RemoveSmarty(smarty)
                        // Events
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_IN_RANGE)
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_TERMINATED)
                    else
                        set Tracker[idSmarty] = tracker
                        call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
                    endif
                endif
            endif
        else
            // if you had a tracker but did not right-click on a unit, remove from Smarty[] list.
            if Tracker[idSmarty] != null then
                call RemoveSmarty(smarty)
            endif
        endif
    endif
   
    set smarty = null
    set tracker = null
    set stopTracking[idSmarty] = false
   
    return false
endfunction


// This function removes tracking from a unit so that right-clicking on it will not trigger any
// custom events from this library.
function CancelSmartOrderTracker takes unit source returns nothing
    local integer id = GetUnitUserData(source)
    local integer i
   
    call FlushChildHashtable(TRIGGER_TRACK, GetHandleId(TrackerTrig[id]))
    call DestroyTrigger(TrackerTrig[id])
   
    set TrackerTrig[id] = null
    set IsATracker[id] = false
    set TrackerCount = TrackerCount - 1
   
    if TrackerCount < 1 then
        call DisableTrigger(SmartyTrack)
        set i = 0
        loop
            set i = i + 1
            exitwhen i > MaxSmarties
            set Tracker[GetUnitUserData(Smarty[i])] = null
            set Smarty[i] = null
        endloop
        set MaxSmarties = 0
    endif
endfunction


// This function will identify a unit as a tracker as well as at what range will the custom
// EVENT_SMART_TRACK_IN_RANGE event will trigger.
function CreateSmartOrderTracker takes unit source, real range returns nothing
    local integer id = GetUnitUserData(source)
   
    if TrackerTrig[id] == null then
        set TrackerTrig[id] = CreateTrigger()
    else
        // If TrackerTrig[id] already exists, it is destroyed and re-created rather than ignored
        // to overwrite the old tracking. This is useful in the event that you wish to modify
        // The range at which a unit tracks.
        call DestroyTrigger(TrackerTrig[id])
        set TrackerTrig[id] = CreateTrigger()
    endif
   
    call TriggerRegisterUnitInRange(TrackerTrig[id], source, range, null)
    call TriggerAddCondition(TrackerTrig[id], function FilterSmarties)
    call SaveUnitHandle(TRIGGER_TRACK, GetHandleId(TrackerTrig[id]), 1, source)
    set TrackerCount = TrackerCount + 1
    set TrackerRange[id] = range //necessary for units that right-click inside the tracker's range.
    set IsATracker[id] = true
   
    if not IsTriggerEnabled(SmartyTrack) then
        call EnableTrigger(SmartyTrack)
    endif
endfunction


// This struct exists solely for prevent a tracking from occuring. It comes with the option to
// force a unit to stop. It orders the unit in question to stop after a zero-second timer, a
// necessary workaround because issuing a stop or stunned command in a function triggered by
// an order event will not work.
struct SmartTrack
   
    unit u
   
    private static method zeroTimerStun takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype this
       
        static if LIBRARY_TimerUtils then
            set this = GetTimerData(t)
            call IssueImmediateOrderById(this.u, 851973) // order stunned
            set this.u = null
            call ReleaseTimer(t)
            call this.deallocate()
        else
            call IssueImmediateOrderById(LoadUnitHandle(TimerHash, GetHandleId(t), 1), 851973) // order stunned
            call FlushChildHashtable(TimerHash, GetHandleId(t))
            call DestroyTimer(t)
        endif
   
        set t = null
    endmethod
   
    static method stop takes unit smarty, boolean interruptOrders returns nothing
        local thistype this
        local timer t
       
        call RemoveSmarty(smarty)
        set stopTracking[GetUnitUserData(smarty)] = true
       
        if interruptOrders then
            static if LIBRARY_TimerUtils then
                set this = allocate()
                set this.u = smarty
                call TimerStart(NewTimerEx(this), 0., false, function thistype.zeroTimerStun)
            else
                set t = CreateTimer()
                call SaveUnitHandle(TimerHash, GetHandleId(t), 1, smarty)
                call TimerStart(t, 0., false, function thistype.zeroTimerStun)
                set t = null
            endif
        endif
    endmethod
   
endstruct


// This module sets up the various events the library uses. Uses RegisterEvent
// that fire when a unit comes within range of a tracker, or tracking has started or ended.
private module Init
    private static method onInit takes nothing returns nothing
   
        static if not LIBRARY_TimerUtils then
            set TimerHash = InitHashtable()
        endif
       
        set EVENT_SMART_TRACK_IN_RANGE = CreateNativeEvent()
        set EVENT_SMART_TRACK_STARTED = CreateNativeEvent()
        set EVENT_SMART_TRACK_TERMINATED = CreateNativeEvent()
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function TrackOrders)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function TrackOrders)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function TrackOrders)
       
    endmethod
endmodule

private struct init
    implement Init
endstruct

endlibrary
 


Demo
Code (vJASS):

scope onSmartTrack initializer init

    /*
        GetSmartUnit() == Unit that enters within range of the tracker
        GetTracker() == The tracker
    */

 
    private function Actions_inRange takes nothing returns boolean
     
        local unit smarty = GetSmartUnit()
        local unit tracker = GetTracker()
     
        local real life = GetUnitState(smarty, UNIT_STATE_LIFE)
     
        //Hunter's Hall
        if GetUnitTypeId(tracker) == 'edob' then
            if GetUnitTypeId(smarty) == 'earc' then //Archers
                //Do a passive transformation
                call UnitAddAbility(smarty, 'pt00')
                call UnitRemoveAbility(smarty, 'pt00')
            elseif GetUnitTypeId(smarty) == 'esen' then //Huntresses
                //Do a passive transformation
                call UnitAddAbility(smarty, 'pt01')
                call UnitRemoveAbility(smarty, 'pt01')
            endif
     
        //Arcane Tower
        elseif GetUnitTypeId(tracker) == 'hatw' then
            if life < GetUnitState(smarty, UNIT_STATE_MAX_LIFE) then
                call SetUnitState(smarty, UNIT_STATE_LIFE, GetUnitState(smarty, UNIT_STATE_LIFE) + 50.)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl", smarty, "chest"))
            else
                call BJDebugMsg("This unit is already at full health")
            endif
         
        //Orc Burrow
        elseif GetUnitTypeId(tracker) == 'otrb' then
            call SetUnitState(smarty, UNIT_STATE_LIFE, life - 50.)
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", smarty, "chest"))
         
        endif
     
        set smarty = null
        set tracker = null
     
        return false
    endfunction
 
    private function Actions_started takes nothing returns boolean
     
        local unit smarty = GetSmartUnit()
        local unit tracker = GetTracker()
     
        //Hunter's Hall - filter example
        if GetUnitTypeId(tracker) == 'edob' then
            if not (GetUnitTypeId(smarty) == 'earc' or GetUnitTypeId(smarty) == 'esen') then //Archers or Huntresses
                call SmartTrack.stop(smarty, true)
                call BJDebugMsg("Only Archers or Huntresses allowed")
            endif
        endif
     
        set smarty = null
        set tracker = null
     
        return false
    endfunction
 
    private function Actions_terminated takes nothing returns boolean
        call BJDebugMsg("Tracking terminated")
        return false
    endfunction
 
    private function init takes nothing returns nothing
        call RegisterNativeEvent(EVENT_SMART_TRACK_IN_RANGE, function Actions_inRange)
        call RegisterNativeEvent(EVENT_SMART_TRACK_STARTED, function Actions_started)
        call RegisterNativeEvent(EVENT_SMART_TRACK_TERMINATED, function Actions_terminated)
    endfunction

endscope
 


Version History
- v1.05.1 fixed an initialization issue.
- v1.05 updated to now use RegisterPlayerUnitEvent/RegisterNativeEvent as dependencies because Bannar happened and is the only one updating his resources. Added demo.
- v1.04 code is now recursion-safe.
- v1.03 Events now use GetSmartUnit() and GetTracker() to return the unit that did the right-clicking and the right-click-tracker respectively. ST_EVENT_UNIT and ST_EVENT_TRACKER have been made private. Also updated Unit Event to 2.5.0.0 for the demo.
- v1.02.1 fixed an issue with TrackOrders not returning a boolean, which was causing a compile error for some.
- v1.02 cleaned a local variable that was leaking as well as a global trigger variable that wasn't being nulled. Added comments to explain the code as well as expanded the demo map to include more significant demonstration of SmartTrack's capabilities.
- v1.01 cleaned up two local variables that were leaking and added a method for interrupting tracking, designed specifically for creating filters for dependent libraries.
- v1.00 released
Contents

SmartTrack v1.05.1 (Map)

  1. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Any recommendations on how to improve this is welcome.
     
  2. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Two locals are leaking in the TrackOrders function. I'll be fixing this tomorrow along with an update to make filtering easier.

    EDIT: Updated to v1.01. See version history.
     
    Last edited: Oct 21, 2017
  3. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,351
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    Required for Approval:
    • Fix all leaks. The function FilterSmarties can potentially cause a unit handle leak due to the local declared local handle variable reference counter leak on return bug.

      Additionally any handle type array variable that is used to map data to units by a unit indexer must also be nulled after the mapping is no longer required. This is to allow timely recycling of the handle indices used as they will not be recycled as long as at least 1 reference to them still exists. An example of this is TrackerTrig, which trigger can be destroyed but the index might never be reused so the trigger handle might never be recycled.
    Feedback and Suggestions:
    • It appears that the private trigger array SmartyTrig is never used. Probably was left in by mistake.
    • Code could do with more descriptive comments. Does not have to be over done but explaining what a private function does or the purpose of a block of code can be useful for future maintenance and for others to learn from.
    • Maybe expand the test map to show off what the system can do. Printing messages on the screen might seem like not much to some people.
     
  4. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Counter leak? I'm not familiar with that - the only bug I know on return is when you return a local unit (for example), which takes away the change to null it. I'll be nulling tracker for the next version. Thanks for spotting that.

    I don't think I using any handle variable besides TrackerTrig, which I will be nulling for the next version.

    Thanks for the review. I'll expand the test map and comment some of my functions for next version.
     
  5. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,351
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    The bug is the entire reason why you have to null local units.

    Handle IDs (you can see these with GetHandleId()) are a finite resource. The Handle IDs are recycled ever few game frames if the object assigned to them has been destroyed and only if there are no existing JASS references to them. This behaviour is needed for safety reasons as otherwise a reference to a destroyed object might change into a reference to a living object of the wrong type.

    References to a handle ID are tracked using a reference counter. Every time a global or local handle variable is assigned to a handle ID it increments that handle ID's reference counter by 1. Every time such variables are assigned to a different handle ID it decrements the original handle ID's reference counter by 1. This behaviour only applies to some handle types, mostly those that represent objects with a finite life cycle.

    The bug is that assigning a local declared local handle variable a handle ID will not automatically decrement the handle ID's reference counter by 1 on function return. This causes a reference counter leak and hence prevents the handle ID from ever being recycled. The solution is to explicitly decrement the handle ID's reference counter by 1 before function return by assigning the variable a null value.
     
  6. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Okay, updated per your requirements and suggestions. I do hope I got rid of all the leaks.

    - v1.02 cleaned a local variable that was leaking as well as a global trigger variable that wasn't being nulled. Added comments to explain the code as well as expanded the demo map to include more significant demonstration of SmartTrack's capabilities.
     
  7. Mirage1

    Mirage1

    Joined:
    Oct 31, 2015
    Messages:
    61
    Resources:
    0
    Resources:
    0
    Hey guys could you give me some help pls? I'm getting this error message from JassHelper when trying to save maps with SmartTrack (even the demo map):

    Jass Helper - "Line 301: Functions passed to Filter or Conditions must return a boolean."
    in this line
    Code (Text):
    call TriggerAddCondition(SmartTrack___SmartyTrack, Condition(function SmartTrack___TrackOrders))
    I'm going to attach a picture of the error in the bottom in any case.

    I'm using Warcraft patch 1.26.0.64. Do I need to use a newer version to be able to save SmartTrack's maps?
     

    Attached Files:

  8. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,351
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    Versions below 1.28.5 are not supported, sorry.
     
  9. Mirage1

    Mirage1

    Joined:
    Oct 31, 2015
    Messages:
    61
    Resources:
    0
    Resources:
    0
    Understood. I've downloaded the newer version and Wex but it still doesn't work to me. I will try to figure out what is causing the problem. Thanks for replying.
     
  10. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Actually, it occurs to me that TrackOrders is not returning a boolean, which it should, but WEX allowed it anyway...

    @Dr Super Good I do have a question concerning that. My first priority is usually performance and I've been told that TriggerAddCondition is faster than TriggerAddAction. But then, there's this:
    call TriggerAddCondition(SmartyTrack, Condition(function TrackOrders))
    . Is that faster than
    call TriggerAddCondition(SmartyTrack, function TrackOrders)
    ?

    Also I guess I should update TrackOrders to return a boolean.
     
  11. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,351
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    And it seemed to run. Does Warcraft III require that it return a boolean anymore?
    The one on the right is not valid JASS. Hence one can only use the one on the left.

    function TrackOrders -> code statement
    Condition(function TrackOrders)) -> filter/boolexpr statement
     
  12. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Well, updated the code in v1.02.1. TrackOrders now returns a boolean along with modifying the related TriggerAddCondition with Condition(function TrackOrders).
     
  13. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Updated to v1.03:
     
  14. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6

    Nitpicks:


    • Describing what the private functions do would help, even if only a bit.

    Notes:


    • Just in case, in FireEvent, the variables could be made recursion-safe by hashing them into locals that store their previous values.

    Code (vJASS):

    local unit prevSmart = ST_UNIT
    local unit prevTracker = ST_UNIT_TRACKER

    ST_UNIT = newVal
    ST_UNIT_TRACKER = newVal2

    // fire...

    ST_UNIT = prevSmart
    ST_UNIT_TRACKER = prevTracker

    // nullify...
     


    Resolved.

    Status:



    Approved
     
    Last edited: May 27, 2018
  15. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    I did describe the private functions though?

    As for FireEvent, is this good? Idk what recursion-safe means :p
    Code (vJASS):

        // This is the function that controls what trigger is run.
        private function FireEvent takes unit tracker, unit u, integer ev returns nothing
            local integer id = GetUnitUserData(u)
            local unit prevSmarty = ST_EVENT_UNIT
            local unit prevTracker = ST_EVENT_TRACKER
            // If SmartTrack.stop is called, stopTracking[id] will be set to true to prevent custom events from firing.
            if not stopTracking[id] then
                set ST_EVENT_UNIT = u
                set ST_EVENT_TRACKER = tracker
                call TriggerEvaluate(EventTrig[ev])
                set ST_EVENT_UNIT = prevSmarty
                set ST_EVENT_TRACKER = prevTracker
            endif
            set prevSmarty = null
            set prevTracker = null
        endfunction
     
     
  16. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    My reason for that is an impractical one, but one that wouldn't hurt to prevent.

    Consider an event that fires, and an action that somehow fires that event. If the former goes, we would lose our reference to the previous event.

    For example:

    Event unit: Death Knight
    Death Knight somehow fires the event again.
    Event unit 2: Death Knight
    Event unit 2: null -> but Event unit is scalar.
    Event unit: null before nulling :(

    EDIT:

    You did? I must have missed that.
     
  17. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Does the example code I gave fix the recursion problem?
     
  18. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Yep. It's up to you whether or not to optimize it.
     
  19. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,916
    Resources:
    15
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    15
    Coolio. Updated to v1.04 and is now recursion safe. I think :D