• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[System] UnitDex - Unit Indexer

System Code
JASS:
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
*   v1.2.2, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   UnitDex assigns every unit an unique integer. It attempts to make that number within the
*   maximum array limit so you can associate it with one.
*   _________________________________________________________________________
*   1. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
    //! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/*  ________________________________________________________________________
*   2. Configuration
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
    private module UnitDexConfig
  
        // The raw code of the leave detection ability.
        static constant integer DETECT_LEAVE_ABILITY = 'uDex'
      
        // Allow debug messages (debug mode must also be on)
        static constant boolean ALLOW_DEBUGGING      = true
      
        // Uncomment the lines below to define a global filter for units being indexed
      
        /*static method onFilter takes unit u returns boolean
            return true
        endmethod*/
      
    endmodule
/*  _________________________________________________________________________
*   3. Function API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Every function inlines.
*
*       function GetUnitId takes unit whichUnit returns integer
*       function GetUnitById takes integer index returns unit
*  
*       function UnitDexEnable takes boolean flag returns nothing
*       function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
*       function IsUnitIndexed takes unit u returns boolean
*       function IsIndexingEnabled takes nothing returns boolean
*
*       function GetIndexedUnit takes nothing returns unit
*       function GetIndexedUnitId takes nothing returns integer
*      
*       function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
*       function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
*       function OnUnitIndex takes code func returns triggercondition
*       function OnUnitDeidex takes code func returns triggercondition
*   _________________________________________________________________________
*   4. Struct API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       UnitDex.Enabled = false // toggle the indexer
*       UnitDex.Initialized     // returns true if the preload timer has finished
*       UnitDex.Count           // returns the amount of units indexed
*       UnitDex.Unit[0]         // access the UnitDex array directly
*       UnitDex.Group           // a unit group containing every indexed unit (for enumeration)
*       UnitDex.LastIndex       // returns the last indexed unit's id
*   _________________________________________________________________________
*   5. Public Variables
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       These are to be used with the "eventtype" argument of the event API:
*
*           constant integer EVENT_UNIT_INDEX     = 0
*           constant integer EVENT_UNIT_DEINDEX   = 1
*   _________________________________________________________________________
*   6. Examples
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Associate a unit with a variable
*
*           set UnitFlag[GetUnitId(yourUnit)] = true
*
*       2. Allocate a struct instance using a units assigned ID
*
*           local somestruct data = GetUnitId(yourUnit)
*  
*       3. Detect when a unit leaves the map
*
*           function Exit takes nothing returns nothing
*               call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
*           endfunction
*
*           call OnUnitDeindex(function Exit)
*           // or
*           call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
*   _________________________________________________________________________
*   7. How it works
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       1. Enumerate all preplaced units
*       2. TriggerRegisterEnterRegion native to detect when a unit enters the map
*       3. Assign each unit that enters the map a unique integer
*       4. Give every unit an ability based off of defend. There is no native to accurately
*          detect when a unit leaves the map but when a unit dies or is removed from the game
*          they are issued the "undefend" order
*       5. Catch the "undefend" order and remove unit from the queue
*   _________________________________________________________________________
*   8. Notes
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
*       - The object merger line should be commented out after saving and restarting.
*
*   -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
  
    globals
        // Event types
        constant integer EVENT_UNIT_INDEX     = 0
        constant integer EVENT_UNIT_DEINDEX   = 1
      
        // System variables
        private trigger array IndexTrig
        private integer Index = 0
        private real E=-1
        private boolexpr FilterEnter
    endglobals
  
    function GetUnitId takes unit whichUnit returns integer
        return GetUnitUserData(whichUnit)
    endfunction
  
    function GetUnitById takes integer index returns unit
        return UnitDex.Unit[index]
    endfunction
  
    function GetIndexedUnit takes nothing returns unit
        return UnitDex.Unit[UnitDex.LastIndex]
    endfunction
  
    function GetIndexedUnitId takes nothing returns integer
        return UnitDex.LastIndex
    endfunction
  
    function IsUnitIndexed takes unit u returns boolean
        return (GetUnitById(GetUnitId(u)) != null)
    endfunction
  
    function UnitDexEnable takes boolean flag returns nothing
        set UnitDex.Enabled = flag
    endfunction
  
    function IsIndexingEnabled takes nothing returns boolean
        return UnitDex.Enabled
    endfunction
  
    function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
        return TriggerAddCondition(IndexTrig[eventtype], func)
    endfunction
  
    function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
        call TriggerRemoveCondition(IndexTrig[eventtype], c)
    endfunction
  
    function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
        call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
    endfunction
  
    function OnUnitIndex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
    endfunction

    function OnUnitDeindex takes code func returns triggercondition
        return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
    endfunction
  
    /****************************************************************/
  
    private keyword UnitDexCore
  
    struct UnitDex extends array
        static boolean Enabled = true
      
        readonly static integer LastIndex
        readonly static boolean Initialized=false
        readonly static group Group=CreateGroup()
        readonly static unit array Unit
        readonly static integer Count = 0
        readonly static integer array List
      
        implement UnitDexConfig
      
        private static integer Counter = 0
      
        implement UnitDexCore
    endstruct
  
    function UnitDexRemove takes unit u, boolean runEvents returns boolean
        return UnitDex.Remove(u, runEvents)
    endfunction
  
    /****************************************************************/
    
    private module UnitDexCore
  
        static method Remove takes unit u, boolean runEvents returns boolean
            local integer i
          
            if (IsUnitIndexed(u)) then
                set i = GetUnitId(u)
                set UnitDex.List[i] = Index
                set Index = i
              
                call GroupRemoveUnit(UnitDex.Group, u)
                call SetUnitUserData(u, 0)
          
                if (runEvents) then
                    set UnitDex.LastIndex = i
                    set E = EVENT_UNIT_DEINDEX
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                    set E = -1
                endif
              
                set UnitDex.Unit[i] = null
                set UnitDex.Count = UnitDex.Count - 1
              
                return true
            endif
          
            return false
        endmethod
      
        private static method onGameStart takes nothing returns nothing
            local integer i = 1
          
            loop
                exitwhen i > Counter
              
                set LastIndex = i
              
                call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                set E = EVENT_UNIT_INDEX
                set E = -1
              
                set i = i + 1
            endloop

            set LastIndex   = Counter
            set Initialized = true
            set FilterEnter = null
          
            call DestroyTimer(GetExpiredTimer())
        endmethod
      
        private static method onEnter takes nothing returns boolean
            local unit    u = GetFilterUnit()
            local integer i = GetUnitId(u)
            local integer t = Index
          
            if (i == 0 and thistype.Enabled) then
              
                // If a filter was defined pass the unit through it.
                static if (thistype.onFilter.exists) then
                    if (not thistype.onFilter(u)) then
                        set u = null
                        return false // check failed
                    endif
                endif
              
                // Handle debugging
                static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
                    if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
                        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
                        set u = null
                        return false
                    endif
                endif
              
                // Add to group of indexed units
                call GroupAddUnit(thistype.Group, u)
              
                // Give unit the leave detection ability
                call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
                call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
              
                // Allocate index
                if (Index != 0) then
                    set Index = List[t]
                else
                    set Counter = Counter + 1
                    set t = Counter
                endif
              
                set List[t] = -1
                set LastIndex = t
                set thistype.Unit[t] = u
                set Count = Count + 1
              
                // Store the index
                call SetUnitUserData(u, t)
              
                if (thistype.Initialized) then
                    // Execute custom events registered with RegisterUnitIndexEvent
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
                  
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_INDEX

                    // Reset so the event can occur again
                    set E = -1
                endif
            endif
          
            set u = null
          
            return false
        endmethod

        private static method onLeave takes nothing returns boolean
            local unit    u
            local integer i
          
            // Check if order is undefend.
            if (thistype.Enabled and GetIssuedOrderId() == 852056) then
              
                set u = GetTriggerUnit()
              
                // If unit was killed (not removed) then don't continue
                if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
                    set u = null
                    return false
                endif
              
                set i = GetUnitId(u)

                // If unit has been indexed then deindex it
                if (i > 0 and i <= Counter and u == GetUnitById(i)) then
                  
                    // Recycle the index
                    set List[i]   = Index
                    set Index     = i
                    set LastIndex = i
                  
                    // Remove to group of indexed units
                    call GroupRemoveUnit(thistype.Group, u)
              
                    // Execute custom events without any associated triggers
                    call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
                  
                    // Handle TriggerRegisterUnitIndexEvent
                    set E = EVENT_UNIT_DEINDEX
                  
                    // Remove entry
                    call SetUnitUserData(u, 0)
                    set thistype.Unit[i] = null
                  
                    // Decrement unit count
                    set Count = Count - 1
              
                    // Reset so the event can occur again
                    set E = -1
                endif
              
                set u = null
            endif
          
            return false
        endmethod
      
        private static method onInit takes nothing returns nothing
            local trigger t         = CreateTrigger()
            local integer i         = 0
            local player p
            local unit u
          
            static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
                local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
                local rect world = GetWorldBounds()
            endif
          
            static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
                local group ENUM_GROUP = CreateGroup() // If not, create the group.
            endif
          
            set FilterEnter = Filter(function thistype.onEnter)
          
            // Begin to index units when they enter the map
            static if (LIBRARY_WorldBounds) then
                call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
            else
                call RegionAddRect(reg, world)
                call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
                call RemoveRect(world)
                set world = null
            endif
          
            call TriggerAddCondition(t, Filter(function thistype.onLeave))
          
            set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
            set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
          
            loop
                set p = Player(i)
              
                // Detect "undefend"
                call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
              
                // Hide the detect ability from players
                call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
              
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
          
            // Index preplaced units
            set i = 0
            loop
                call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
              
                set i = i + 1
              
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
          
            static if (not LIBRARY_GroupUtils) then
                call DestroyGroup(ENUM_GROUP)
                set ENUM_GROUP = null
            endif

            set LastIndex = Counter
          
            // run init triggers
            call TimerStart(CreateTimer(), 0.00, false, function thistype.onGameStart)
        endmethod
  
    endmodule
  
endlibrary


Examples


JASS:
set GlobalInteger[GetUnitId(unit)] = 50

JASS:
function onIndex takes nothing returns boolean
    call BJDebugMsg(GetUnitName(GetIndexedUnit()) + "[" + I2S(GetIndexedUnitId()) + "] enters the map.")
    return false
endfunction

function onDeindex takes nothing returns boolean
    call BJDebugMsg(GetUnitName(GetIndexedUnit()) + "[" + I2S(GetIndexedUnitId()) + "] leaves the map.")
    return false
endfunction

//===========================================================================
function InitTrig_UnitDex_Example takes nothing returns nothing
    call RegisterUnitIndexEvent(Filter(function onIndex)  , EVENT_UNIT_INDEX)
    call RegisterUnitIndexEvent(Filter(function onDeindex), EVENT_UNIT_DEINDEX)
endfunction

JASS:
struct UnitData
    static method operator[] takes unit u returns integer
        return GetUnitId(u)
    endmethod
endstruct

local UnitData data = UnitData[GetTriggerUnit()]


Compatibility With Other Indexers

UnitIndexer

JASS:
library UnitIndexer requires UnitDex
  
    struct UnitIndexer
        readonly static integer INDEX   = EVENT_UNIT_INDEX
        readonly static integer DEINDEX = EVENT_UNIT_DEINDEX
      
        static method operator enabled= takes boolean b returns nothing
            set UnitDex.Enabled=b
        endmethod
      
        static method operator enabled takes nothing returns boolean
            return UnitDex.Enabled
        endmethod
    endstruct
  
    module UnitIndexStructMethods
        static method operator [] takes unit u returns thistype
            return GetUnitUserData(u)
        endmethod
      
        method operator unit takes nothing returns unit
            return UnitDex.Unit[this]
        endmethod
    endmodule
  
    module UnitIndexStruct
        implement UnitIndexStructMethods
      
        method operator allocated takes nothing returns boolean
            return this==GetUnitUserData(UnitDex.Unit[this])
        endmethod
    endmodule

    function IsUnitDeindexing takes unit u returns boolean
        return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u, UnitDex.DETECT_LEAVE_ABILITY)
    endfunction
  
    struct UnitIndex extends array
        method lock takes nothing returns nothing
        endmethod
      
        method unlock takes nothing returns nothing
        endmethod
      
        method operator unit takes nothing returns unit
            return UnitDex.Unit[this]
        endmethod
      
        static method operator [] takes unit whichUnit returns thistype
            return GetUnitUserData(whichUnit)
        endmethod
    endstruct
  
endlibrary

GUI Unit Indexer

JASS:
library UnitIndexerGUI initializer Init requires UnitDex
  
    private function OnIndex takes nothing returns nothing
        set udg_UDex = GetIndexedUnitId()
        set udg_UDexUnits[udg_UDex] = GetIndexedUnit()
        set udg_UnitIndexEvent = 1.
        set udg_UnitIndexEvent = 0.
    endfunction
  
    private function OnDeindex takes nothing returns nothing
        set udg_UDex = GetIndexedUnitId()
        set udg_UDexUnits[udg_UDex] = GetUnitById(udg_UDex)
        set udg_UnitIndexEvent = 2.
        set udg_UnitIndexEvent = 0.
    endfunction
  
    private function Init takes nothing returns nothing
        call OnUnitIndex(function OnIndex)
        call OnUnitIndex(function OnDeindex)
    endfunction

endlibrary

AIDS

(Thanks to Bannar)
JASS:
library AIDS requires UnitDex

    public function RegisterOnEnter takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_INDEX)
    endfunction

    public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_INDEX)
    endfunction

    public function RegisterOnDeallocate takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_DEINDEX)
    endfunction

    function GetIndexUnit takes integer index returns unit
        return GetUnitById(index)
    endfunction

    function GetUnitIndex takes unit u returns integer
        return GetUnitId(u)
    endfunction

    public struct DEFAULT extends array
        method AIDS_onCreate takes nothing returns nothing
        endmethod

        method AIDS_onDestroy takes nothing returns nothing
        endmethod
      
        static method AIDS_filter takes unit u returns boolean
            return true
        endmethod
      
        static method AIDS_onInit takes nothing returns nothing
        endmethod
    endstruct

    //! textmacro AIDS
        private static delegate AIDS_DEFAULT deleg = 0

        method AIDS_addLock takes nothing returns nothing
        endmethod

        method AIDS_removeLock takes nothing returns nothing
        endmethod

        static method operator[] takes unit whichUnit returns thistype
            return GetUnitId(whichUnit)
        endmethod

        method operator unit takes nothing returns unit
            return GetUnitById(this)
        endmethod

        private static method AIDS_onEnter takes nothing returns boolean
            call thistype(GetIndexedUnitId()).AIDS_onCreate()
            return false
        endmethod

        private static method AIDS_onDeallocate takes nothing returns boolean
            if IsUnitIndexed(GetIndexedUnit())  then
                call thistype(GetIndexedUnitId()).AIDS_onDestroy()
            endif
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterUnitIndexEvent(Filter(function thistype.AIDS_onEnter), EVENT_UNIT_INDEX)
            call RegisterUnitIndexEvent(Filter(function thistype.AIDS_onDeallocate), EVENT_UNIT_DEINDEX)

            call thistype.AIDS_onInit()
        endmethod
    //! endtextmacro

endlibrary

AutoIndex
JASS:
library AutoIndex requires UnitDex

    function interface IndexFunc takes unit u returns nothing
  
    struct AutoIndex
  
        private static IndexFunc array indexfuncs
        private static integer indexfuncs_n = -1
        private static IndexFunc array deindexfuncs
        private static integer deindexfuncs_n = -1
        private static IndexFunc indexfunc
  
        static method getIndex takes unit u returns integer
            return GetUnitId(u)
        endmethod
      
        static method isUnitIndexed takes unit u returns boolean
            return u != null and UnitDex.Unit[GetUnitId(u)] == u
        endmethod
  
        static method getIndexDebug takes unit u returns integer
            if u == null then
                return 0
            elseif GetUnitTypeId(u) == 0 then
                   call BJDebugMsg("AutoIndex error: Removed or decayed unit passed to GetUnitId.")
            elseif UnitDex.Unit[GetUnitId(u)] != u and GetIssuedOrderId() != 852056 then
                call BJDebugMsg("AutoIndex error: "+GetUnitName(u)+" is a filtered unit.")
            endif
          
            return getIndex(u)
        endmethod
      
        private static method onUnitIndexed_sub takes nothing returns nothing
            call indexfunc.evaluate(GetEnumUnit())
        endmethod
      
        static method onUnitIndexed takes IndexFunc func returns nothing
            set indexfuncs_n = indexfuncs_n + 1
            set indexfuncs[indexfuncs_n] = func
            if UnitDex.Initialized then //During initialization, evaluate the indexfunc for every preplaced unit.
                set indexfunc = func
                call ForGroup(UnitDex.Group, function AutoIndex.onUnitIndexed_sub)
            endif
        endmethod
      
        static method onUnitDeindexed takes IndexFunc func returns nothing
            set deindexfuncs_n = deindexfuncs_n + 1
            set deindexfuncs[deindexfuncs_n] = func
        endmethod
    endstruct
  
    function OnUnitIndexed takes IndexFunc func returns nothing
        call AutoIndex.onUnitIndexed(func)
    endfunction

    function OnUnitDeindexed takes IndexFunc func returns nothing
        call AutoIndex.onUnitDeindexed(func)
    endfunction

endlibrary

Changelog

v1.2.2
- UnitDexRemove was moved below the struct to prevent JassHelper from generating an unnecessary function
- Pre-placed units are now enumerated on initialization.

v1.2.1
- UnitDexRemove now returns true on success
- Remove method added to struct
- UnitDexRemove now just calls .Remove
- Fixed a bug where UnitDex.Count wouldn't decrease when a unit left
- OnUnitIndex/OnUnitDeindex functions added
- LastIndex now readonly
- Hashtable option removed (due to static if's not working properly with modules outside of the struct)
- Minor optimizations

v1.2.0
- IsUnitIndexed now works correctly when using the hashtable version
- UnitRecycleId -> UnitDexRemove
- UnitDexRemove now set the units id to zero
- When using hashtable version the stored integer now gets reset to zero

v1.1.9
- Bug fixes

v1.1.8
- Fixed a bug where events would fire an extra time on initialization.
- readonly boolean "Initialized" added to the UnitDex struct.

v1.1.7
- A public unitgroup (UnitDex.Group) has been implemented to allow easy enumeration of all indexed units.
- TimerUtils removed due to module initializing.
- Documentation improved.

v1.1.6
- Events registered at initialization are now properly executed via a timer.
- Because of the timer required for runInitTriggers, TimerUtils has become optional for preloading and recycling.

v1.1.5
- Units are now stored in a publicly accessible, readonly array (UnitDex.Unit).
- Initialization is now done through a module to ensure all units are indexed.
- A few globals have been moved to the UnitDex struct, to allow public use.
- The private function, SetUnitId was removed.
- PlayerUtils optional requirement removed, due to module initializing.

v1.1.4
- Flush removed.
- UnitDexConfig no longer optional.
- Filtering units is now done by defining onFilter inside the configuration. If the method doesn't exist than static if's will remove unused code.
- Removed the need for a trigger queue by using TriggerRegisterVariableEvent, meaning much better efficiency and cleaner code.

v1.1.3
- UnitDex struct implemented.
- Removed unused variables
- You can now reset the indexer via .Flush() or UnitDexReset.
- Bounds safety removed because it had little performance benefit.
- You can now filter which units get indexed through the UnitDexOptions module.
- IsIndexingEnabled added.
- UnitRecycleIndex renamed to UnitRecycleId.

v1.1.2
- Fixed a small leak on initialization where I nulled the region instead of the rect.
- Evaluating the trigger queue is now ignored when there's no registered events.
- RemoveUnitIndexEvent implemented to allow you to unregister events.
- RegisterUnitIndexEvent now returns triggercondition so you can catch the handle.

v1.1.1
- Fixed a few bugs concerning the hashtable only version.
- UnitRecycleIndex implemented to allow manual deindexing.
- Can now toggle the indexer with UnitDexEnable.
- onLeave now nulls the unit index.
- Optimized GetIndexedUnit.
- IsUnitIndexed added.
- Units properly removed from the table if hashtable was used for indexing.

v1.1.0
- Event API simplified without sacrificing inlining.
- Merged trigger queue counters.

v1.0.5
- Bounds Safety now sets user data as well.
- GetUnitId now inlines with bounds safety on.
- Bounds Safety no longer uses hashtable for storing the index (performance boost).
- Event API modified to allow inlining as well as allow you to register indexing and deindexing events in a single call.

v1.0.4
- Defaulted LastIndexedUnit to 0 to prevent thread crashing.
- Fixed an issue where onLeave would fire for units who cast the generic Defend ability.
- Fixed an issue where onLeave would only fire the first trigger registered.
- Now the script doesn't deindex dead units, only removed ones.
- Merged trigger queues into one variable and made a wrapper for looping through them.
- Fixed a bug where a hashtable would be created regardless.

v1.0.3
- Documentation improved.
- Fixed an issue where some optional libraries wouldn't be detected.
- Fixed a leak that only occured in debug mode.

v1.0.2
- Debugging options added.
- Fixed an issue where a units id would be -1 on deindexing events.
- Added a bounds safety option that will switch to a hashtable once the array limit has been reached.

v1.0.1
-- Improved variable names
-- Fixed a possible leak.
 
Last edited:
Updated.

Code:
v1.0.4
- Defaulted LastIndexedUnit to 0 to prevent thread crashing.
- Fixed an issue where onLeave would fire for units who cast the generic Defend ability.
- Fixed an issue where onLeave would only fire the first trigger registered.
- Now the script doesn't deindex dead units, only removed ones.
- Merged trigger queues into one variable and made a wrapper for looping through them.
- Fixed a bug where a hashtable would be created regardless.

What do you all think of initializing via a module?

EDIT: Updated (the lite version is behind).

Code:
v1.0.5
- Bounds Safety now sets user data as well.
- GetUnitId now inlines with bounds safety on.
- Bounds Safety no longer uses hashtable for storing the index (performance boost).
- Event API modified to allow inlining as well as allow you to register indexing and deindexing events in a single call.
 
Last edited:
Splitting versions is pointless. Lite differs from normal with PlayerUtils? Considering you called it "Light" already brings us solution: Post just one with "optional" directive for each of it's requirements or post two: with requirements and second with none (optionals).

Ruke has a point. Even though some of many Nes reasources may be not usefull in wc3, if we look at his UI it's basically perfect. One does not need anything more. Apart from that, his lib isn't requirement heavy. It requires WorldBounds (which can be easily made optional) and Event - honestly everyone uses somekind of event and even if not, it still can be quite easily omited too. If I'm not mistaken he even gave us UI without any requirement.
 
uhmm, why should we use yours instead of nestharus' one (which can be found here) or even aids?

Neither of those are on Hive and this is basically the only active modding site for Warcraft III at this point. I feel we needed one of these here. AIDS' implementation is much different than mine and I personally think the Syntax is horrible. UnitIndexers readability has much to be desired, and it works a bit different than mine. Oh, and mine has hashtable and bounds safety options. This is why we have variety.

Splitting versions is pointless. Lite differs from normal with PlayerUtils?

Each version has the exact same requirements. Why don't you look through the code first?

The Lite version provides no options but much more readable code because of the lack of static if's and extra functionality.

Ruke has a point. Even though some of many Nes reasources may be not usefull in wc3, if we look at his UI it's basically perfect. One does not need anything more. Apart from that, his lib isn't requirement heavy. It requires WorldBounds (which can be easily made optional) and Event - honestly everyone uses somekind of event and even if not, it still can be quite easily omited too. If I'm not mistaken he even gave us UI without any requirement

Ruke never made a point, lol.

Anyway what makes his UnitIndexer "basically perfect" that the previous indexers before didn't have? I mean really this same logic could have been applied when UI was submitted in the first place.

Not to mention mine likely performs more efficiently, although it's negligible.

Updated to version 1.1.0 because API should be final (unless new functions are added).
 
Last edited:
Level 10
Joined
Sep 19, 2011
Messages
527
Neither of those are on Hive and this is basically the only active modding site for Warcraft III at this point. I feel we needed one of these here. AIDS' implementation is much different than mine and I personally think the Syntax is horrible. UnitIndexers readability has much to be desired, and it works a bit different than mine. Oh, and mine has hashtable and bounds safety options. This is why we have variety.

i wouldn't be so sure about that (the only active site).

aids, uhmm yes, the syntax is horrible, but the api is quite nice, and that is what you're going to be using, so...

unitindexer yes, the names are horrible, but again, the api is what you are going to be using and it is quite nice too.

not to mention that these resources have been tested a lot by the users.

in my opinion, the safety option is useless. if you have that many units on your map you should consider to fix that.

Anyway what makes his UnitIndexer "basically perfect" that the previous indexers before didn't have? I mean really this same logic could have been applied when UI was submitted in the first place.

it is not perfect, it just works (as far as we know pretty well actually).

Not to mention mine likely performs more efficiently, although it's negligible.

careful, benchmark please.
although, we are talking about an unit indexer, efficiency isn't that important.

edit: by the way

People should just stop abusing module initializers..

agree
 
Ruke said:
aids, uhmm yes, the syntax is horrible, but the api is quite nice, and that is what you're going to be using, so...

unitindexer yes, the names are horrible, but again, the api is what you are going to be using and it is quite nice too.

The AIDS' textmacro is part of the API, as well as the AIDS_ struct methods, which I personally think are ugly.

UnitIndexer's source is complete spaghetti code, however it compiles fairly normal.

again, variety + no_more_nestharus.

Ruke said:
not to mention that these resources have been tested a lot by the users.

Well good thing I use the same "undefend" method then ;)

Ruke said:
i wouldn't be so sure about that (the only active site).

I'm sure about it.

Maybe your standards for activity are different than mine ;)

Ruke said:
it is not perfect, it just works (as far as we know pretty well actually).

Well if Nestharus didn't rage quit like an emotional baby than I honestly wouldn't have submitted this.

I don't want to use libraries from someone who is unstable and can't decide where he wants his damn resources, or even how to code them (Example).

Yes, it just works but it's also not a part of this site anymore.

... and really, "it just works" ? haha

Ruke said:
careful, benchmark please.
although, we are talking about an unit indexer, efficiency isn't that important.

Careful of what? The claims aren't that crazy.

I've benchmarked previous versions and everything executed faster than UnitIndexer by a little bit, or matched it in speed.

I suppose I'll test again and post the results.
 
Level 10
Joined
Sep 19, 2011
Messages
527
Well good thing I use the same "undefend" method then ;)

yes, but the implementation is different (a unit indexer it's not just "undefend").

Careful of what? The claims aren't that crazy.

I've benchmarked previous versions and everything executed faster than UnitIndexer by a little bit, or matched it in speed.

I suppose I'll test again and post the results.

well yes... of course, post the results ._.

by the way, why hiveworkshop should only use resources from hiveworkshop?.

the parts that i didn't quote, it is because they're out of context (except for the aids api).
 
Ruke said:
yes, but the implementation is different (a unit indexer it's not just "undefend").

Yes that's true but if you look at the source of onLeave, not much can go wrong.

It's fairly transparent.

Ruke said:
by the way, why hiveworkshop should only use resources from hiveworkshop?.

No, lol.

Like I said before, I feel we needed one of these here and it differs from the already approved ones at wc3c and thehelper.

Ruke said:
well yes... of course, post the results ._.

The difference is microseconds but I'll perform the tests again.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
I'm agree with TriggerHappy, blame Nestharus if you wan't but plz don't blame someone just because he is trying to fill a hole caused by an ego rage quit.
Nestharus has always been unreliable anyway, and if you don't think it, then you're blind. (ofc it's still my opinion, you can have the opposite one, but let's say my statement is based with facts, not just because i don't like him)
 
Level 10
Joined
Sep 19, 2011
Messages
527
AIDS has an awfull api and you can't catch when an unit is removed, so i have not talk about it at all ...

yes you can:

JASS:
struct test
    //! runtextmacro AIDS()
    
    private method AIDS_onCreate takes nothing returns nothing
        call BJDebugMsg("created")
    endmethod
    private method AIDS_onDestroy takes nothing returns nothing
        call BJDebugMsg("removed")
    endmethod
    
    private static method AIDS_onInit takes nothing returns nothing
        call RemoveUnit(CreateUnit(Player(0), 'hfoo', 0, 0, 0))
    endmethod
endstruct
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
And it will work also when an unit is removed because of the end of decay and instant, not delayed ?

EDIT : Ok, it seems so, i was wrong about that point, i guess it has been added when the 'Adef' trick has been found, my bad.
EDIT 2
: Yep
- Version 1.1.0: Added support for undefend unit leaves map detection. A constant boolean can be used to determine whether or not the system runs on undefend removal detection or a periodic timer. Made the AIDS filter function non-constant.
 
Updated.

Code:
v1.1.1
- Fixed a few bugs concerning the hashtable only version.
- UnitRecycleIndex implemented to allow manual deindexing.
- Can now toggle the indexer with UnitDexEnable.
- onLeave now nulls the unit index.
- Optimized GetIndexedUnit.
- IsUnitIndexed added.
- Units properly removed from the table if hashtable was used for indexing.

I'm going to be doing some tests and if BOUNDS_SAFETY doesn't provide any performance benefits I'll remove the option.
 
I decided to keep BOUNDS_SAFETY because it proved to be about ~10% faster than than hashtable when there's less than 8191 units, however I may remove it later to make the code less ugly.

BOUNDS_SAFETY removed.

Code:
v1.1.3
- UnitDex struct implemented.
- Removed unused variables
- You can now reset the indexer via .Flush() or UnitDexReset.
- Bounds safety removed because it had little performance benefit.
- You can now filter which units get indexed through the UnitDexOptions module.
- Is IndexingEnabled added.

v1.1.2
- Fixed a smal leak on initialization where I nulled the region instead of the rect.
- Evaluating the trigger queue is now ignored when there's no registered events.
- RemoveUnitIndexEvent implemented to allow you to unregister events.
- RegisterUnitIndexEvent now returns triggercondition so you can catch the handle.
 
Last edited:
Level 10
Joined
Sep 19, 2011
Messages
527
you should use the real event for the triggers, that way your code will be smaller/simplier.

i don't like the UnitDexOptions module, i think it would be better to just add a global filter (for the entire system).

i don't see any use case for the reset feature.

debug boolean UnitDex___debugBool = false unused
 
Ruke said:
you should use the real event for the triggers, that way your code will be smaller/simplier.

hmm, to remove the trigger queue?

I'll see

Ruke said:
i think it would be better to just add a global filter (for the entire system).

A global filter would be constant.

Ruke said:
I don't see any use case for the reset feature.

you may be right
 
Last edited:
Updated. I think the configuration is more convenient now.

Code:
v1.1.4
- Flush removed.
- UnitDexConfig no longer optional.
- Filtering units is now done by defining onFilter inside the configuration. If the method doesn't exist than static if's will remove unused code.
- Removed the need for a trigger queue by using TriggerRegisterVariableEvent, meaning much better efficiency and cleaner code.
 
At least API hasn't changed -> since this will get approved soon, it would be nice if some libs support it. Function names are basically the same (as UnitIndexer). Guys, don't get me wrong (or maybe u did already) Nes' lib was here for a long time, and everyone knows how popular it is as requirement. The only reason why I posted response in regard to UI is fact that now, each lib should also add support for this (libraries without support are.. (you end the sentence yourself)).

Honestly, there should be some kind of std:
JASS:
library_once UnitIndexing

static if LIBRARY_AIDS then
// implement textmacro with AIDS libs
elseif LIBRARY_UnitIndexer then
// implement textmacro with UnitIndexer
elseif LIBRARY_UnitDex then
// implement textmacro with UnitDex
endif

endlibrary
With such standards the only thing given user would have to do is: "require UnitIndexing" instead or writing "optional this, optional that" in each library.

EDIT: Unregistering triggercondition might be a useless part -> basically none uses it, since it requires storing the handle when you actually add condition. Thats why in newer version of RestoreTrigger I've decided to stick with "code" and/or "boolexpr" since user won't be forced to store those rarely used stuff. Of course, for the sake of completness meaby it should stay as it is.
 
Level 10
Joined
Sep 19, 2011
Messages
527
or people could just utilize GetUnitUserData

what if you use hashtable

EDIT: Unregistering triggercondition might be a useless part -> basically none uses it, since it requires storing the handle when you actually add condition. Thats why in newer version of RestoreTrigger I've decided to stick with "code" and/or "boolexpr" since user won't be forced to store those rarely used stuff. Of course, for the sake of completness meaby it should stay as it is.

agree
 
TriggerHappy, meaby my example wasn't the best, what I rly meant:

JASS:
library_once UnitIndexing

//    API

   keyword RegisterUnitIndexEvent //takes boolexpr b, Event ev returns nothing
   keyword TriggerRegisterUnitIndexEvent //takes trigger t, Event ev returns nothing

   keyword GetUnitById //takes integer index returns unit
   keyword GetUnitId //takes unit u returns integer

   keyword IsUnitIndexed //takes unit u returns boolean
   keyword IsUnitDeindexing //takes unit u returns boolean

   keyword GetIndexedUnitId //takes nothing returns integer
   keyword GetIndexedUnit //takes nothing returns unit

static if LIBRARY_AIDS then
// implement textmacro with AIDS libs
elseif LIBRARY_UnitIndexer then
// implement textmacro with UnitIndexer
elseif LIBRARY_UnitDex then
// implement textmacro with UnitDex
endif

endlibrary
You get the point, y? Unification.

Unfortunately vjass struct polymorphism or basically any kind of inheritence generates trash code, thus you get ppl bashing your head over and over. Wish we had option to implement "base class" for each of such libs/issues, yet module interface or textmacros are probably the only available and "possitive" options and those don't offer such features.
 
Btw, ain't this simpler:
JASS:
// since below takes in account your const bool
    private function SetUnitId takes unit whichUnit, integer index returns nothing
        static if (UnitDex.DEFAULT_HASHTABLE) then
            call SaveInteger(UnitDex.Hash, 0, GetHandleId(whichUnit), index)
        else
            call SetUnitUserData(whichUnit, index)
        endif
    endfunction

// ("onEnter" method) you could simplify this part:
    static if (UnitDex.DEFAULT_HASHTABLE) then
        call SaveInteger(UnitDex.Hash, 0, GetHandleId(u), t)
        call SaveUnitHandle(UnitDex.Hash, 0, t, u)
    else
        call SetUnitId(u, t)
    endif

// to
    call SetUnitId(u, t)
    static if (UnitDex.DEFAULT_HASHTABLE) then
        call SaveUnitHandle(UnitDex.Hash, 0, t, u)
    endif
My next thought - why "U" is even implemented when user chose to use hashtable format. You follow code shown by me above with: set U[t] = u while within "onLeave" method you check for constant instead. I could even question the existence of "SetUnitId" function. It is used only once and I doubt that more one enter + one static if is less readable than what's already within, static if is already there anyways.
JASS:
// onEnter
    static if (UnitDex.DEFAULT_HASHTABLE) then
        call SaveInteger(UnitDex.Hash, 0, GetHandleId(u), t)
        call SaveUnitHandle(UnitDex.Hash, 0, t, u)
    else
        call SetUnitUserData(t, u)
        set U[t] = u
    endif
Meaby I'm just dumb and tired after exams, and yes I've not looked carefully through the code, yet meaby there is a point.
 
Few more: does it handle nested events?

"initializer Init" directive instead of module initializer makes this virtualy useless.

I just wanted to add support for your lib in few of my scripts and spent hour or so finding out why suddently everything blown up. There are systems and scripts which require immidiate registering via unit indexer. Now, simple modification:
JASS:
    function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
        call BJDebugMsg("registering: " + I2S(eventtype))
        if ( IndexTrig[eventtype] == null ) then
            call BJDebugMsg("does not exist")
        endif
        return TriggerAddCondition(IndexTrig[eventtype], func)
    endfunction
told me everything..

I don't think any indexer should allow itself to be "late" in registering.
 
Spinnaker said:
TriggerHappy, meaby my example wasn't the best, what I rly meant:

I'm not really sure if that's a good idea.

Ruke said:
what if you use hashtable
Don't all these systems include a GetUnitId function?

I also realize that a unit indexer is generally more than just that, however for spells GetUnitId should the only function used.

Spinnaker said:
My next thought - why "U" is even implemented when user chose to use hashtable format.

I guess I missed that.

Spinnaker said:
I could even question the existence of "SetUnitId" function. It is used only once and I doubt that more one enter + one static if is less readable than what's already within, static if is already there anyways.

The function is inlined and produces smaller code, so I don't see your point?

You're also forgetting I previously had BOUNDS_SAFETY which made each function more complicated if it was enabled.

Spinnaker said:
I don't think any indexer should allow itself to be "late" in registering.

Sure I'll include that in debug mode, and I suppose I should use module initializing.

The reason I didn't before was again, because of BOUNDS_SAFETY and it would create much more static if's.
 
Your call. I won't force you to change anything, neither algorithm nor workflow. There are no major issues (except for module init), library is written nicely.
I find it possitive when receiving feeback no matter if it's yell or approve; gives me at least satisfaction that someone spent time on my resource; it's kind of motivation.

EDIT: how about adding ref count based indexing? There are no lock/unlock counterparts from AIDS/UI.
 
Last edited:
Here you have another support:
JASS:
library AIDS requires UnitDex

    public function RegisterOnEnter takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_INDEX)
    endfunction

    public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_INDEX)
    endfunction

    public function RegisterOnDeallocate takes boolexpr b returns triggercondition
        return RegisterUnitIndexEvent(b, EVENT_UNIT_DEINDEX)
    endfunction

    function GetIndexUnit takes integer index returns unit
        return GetUnitById(index)
    endfunction

    function GetUnitIndex takes unit u returns integer
        return GetUnitId(u)
    endfunction

    public struct DEFAULT extends array
        method AIDS_onCreate takes nothing returns nothing
        endmethod

        method AIDS_onDestroy takes nothing returns nothing
        endmethod
        
        static method AIDS_filter takes unit u returns boolean
            return true
        endmethod
        
        static method AIDS_onInit takes nothing returns nothing
        endmethod
    endstruct

    //! textmacro AIDS
        private static delegate AIDS_DEFAULT deleg = 0

        method AIDS_addLock takes nothing returns nothing
        endmethod

        method AIDS_removeLock takes nothing returns nothing
        endmethod

        static method operator[] takes unit whichUnit returns thistype
            return GetUnitId(whichUnit)
        endmethod

        method operator unit takes nothing returns unit
            return GetUnitById(this)
        endmethod

        private static method AIDS_onEnter takes nothing returns boolean
            call thistype(GetIndexedUnitId()).AIDS_onCreate()
            return false
        endmethod

        private static method AIDS_onDeallocate takes nothing returns boolean
            if IsUnitIndexed(GetIndexedUnit())  then
                call thistype(GetIndexedUnitId()).AIDS_onDestroy()
            endif
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterUnitIndexEvent(Filter(function thistype.AIDS_onEnter), EVENT_UNIT_INDEX)
            call RegisterUnitIndexEvent(Filter(function thistype.AIDS_onDeallocate), EVENT_UNIT_DEINDEX)

            call thistype.AIDS_onInit()
        endmethod
    //! endtextmacro

endlibrary
Backward compatibility with AIDS. However, your lib does not feature ref counted indexes, neither it does provide module api thus I'm unsure if AIDS_instanciated should be removed - someone might actually use this. Filter usage has been cut because of exact the same reason - user has no ability to prevent actual allocation or deallocation.
Requires UnitDex to use module initializer, otherwise it will fail with indexing.
 
Updated.

Code:
v1.1.5
- Units are now stored in a publicly accessible, readonly array (UnitDex.Unit).
- Initialization is now done through a module to ensure all units are indexed.
- A few globals have been moved to the UnitDex struct, to allow public use.
- The private function, SetUnitId was removed.
- PlayerUtils optional requirement removed, due to module initializing.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
I have nothing against TimerUtils (well expected that the blue version of Vexorian is not done correctly and still not fixed :grin:), but here there is really 0 pro to use it, it just makes the code "harder" to read and more spaghetti-like with this added static if.
Same for GroupUtils.
Hell, we are talking about one script running only one time at init.

Btw IIRC create/destroy group was about the same speed or even (much ?, but yeah all is relative) than group recycling, really only "interactive" handles, such as units, worth a recycling (if it's possible and doesn't bring more problems than it solves, so no for every unit recycling)
 
Last edited:
I have nothing against TimerUtils (well expected that the blue version of Vexorian is not done correctly and still not fixed :grin:), but here there is really 0 pro to use it, it just makes the code "harder" to read and more spaghetti-like with this added static if.
Same for GroupUtils.
Hell, we are talking about one script running only one time at init.

Btw IIRC create/destroy group was about the same speed or even (much ?, but yeah all is relative) than group recycling, really only "interactive" handles, such as units, worth a recycling (if it's possible and doesn't bring more problems than it solves, so no for every unit recycling)

Well, it's already written so why not use it?

It's obviously better to use cached handles when possible, and the static if's only make the end of initialization spaghetti-like. It was never about the performance increase.

I see your point, though.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I disagree it's not better to cache handles when possible, i mean it's not an absolute general fact.
Due to what i've said above.

Ofc yes you should recycle dummies units because create/destroy an unit is the most costly jass task ever, and if you don't it's most lileky players will have spikes lag.
 
Top