1. 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
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. 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.

[System] UnitDex - Unit Indexer

Discussion in 'JASS Resources' started by TriggerHappy, Feb 13, 2014.

  1. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    System Code
    Code (vJASS):
    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


    Code (vJASS):


    set GlobalInteger[GetUnitId(unit)] = 50
     


    Code (vJASS):


    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
     


    Code (vJASS):


    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

    by Nestharus
    Code (vJASS):
    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

    by Bribe
    Code (vJASS):
    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

    by Jesus4Lyf
    (Thanks to Bannar)
    Code (vJASS):
    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
    by Grim001
    Code (vJASS):
    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
    Log

    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: Jul 6, 2018
  2. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    why those variable names?
     
  3. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    They weren't that hard to follow originally, but updated.

    Code (Text):

    v1.0.1
    -- Improved variable names.
    -- Fixed a possible leak.
     
     
  4. Daffa the Mage

    Daffa the Mage

    Map Moderator

    Joined:
    Jan 30, 2013
    Messages:
    7,685
    Resources:
    27
    Packs:
    1
    Maps:
    8
    Spells:
    16
    Tutorials:
    2
    Resources:
    27
    Good, we can use a replacement since Nestharus taken down all of his resources.
     
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    The table stretches the page on my monitor. Perhaps move the examples down to a separate row or make it tabbed.
     
  6. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated.

    Code (Text):

    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.
     
    Last edited: Feb 14, 2014
  7. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
  8. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
  9. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated.

    Code (Text):

    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 (Text):

    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: Feb 14, 2014
  10. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    uhmm, why should we use yours instead of nestharus' one (which can be found here) or even aids?
     
  11. looking_for_help

    looking_for_help

    Joined:
    Dec 12, 2012
    Messages:
    961
    Resources:
    5
    Spells:
    2
    JASS:
    3
    Resources:
    5
    I don't like modules at all because they split up everything and produce spaghetti code. However for initialization they should be used IMO as only that guarantees that they are initialized at first.
     
  12. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,087
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    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.
     
  13. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    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.

    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 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: Feb 19, 2014
  14. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    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.

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

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

    edit: by the way

    agree
     
  15. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    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.

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

    I'm sure about it.

    Maybe your standards for activity are different than mine ;)

    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

    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.
     
  16. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    yes, but the implementation is different (a unit indexer it's not just "undefend").

    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).
     
  17. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Yes that's true but if you look at the source of onLeave, not much can go wrong.

    It's fairly transparent.

    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.

    The difference is microseconds but I'll perform the tests again.
     
    Last edited: Feb 19, 2014
  18. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    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)
     
  19. Ruke

    Ruke

    Joined:
    Sep 19, 2011
    Messages:
    517
    Resources:
    7
    Tools:
    1
    Spells:
    5
    Wurst:
    1
    Resources:
    7
    sorry but, is this going to be a talk about nestharus or the resource? xD
     
  20. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,668
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Well you were the one saying why use UnitDex instead of Nestharus' indexer, so I answered.

    Troll-Brain was just agreeing with me.

    But yeah, lets forget about nes.