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

[System] Unit Indexer

Level 6
Joined
Jun 20, 2011
Messages
249
I know that one person didn't have undefend in their map
Guilty, again, sorry for the inconvenience.
Because your indentation is terrible.
I wrote that directly into the post box...
Because if there is no index and no deindex method, it expects that you don't want any units to be indexed for that struct
This makes no sense, of course i want to index units into my struct, thats why i'm adding a filter method, i just don't want to do anything once they're indexed.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
Well, you should edit the main post again.
Firstly, the main post says that its version is 4.0.2.3 whilst the script says 4.0.2.4.
Also, since now you're supporting filters again (gladly), you should edit the AIDS backwards compatibility snippet.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I've already explained why the requirements are annoying in case of short tests, and since Nestharus will never change his mind i made myself the requirements as optional.
For the Event library i've justed literraly copy/paste inside UnitIndexer (and renamed the variable q to avoid conflicts)

JASS:
library UnitIndexer /* v4.0.2.4 This an unofficial version not supported by Nestharus, i have just made requirements optional.

Just in order to make short tests not annoying, in a real map use the official version
*************************************************************************************
*
*   Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
*   */uses/*
*       */ optional WorldBounds /*       hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
*       */ optional Event /*             hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
*   SETTINGS
*/
globals
    constant integer ABILITIES_UNIT_INDEXER = 'Uind' // use the right rawcode
endglobals
/*
************************************************************************************
*
*    Functions
*
*       function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
*       function GetUnitById takes integer index returns unit
*           -   Returns unit given a unit index
*       function GetUnitId takes unit u returns integer
*           -   Returns unit index given a unit
*
*       function IsUnitIndexed takes unit u returns boolean
*
*       function GetIndexedUnitId takes nothing returns integer
*       function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
*   module UnitIndexStruct
*
*       -   A pseudo module interface that runs a set of methods if they exist and provides
*       -   a few fields and operators. Runs on static ifs to minimize code.
*
*       static method operator [] takes unit u returns thistype
*           -   Return GetUnitUserData(u)
*
*       readonly unit unit
*           -   The indexed unit of the struct
*       readonly boolean allocated
*           -   Is unit allocated for the struct
*
*       Interface:
*
*           -   These methods don't have to exist. If they don't exist, the code
*           -   that calls them won't even be in the module.
*
*           private method index takes nothing returns nothing
*               -   called when a unit is indexed and passes the filter.
*               -
*               -   thistype this:              Unit's index
*           private method deindex takes nothing returns nothing
*               -   called when a unit is deindexed and is allocated for struct
*               -
*               -   thistype this:              Unit's index
*           private static method filter takes unit unitToIndex returns boolean
*               -   Determines whether or not to allocate struct for unit
*               -
*               -   unit unitToIndex:           Unit being filtered
*
************************************************************************************
*
*   struct UnitIndexer extends array
*
*        -    Controls the unit indexer system.
*
*       static constant Event UnitIndexer.INDEX
*       static constant Event UnitIndexer.DEINDEX
*           -   Don't register functions and triggers directly to the events. Register them via
*           -   RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
*       static boolean enabled
*           -   Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
*   struct UnitIndex extends array
*
*       -    Constrols specific unit indexes.
*
*       method lock takes nothing returns nothing
*           -   Locks an index. When an index is locked, it will not be recycled
*           -   when the unit is deindexed until all locks are removed. Deindex
*           -   events still fire at the appropriate times, the index just doesn't
*           -   get thrown into the recycler.
*       method unlock takes nothing returns nothing
*           -   Unlocks an index.
*
************************************************************************************/

static if not LIBRARY_Event then
    globals
        private real z=0
    endglobals
    struct Event extends array
        private static integer w=0
        private static trigger array e
        static method create takes nothing returns thistype
            set w=w+1
            set e[w]=CreateTrigger()
            return w
        endmethod
        method registerTrigger takes trigger t returns nothing
            call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"z",EQUAL,this)
        endmethod
        method register takes boolexpr c returns nothing
            call TriggerAddCondition(e[this],c)
        endmethod
        method fire takes nothing returns nothing
            set z=0
            set z=this
            call TriggerEvaluate(e[this])
        endmethod
    endstruct
    function CreateEvent takes nothing returns Event
        return Event.create()
    endfunction
    function TriggerRegisterEvent takes trigger t,Event ev returns nothing
        call ev.registerTrigger(t)
    endfunction
    function RegisterEvent takes boolexpr c,Event ev returns nothing
        call ev.register(c)
    endfunction
    function FireEvent takes Event ev returns nothing
        call ev.fire()
    endfunction
endif

    globals
        private trigger q=CreateTrigger()
        private trigger l=CreateTrigger()
        private unit array e
        private integer r=0
        private integer y=0
        private integer o=0
        private boolean a=false
        private integer array n
        private integer array p
        private integer array lc
    endglobals
    function GetIndexedUnitId takes nothing returns integer
        return o
    endfunction
    function GetIndexedUnit takes nothing returns unit
        return e[o]
    endfunction
    //! runtextmacro optional UNIT_LIST_LIB()
    private struct PreLoader extends array
        public static method run takes nothing returns nothing
            call DestroyTimer(GetExpiredTimer())
            set a=true
        endmethod
        public static method eval takes trigger t returns nothing
            local integer f=n[0]
            local integer d=o
            loop
                exitwhen 0==f
                if (IsTriggerEnabled(t)) then
                    set o=f
                    if (TriggerEvaluate(t)) then
                        call TriggerExecute(t)
                    endif
                else
                    exitwhen true
                endif
                set f=n[f]
            endloop
            set o=d
        endmethod
        public static method evalb takes boolexpr c returns nothing
            local trigger t=CreateTrigger()
            local thistype f=n[0]
            local integer d=o
            call TriggerAddCondition(t,c)
            loop
                exitwhen 0==f
                set o=f
                call TriggerEvaluate(t)
                set f=n[f]
            endloop
            call DestroyTrigger(t)
            set t=null
            set o=d
        endmethod
    endstruct
    //! runtextmacro optional UNIT_EVENT_MACRO()
    private module UnitIndexerInit
        private static method onInit takes nothing returns nothing
            local integer i=15
            local boolexpr bc=Condition(function thistype.onLeave)
            local boolexpr bc2=Condition(function thistype.onEnter)
            local group g=CreateGroup()
            local player p
            static if not LIBRARY_WorldBounds then
                local rect map = GetWorldBounds()
                local region reg = CreateRegion()
                call RegionAddRect(reg,map)
                call TriggerRegisterEnterRegion(q,reg,bc2)
                call RemoveRect(map)
                set reg = null
                set map = null
            else
                call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
            endif
            set INDEX=CreateEvent()
            set DEINDEX=CreateEvent()
            loop
                set p=Player(i)
                call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
                call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
                call GroupEnumUnitsOfPlayer(g,p,bc2)
                exitwhen 0==i
                set i=i-1
            endloop
            call DestroyGroup(g)
            set bc=null
            set g=null
            set bc2=null
            set p=null
            call TimerStart(CreateTimer(),0,false,function PreLoader.run)
        endmethod
    endmodule
    struct UnitIndex extends array
        method lock takes nothing returns nothing
            debug if (null!=e[this]) then
                set lc[this]=lc[this]+1
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
            debug endif
        endmethod
        method unlock takes nothing returns nothing
            debug if (0<lc[this]) then
                set lc[this]=lc[this]-1
                if (0==lc[this] and null==e[this]) then
                    set n[this]=y
                    set y=this
                endif
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
            debug endif
        endmethod
    endstruct
    struct UnitIndexer extends array
        readonly static Event INDEX
        readonly static Event DEINDEX
        static boolean enabled=true
        private static method onEnter takes nothing returns boolean
            local unit Q=GetFilterUnit()
            local integer i
            local integer d=o
            if (enabled and Q!=e[GetUnitUserData(Q)]) then
                if (0==y) then
                    set r=r+1
                    set i=r
                else
                    set i=y
                    set y=n[y]
                endif
                call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
                call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
                call SetUnitUserData(Q,i)
                set e[i]=Q
                static if not LIBRARY_UnitList then
                    if (not a)then
                        set p[i]=p[0]
                        set n[p[0]]=i
                        set n[i]=0
                        set p[0]=i
                    endif
                else
                    set p[i]=p[0]
                    set n[p[0]]=i
                    set n[i]=0
                    set p[0]=i
                    call GroupAddUnit(g,e[i])
                endif
                set o=i
                call FireEvent(INDEX)
                set o=d
            endif
            set Q=null
            return false
        endmethod
        private static method onLeave takes nothing returns boolean
            static if LIBRARY_UnitEvent then
                implement optional UnitEventModule
            else
                local unit u=GetFilterUnit()
                local integer i=GetUnitUserData(u)
                local integer d=o
                if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
                    static if not LIBRARY_UnitList then
                        if (not a)then
                            set n[p[i]]=n[i]
                            set p[n[i]]=p[i]
                        endif
                    else
                        set n[p[i]]=n[i]
                        set p[n[i]]=p[i]
                        call GroupRemoveUnit(g,e[i])
                    endif
                    set o=i
                    call FireEvent(DEINDEX)
                    set o=d
                    if (0==lc[i]) then
                        set n[i]=y
                        set y=i
                    endif
                    set e[i]=null
                endif
                set u=null
            endif
            return false
        endmethod
        implement UnitIndexerInit
    endstruct
    //! runtextmacro optional UNIT_EVENT_MACRO_2()
    function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
        call RegisterEvent(c, ev)
        if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
            call PreLoader.evalb(c)
        endif
    endfunction
    function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
        call TriggerRegisterEvent(t,ev)
        if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
            call PreLoader.eval(t)
        endif
    endfunction
    function GetUnitById takes integer W returns unit
        return e[W]
    endfunction
    function GetUnitId takes unit u returns integer
        return GetUnitUserData(u)
    endfunction
    function IsUnitIndexed takes unit u returns boolean
        return u==e[GetUnitUserData(u)]
    endfunction
    module UnitIndexStruct
        static method operator [] takes unit u returns thistype
            return GetUnitUserData(u)
        endmethod
        method operator unit takes nothing returns unit
            return e[this]
        endmethod
        static if thistype.filter.exists then
            static if thistype.index.exists then
                static if thistype.deindex.exists then
                    readonly boolean allocated
                else
                    method operator allocated takes nothing returns boolean
                        return filter(e[this])
                    endmethod
                endif
            else
                method operator allocated takes nothing returns boolean
                    return filter(e[this])
                endmethod
            endif
        elseif (thistype.index.exists) then
            static if thistype.deindex.exists then
                readonly boolean allocated
            else
                method operator allocated takes nothing returns boolean
                    return this==GetUnitUserData(e[this])
                endmethod
            endif
        else
            method operator allocated takes nothing returns boolean
                return this==GetUnitUserData(e[this])
            endmethod
        endif
        static if thistype.index.exists then
            private static method onIndexEvent takes nothing returns boolean
                static if thistype.filter.exists then
                    if (filter(e[o])) then
                        static if thistype.deindex.exists then
                            set thistype(o).allocated=true
                        endif
                        call thistype(o).index()
                    endif
                else
                    static if thistype.deindex.exists then
                        set thistype(o).allocated=true
                    endif
                    call thistype(o).index()
                endif
                return false
            endmethod
        endif
        static if thistype.deindex.exists then
            private static method onDeindexEvent takes nothing returns boolean
                static if thistype.filter.exists then
                    static if thistype.index.exists then
                        if (thistype(o).allocated) then
                            set thistype(o).allocated=false
                            call thistype(o).deindex()
                        endif
                    else
                        if (filter(e[o])) then
                            call thistype(o).deindex()
                        endif
                    endif
                else
                    static if thistype.index.exists then
                        set thistype(o).allocated=false
                    endif
                    call thistype(o).deindex()
                endif
                return false
            endmethod
        endif
        static if thistype.index.exists then
            static if thistype.deindex.exists then
                private static method onInit takes nothing returns nothing
                    call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
                    call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
                endmethod
            else
                private static method onInit takes nothing returns nothing
                    call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
                endmethod
            endif
        elseif thistype.deindex.exists then
            private static method onInit takes nothing returns nothing
                call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
            endmethod
        endif
    endmodule
endlibrary
 
Level 6
Joined
Jun 20, 2011
Messages
249
If i wanted a struct for mainly attaching data to all units as in
JASS:
struct A extends array
    integer data
    implement UnitIndexStruct
endstruct
So then when i want to set an specific data to an unit i just go A[unit].data=value. Would that work? or the behavior prevents anything to be done if there are no filter, index or deindex methods?
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
I don't like how the preloader handles boolexpr evaluation.

Because of it, a huge map may end up with dozens of triggers being created, evaluated & destroyed at map initialization... which isn't really the nicest thing.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
JASS:
    private struct PreLoader extends array
        
        public static method evalb takes boolexpr c returns nothing
            local trigger t=CreateTrigger()
            local thistype f=n[0]
            local integer d=o
            call TriggerAddCondition(t,c)
            loop
                exitwhen 0==f
                set o=f
                call TriggerEvaluate(t)
                set f=n[f]
            endloop
            call DestroyTrigger(t)
            set t=null
            set o=d
        endmethod
    
    endstruct
That's a part of UnitIndexer, isn't it?
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
Go and benchmark something like something like ten (a complete map would have even more than that, but still...) CreateTrigger() + TriggerAddCondition() + TriggerEvaluate + DestroyTrigger + set trigger = null vs ten ForceEnumPlayersCounted().

Furthermore, you may want to check out this thread (especially the exitwhen thing) and start writing sensible code.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Last time i checked (several years ago) all the Enum...Counted functions were just a wrapper of the other ones without the Counted thing.
I mean the extra integer argument seems to have no effect, aka Not Yet Implanted.
That should be tested again, as i could have made a not valid test.

I suppose you are talking about this :

http://www.hiveworkshop.com/forums/2005605-post9.html

Yes, as already stated the benchmark is far from perfect, but good enough to know that ForForce is the absolute winner in term of efficiency, but seems closed to TriggerEvaluate.
However, maybe with this new Or(), And() trick the results could be different with TriggerEvaluate.
But you have to remember that Or/And() creates a new boolexpr each time it's called, not like Filter/Condition().

Ofc you shouldn't use ForceEnumPlayers for code executing since it will run (and open a new thread) for each player enumed, i did it just to test all the ways (since it could still be used in a solo map, in theory ofc, only in theory, plz don't use it at all...)

And btw even in the current state, i don't get why you just don't use ClearTriggerConditions and a global already created trigger instead.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Troll-Brain, if I came to you and told you that 1+1 != 2 then how should you react? Would you test it or would you require me to be the one to present evidence?

We are not talking about math evidence but jass ...

It's obvious that some native jass functions are just not working correctly, i mean not finished, think about the functions AbilityId...
Yes there is a comment but if there is none that doesn't mean the function is working 100%, especially if GUI doesn't use them at all.

I'm not the first random guy, if i say if i remember something lame it should be taken in consideration.
But ofc i can make errors, and because i've not tested again i just say it should be tested.

And basically you say it works, so i assumed you have used it sucesfully, but now it's obvious that you haver never used it at all ...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
Troll-Brain, no I have not tested this in multiplayer you have me there, for the time being I will not promote the technique, and maybe you are not the "first random guy" but I have not heard this from anyone besides you up until now.

All things said I am a scientist and I accept opposing ideas but as you are the one who presented the opposing idea you are the one who should be responsible to back up your claims.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
But still you shouldn't have say as the truth, coz of what i've said before you.

I can't test since i can't host.

Also, well i was the first which has say that UnitId2String bug, and probably mentionned other bugs, your argument is pointless, in fact i've never seen a ...Enum...Counted function in an used script.

Actually i don't get how i've made a mistake, i mean i didn't say that was true, just possible and should be tested.

But i can test the GroupEnum...Counted things, and i will later and edit this post.

EDIT : Test done with GroupEnumUnitsInRangeCounted

JASS:
scope Test initializer init

    private function init takes nothing returns nothing
        local group g = CreateGroup()
        local unit u
        
        call CreateUnit(Player(0),'hfoo',0,0,0)
        call CreateUnit(Player(0),'hfoo',0,0,0)
        call GroupEnumUnitsInRangeCounted(g,0,0,666,null,1)
        
        loop
        set u = FirstOfGroup(g)
        exitwhen u == null
            call BJDebugMsg(I2S(GetHandleId(u)))
        call GroupRemoveUnit(g,u)
        endloop
          
    endfunction

endscope

It displays the two unit handle id when it is supposed to display only one.
I'm not going to test all other functions, for now i will just assume they are all NYI, until there is someone which test the others and say it works (and share the test code).
 
Last edited:
Level 6
Joined
Oct 23, 2011
Messages
182
JASS:
    private module M
        private static method onInit takes nothing returns nothing
            set .ac = Condition(function OnDamageActions)
        endmethod
    endmodule

    private struct Detector extends array
        implement M
    
        private trigger t
        
        static conditionfunc ac
        
        private method index takes nothing returns nothing
            set this.t=CreateTrigger()
            call TriggerAddCondition(.t,.ac)
            call TriggerRegisterUnitEvent(.t,unit,EVENT_UNIT_DAMAGED)
        endmethod
        
        private method deindex takes nothing returns nothing
            call DestroyTrigger(.t)
        endmethod
        
        implement UnitIndexStruct
    endstruct

I had to do this for it to work with j4l's Damage
would i have to use module initializer like that everytime? kinda annoying

Also
does changing the defend effect on UnitIndexing ability matter?
 
Condition() returns the same handle each time (after the first creation) instead of creating a new handle. (unless it was destroyed) In terms of efficiency, it doesn't make much of a difference, although the first method is probably slightly faster. (however, it is an unnoticeable speed difference so it doesn't really matter)
 

Fud

Fud

Level 3
Joined
Aug 12, 2011
Messages
42
I find it quite funny that everyone is criticizing UnitIndex basically saying "AIDS would be just as good if they only improved it in all these different ways." Well until that day (which as Nestharus suggests is unlikely to ever come) this is far better...

Please clarify what installation entails. It says I code reopen delete? which section do i delete? What is the AIDS code that is posted first on the list of triggers? And finally, The only code that is on the map is the Systems so where do i download the code that I am supposed to delete at some point? I am very new to triggers and my knowledge is very limited...

Basically i want a step by step system to just install this shit...
 
Top