[System] Unit Indexer

Level 30
Joined
Jul 10, 2007
Messages
6,307
I don't see why I would ever choose this over AIDS. Sure, it's simpler, but it's missing that struct stuff that AIDS provides that is so very helpful.

I will add struct stuff that will be better than AIDS as it will be in a module + use static ifs.

deal? : P

edit
the module is up for structs and I must admit it turned out very good : D. The struct module on this is the best of all of the indexing systems. It uses major static ifs to keep code to a bare minimal and make everything as optimal as possible.

edit
Made a very useful object creator script, but importing the constants for the object ids is a problem that you know about Azlier because the constants are generated JASS.

This is the script-
http://www.hiveworkshop.com/forums/triggers-scripts-269/lua-any-ideas-making-more-plausible-172582/

If there are fixes to JASS, I can update this to use that script as it'll be much more stable with the object ids (0 chances for collision).
 
Last edited:
Level 8
Joined
Oct 3, 2008
Messages
367
This really doesn't offer anything more than AIDS does, honestly. There are enough wars between AutoIndex and AIDS already, and a possible efficiency tweak and some sort of access to units I've never needed isn't enough reason to throw a third system into the fray.

And why are functions to index individual units needed... at all? I can't fathom a scenario in which they'd be useful.

And this doesn't support AIDS or AutoIndex API, the most widely used indexing systems out there. Instead, you're adding support for PUI which was deprecated long ago. That is a stupid idea. Of course, I doubt you would ever add AIDS API. That would be incredibly difficult.

I can't see a reason why we would need this approved here, especially since it seems to offer nothing useful that the other two systems don't.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Actually, I could add an AIDS and AutoIndex API (which I had in here before), but eh. Use whatever you guys want to use.

It's impossible for a standalone library to ever be as good as AutoEvent. I will personally only ever code for this library because of the crazy cumbersome style of AutoEvent/AutoIndex and because of my own personal issues with AIDS and the fact that a standalone event library will never be as good as AutoEvent or Unit Event (talking about AIDS here).

And why are functions to index individual units needed... at all? I can't fathom a scenario in which they'd be useful.

They are there for ini dummy units so that you don't have to add a unit type boolean to the global filter for every single system with a dummy unit that you install. I can only think of one use for the global filter, otherwise I think that the enable/disable is a better choice.

I can't see a reason why we would need this approved here, especially since it seems to offer nothing useful that the other two systems don't.

When it comes to functionality, you are spot on = ). However, I like to think that this has the pros of both and the cons of neither.

It's up to you guys as a community as to whether you want to use it or not, but I know when it comes to me, this is my #1 choice = ).

I don't see any way that the design between the two libraries can be improved. I believe that AIDS is never going to be coupled with an event library (jesus4lyf had said that before), and I don't believe that AutoIndex or AutoEvent will ever be improved with some of the techniques I came up with (dummy unit killed at start for decay, etc).

To me, using AutoIndex is out of the question because of the RemoveUnit hook. Using AIDS whenever you want unit events is out of the question because of the fact that a standalone unit event library (this is the third time now, hope I'm making an impression) will never compare with a unit event library coupled with a unit indexing system.


Again, totally up to you guys = ). I'm just giving you the figures so that you can make a logical decision ^.^.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Well, wouldn't the logical solution be to produce a unit event library for AIDS? If it's possible to do that, then it seems that everybody wins. Except for AutoIndex, of course.

It's too bad that jesus4lyf's plan is for a standalone event system ^.^.

Also, how long do you think it will take him to update AIDS eh?

And you skipped my argument for enable/disable vs global where I pretty much state that global is nearly useless, not enable/disable : ).

Just giving you what I've come to understand and why I've always said that I believe this+Unit Event has the pros of AutoIndex/AutoEvent and AIDS with the cons of neither and that that's likely never to change.

Again, totally up to you. I hope you can start to see why I was getting so frustrated with TH (that plus the fact that I hadn't slept that week). I pretty much just gave up at wc3c and said w/e. They love cumbersome stuff ; P.

At this point I really don't care, to each their own. I'll update this code with w/e people want as quickly as possible (and you should know by this point how fast I typically am with updates), but that's about all I can do.
 
Level 6
Joined
Jun 6, 2010
Messages
224
I don't see why I would ever choose this over AIDS. Sure, it's simpler, but it's missing that struct stuff that AIDS provides that is so very helpful.

AIDS provides nothing but only redundant stuff

Unit Indexer means only one thing

INDEXING OF UNITS

so in case i wanna get a unique ID of a unit i'll use an indexer.
Everything else is unecessary and redundant.

But let's keep up with the review of this.

Despite my opinion on vJass (bad performance, lot of garbage in map script, and lol)

this is quite useful and good
You see, simplicity is underrated these days.
If we keep making things that are huge and huge and rich in content, what options do we give to a user?

I specificly am an AOS user. I want simple things that run fast. And this is one of it. But unfortunately i've implemented every single system that has been posted on hive in simple JASS so i'll just throw my rating out there and propably some rep for the good work

Rating - 4.5/5.0 [You could make it even more simple but you didn't -.-]
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
uh...
AIDS provides nothing but only redundant stuff

Unit Indexer means only one thing

AIDS does indexing + locks ... : |

Despite my opinion on vJass (bad performance, lot of garbage in map script, and lol)

How does this even relate to this system considering it uses extends array?? ....

You see, simplicity is underrated these days.

Simplicity is not underrated these days at all. Modularity and simplicity is the way of the world... : |...

I specificly am an AOS user

What is AOS???

But unfortunately i've implemented every single system that has been posted on hive in simple JASS so i'll just throw my rating out there and propably some rep for the good work

Again, if you use struct that extends array, vjass doesn't do anything extra...

Rating - 4.5/5.0 [You could make it even more simple but you didn't -.-]

I'm curious as to how I could make it more simple. Rather than saying this sucks or this could be more simple or w/e, you should directly state what could be more simple and how it can be more simple. For example
set x = x + 1 + 1 + 1 can just be set x = x + 3 rather than saying "that line sux0rz."



Thank you for the review : )
 
Level 8
Joined
Oct 3, 2008
Messages
367
Putting this to use, it seems to handle about as well as AIDS, but the API feels rather clunky and somewhat unintuitive compared to that of AIDS.

And I would really appreciate a benchmark of this vs. AIDS, if possible. No need for a benchmark against AutoIndex, since AIDS is already faster.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Putting this to use, it seems to handle about as well as AIDS, but the API feels rather clunky and somewhat unintuitive compared to that of AIDS.

And I would really appreciate a benchmark of this vs. AIDS, if possible. No need for a benchmark against AutoIndex, since AIDS is already faster.

can't run a benchmark since I'm unable to run RtC ;\. One day I just couldn't run it anymore (don't ask me why, no clue).

If you want benchmark answers, I can tell you though. Periodic recycler, for example, is the fastest way to deindex units. However, it cannot retrieve a unit before it goes out of scope.

You need to understand that the methodology with AIDS and UnitIndexer is very similar, so knowing the outcomes of the code is easy if you compare the operations. For example, the undefend in AIDS adds the undefending unit to a stack and runs a timer. From here, the timer iterates through the undefend stack and sees which units are null. The units that are null get their deindex triggers evaluated. UnitIndexer doesn't have the timer or the looping >.<.

It's like jesus4lyf stated, the periodic recycler in AIDS is the best way to deindex units when it comes to speed. His primary argument against UnitIndexer was that the only real fundamental difference was the more optimal deindexing method that allowed units to be retrieved before they went out of scope. Well, that and the module anyways.

The reason UnitIndexer was graveyarded at TH was that it was regarded as not original enough to merit its own submission and that the fixes in it should have been posted on the AIDS thread. I obviously made this system for the hell of it, and its end result ended up being a bit better than AIDS, but anyways...


So take this into consideration because these are the things you should be focusing on when making your decision.

1. The fundamental difference between UnitIndexer and AIDS is the deindexing function and the module. the ones in UnitIndexer are superior and allow for a bit of increased functionality, but the code difference itself was regarded as too small to be original (even though I didn't copy AIDS in any way, it just ended up being like that).

2. UnitIndexer and AIDS follow different philosophies when dealing with unit events. UnitIndexer couples the UnitEvent library with itself to dramatically increase efficiency. The philosophy of AIDS is to support a standalone library that is not in any way coupled at a major efficiency loss. jesus4lyf hasn't done add-ons in the past, and it's pretty safe to assume, also given his statements about it, that he won't do add-ons in the future. If you look at AutoIndex, the reason it was coupled with AutoEvent was because whoever the author of that system was (I forgot o-o) came to the same conclusion I did.

3. jesus4lyf has yet to update AIDS and it has been a long time... remember that 7 day thing I talked about? That might be a good suggestion to follow. If an author is unwilling to make fixes or takes an inordinate amount of time to do it, how do you respond?


You represent your community ; P. Which one do you think people will like more and which one will work better in maps? If you think this one has a terrible API compared to AIDS, then let me know so I can fix it.

The above has been what all of these debates has boiled down to. wc3c rejected it because of originality: AutoIndex did the same thing, didn't matter which one did it better. Keep in mind all of the scripts that use AIDS and AutoIndex and the fact that many of these scripts use features within the 2 systems that are entirely unique to them: Locks in AIDS and the weirded out lists in AutoIndex. This means that UnitIndexer, lacking both, can support neither of them.

All three systems follow different philosophies as well : \. For example, the locks in AIDS comes from the idea that a handle id isn't recycled until it is no longer in use. The linked lists in AutoIndex probably come from the idea to be able to associate any number of indexed units with a given struct instance. UnitIndexer just follows minimize the features to maximize the speed and remove all unused code (static ifs in the modules and the lack of locks and so on).

I don't care which one you pick. I submitted this to help people out and try to give people what I find to be a better solution for unit indexing and unit events. What philosophies does the THW community follow? What do they want? You are the moderator, so you decide. When it comes to the code itself, there is nothing wrong with it.

I hope this post was useful in helping you make your decision.
 
Last edited:
Level 30
Joined
Jul 10, 2007
Messages
6,307
3.0.1.1

Made a very tiny update (just added 2 lines of code).

forceful unit index
call UnitMakeAbilityPermanent(u, true, 'OUIN')

unit indexing on creation
call UnitMakeAbilityPermanent(filterUnit, true, 'OUIN')

This is so that the system doesn't bug when the unit morphs = ).

I originally thought that that native wouldn't do anything of benefit to Unit Indexer. I decided to do a little more research on the native to see precisely what it did. After learning that it prevented abilities from being dropped on morph, I added the 2 lines into the system : P.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
I would really love to have the AutoDestroy module in this system too. (Destroys the struct when a unit assigned to the struct leaves the game)

First I'd like to thank you for showing interest in UnitIndexer = ).

private method unitDeindex takes nothing returns nothing??


This does not assign multiple units per struct. Each struct's instance is the same as the unit's instance. This means there is no allocate or deallocate for a struct. If a unit is allocated and assigned the value of 81, all structs that use that unit will be assigned the value of 81. Only the unit is allocated/deallocated.

I hope this makes sense ; ).


In other words, if a unit is created and passes the struct's filter, the struct's unitIndex method is called. When a unit is deindexed, if the struct was created for that unit, it's unitDeindex method is called. There is no create/destroy for structs that implement the module, there is only onUnitIndex and onUnitDeindex, named unitIndex and unitDeindex for short : ).

It can be best understood when looking at the demo ; )
JASS:
struct UnitIndexerDemo extends array
    //gets called when the struct is created, which is whenever the filter returns true and a unit is indexed
    //because of the filter in this struct, will not be called on knights
    private method unitIndex takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(unit) + " was indexed")
    endmethod
    
    //gets called if the struct is created and the unit is deindexed
    //because of the filter on this struct, will not be called on knights
    private method unitDeindex takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(unit) + " was deindexed")
    endmethod
    
    //the filter. This filter essentially says, create this struct for all units excluding knights.
    private static method unitFilter takes unit u returns boolean
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(u) + " was filtered")
        
        if (GetUnitTypeId(u) == 'hkni') then
            return false
        endif
        return true
    endmethod
    
    implement UnitIndexStruct
endstruct

If you think that it calls the filter for deindex checking, you'd be wrong. It stores whether a struct is indexed or not into a boolean ^>^. What if you don't use filter checking, would it still do that boolean check? Nop. It minimizes the code, so using the module is 100% guaranteed to give you the best possible code... well, if you don't use the filter it will still add a global, a constant static boolean essentially saying that all units pass the filter.
public static constant boolean unitIndexAllocated = true
 
Last edited:
Level 11
Joined
Sep 30, 2009
Messages
697
Thats not what I actually meant :/ I thought about AutoDestroy module AutoIndex has which can be implemented in any struct without the other things
JASS:
    struct Example
        static method create takes unit u returns Example
            local Example this = allocate()
                set me = u //You should assign a unit to "me",
            return this    //otherwise AutoDestroy does nothing.
        endmethod          //Not necessary if using AutoCreate.
        private method onDestroy takes nothing returns nothing
            call BJDebugMsg(GetUnitName(me)+" left the game!")
        endmethod
        implement AutoDestroy
    endstruct
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
..... just use unitDeindex ...... it can be implemented without the other things...

JASS:
struct UnitIndexerDemo extends array
    //gets called when the unit is deindexed (struct is always created in absence of filter)
    private method unitDeindex takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(unit) + " was deindexed")
    endmethod
    
    implement UnitIndexStruct
endstruct

And you could easily add a filter like
JASS:
struct UnitIndexerDemo extends array
    private boolean isAllocated
    
    public static method allocate takes unit u returns thistype
        set isAllocated[GetUnitId(u)] = true
        return GetUnitId(u)
    endmethod
    
    private static method unitFilter takes unit u returns boolean
        return isAllocated[GetUnitId(u)]
    endmethod
    
    //gets called when the unit is deindexed (struct is always created in absence of filter)
    private method unitDeindex takes nothing returns nothing
        set isAllocated = false
    endmethod
    
    implement UnitIndexStruct
endstruct

edit
3.1.0.2
found another bug. One line change = ).

Here is your basic format for an ability that can be detached whenever.
JASS:
library UnitListStruct uses UnitIndexer
    module UnitListStruct
        private thistype nextX
        private thistype previousX
        private thistype head
        private thistype uhead
        private thistype ihead
        
        private static integer instanceCount = 0
        private static integer array recycle
        private static integer recycleCount = 0
        
        public method operator next takes nothing returns thistype
            return nextX
        endmethod
        
        public method operator previous takes nothing returns thistype
            return previousX
        endmethod
        
        public method list takes nothing returns thistype
            return ihead
        endmethod
        
        public method operator unitId takes nothing returns thistype
            return head.uhead
        endmethod
        
        public method deallocate takes nothing returns nothing
            if (head != this) then
                set nextX.previousX = previousX
                set previousX.nextX = nextX
                
                set recycle[recycleCount] = this
                set recycleCount = recycleCount + 1
            endif
        endmethod
        
        public static method allocate takes unit u returns thistype
            local thistype this = thistype(GetUnitId(u)).uhead
            local thistype g
            
            if (recycleCount != 0) then
                set recycleCount = recycleCount - 1
                set g = recycle[recycleCount]
            else
                set instanceCount = instanceCount + 1
                set g = instanceCount
            endif
            
            //add to list
            set previousX.nextX = g
            set g.nextX = this
            set g.previousX = previousX
            set previousX = g
            set g.head = this
            set g.ihead = this
            
            return g
        endmethod
        
        private method onAllocate takes nothing returns boolean
            local thistype this = GetIndexedUnitId()
            if (recycleCount != 0) then
                set recycleCount = recycleCount - 1
                set uhead = recycle[recycleCount]
            else
                set instanceCount = instanceCount + 1
                set uhead = instanceCount
            endif
            set uhead.ihead = this
            set uhead.head = uhead
            set ihead.ihead = ihead
            
            return false
        endmethod
        
        private static method onDeallocate takes nothing returns boolean
            local thistype this = GetIndexedUnitId()
            set ihead.uhead = 0
            
            loop
                set this = nextX
                set recycle[recycleCount] = this
                set recycleCount = recycleCount + 1
                
                exitwhen this == head
                
                static if thistype.unitNodeDestoy.exists then
                    call unitNodeDestoy()
                endif
            endloop
            
            set head = 0
            set ihead = 0
            set nextX = 0
            set previousX = 0
            
            return false
        endmethod
        
        public method destroyList takes nothing returns nothing
            local thistype this = GetEventUnitId()
            set ihead.uhead = 0
            
            loop
                exitwhen this == head
                set this = nextX
                set recycle[recycleCount] = this
                set recycleCount = recycleCount + 1
                
                static if thistype.unitNodeDestoy.exists then
                    call unitNodeDestoy()
                endif
            endloop
        endmethod
        
        private static method onInit takes nothing returns nothing
            call OnUnitIndex(Condition(function thistype.onAllocate))
            call OnUnitDeindex(Condition(function thistype.onDeallocate))
        endmethod
    endmodule
endlibrary

You know what... this is making me want to make a few modules for UnitIndexer =O.
 
Last edited:
Level 8
Joined
Oct 3, 2008
Messages
367
I'm going to approve this. While it may be very similar to AIDS, it has Unit Event to go alongside it. Something that's apparently not possible with AIDS without a significant efficiency loss or something.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
AIDS without a significant efficiency loss or something.

Well it's possible if jesus4lyf did it as an add on: rewrote some of AIDS to be coupled with an event library. We both know that jesus4lyf wouldn't do that even if someone was holding a gun to his head. He'd create a separate event library that ran alongside AIDS, which is where the major efficiency loss comes from (almost double operations >.<, including extra trigger evaluations).

/happy, my nights without sleeping working on this paid off ^>^.
 
Level 34
Joined
Sep 26, 2009
Messages
8,435
indexedArray[indexed]

You use that instead of a single global in case a unit is indexed or deindexed from within the callback thread, right? What if you used TriggerExecute instead of TriggerEvaluate... will it still have the same recursion?

If it doesn't have the recursion, it'd be certainly faster to use TriggerExecute with a single global instead of TriggerEvaluate with an array.

Also, the initialisation is a weird case with UnitIndexer. Why do you put your initialiser into a module instead of a simple library initialiser? To me it looks like it can bug if a unit is created between the UnitIndexer's initialisation and the initialisation of UnitIndexStruct, because it will trigger the "unit enters region" before registering the OnUnitIndex events...
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Actually, both should be in the module initializer ;o. The reason for this is because a unit's custom data might be accessed before the game starts and before library initializers run. This means that all units need to immediately have an indexed id : 0.

Now, considering units in the map are not even placed until after library initializers, an enum isn't even actually needed (I'll recheck the order of initialization, but I think libs go before all standard map initialization, and units placed on map via WE are part of standard map ini).

If this is the case, the enum in the library initialization is stupid : D.
 
Level 34
Joined
Sep 26, 2009
Messages
8,435
Library runs before game-placed units, but units that are created manually from a module or library... will trigger the events before OnUnitIndex or OnUnitDeindex get called. And you have no backup in the case something like that happened, unlike AIDS and AutoIndex.

I found a solution for recursion prevention in your indexing method without using arrays, which are obviously slow.

Instead of set index = index + 1, store the current value of "index" in a local variable, then instead of index = index - 1, re-set "index" to the local variable, and this will achieve the same effect. Instead of indexedArray[indexed] you just need to reference "indexed". I edited the code at home this morning to show you what I mean, but I didn't have time to upload it.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Er... arrays are the same speed as reg vars unless you know something I don't... >.>.

However, I see your technique. You don't need to upload the code ; P. That's quite ingenious of you Bribe ; ). Just so I don't forget, imma write a quick little snippet here (I happen to be tired to the extreme atm from this nightmare week of work).

JASS:
integer eventIndex = 0 //readonly event variable, GetIndexedUnitId()

function onIndex
    local integer indexed //store previous indexed unit id

        //event code
        set indexed = eventIndex //set to previous indexed unit id
        set eventIndex = this //update indexed unit id
        call TriggerEvaluate(indexEvent) //eval trigger
        set eventIndex = indexed //set indexed unit id back to what it previous was
endfunction

Again tx Bribe ; )

->Library runs before game-placed units, but units that are created manually from a module or library

You make a point there... so I guess the enumeration is needed in this case.

So I guess my current setup with the lib and module was actually good. The reason for this is that some modules/struct initializers may specifically use UnitIndexer. If I were to just have a library initializer, I'd break all structs/modules that used UnitIndexer -.-.

As you pointed out, if I simply use the module initializer in the lib, then some random module in another lib that doesn't use UnitIndexer may have units that don't get indexed ;o.

No work around really. But don't worry, the current trigger/enum set up works perfectly :0. Things aren't indexed twice due to the if statement in there to make sure only things that aren't indexed get run ; P.
 
Level 34
Joined
Sep 26, 2009
Messages
8,435
You shouldn't put UnitIndexer's initialiser into a module. The reason why is because you don't need to index any of those units in a hurry, as long as it's done before the game itself actually starts. In fact, a scope initialiser could even be used, because those are even lower priority than library initialisers. Basically, the key priority is to make sure that OnUnitIndex/OnUnitDeindex are called faster than anything else, because those are the most important. It is better to have these run first, because there is no urgency to set up the enter/exit triggers yet because you already enum all players' units through the library initialiser, which will always run after every struct/module initialiser has run, which also should not happen until OnUnitIndex/Deindex are called, anyway.

Also, arrays are way slower than normal globals. I made a script use delegates before (which add +1 additional array search) to a fast timer, and there was a very high drop in framerate! (compared with using it with a normal, extended struct)
 
Last edited:
Level 30
Joined
Jul 10, 2007
Messages
6,307
You really don't get it ; )

...

JASS:
module MyRandomMod

private real x

private static method onInit takes nothing returns nothing

local thistype this = GetUnitId(CreateUnit(...))
set x = 50 //BOOM ERROR

endmethod

endmodule

Now I'm unsure of how the events should be ordered o-o... the reason is that modules might create units that get indexed and libs might have onIndex events, but the onIndex events never run for early placed units.

Ahhhh
 
Level 34
Joined
Sep 26, 2009
Messages
8,435
Now I get it ;)

No one should be calling GetUnitId(CreateUnit(...)) from a module initialiser anyway. That's an idiotic approach.

Some problems are unavoidable and the solution is to tell your user-base not to do that in their modules. The number of people who hurt from that (probably no one) is insignificant to the number of people you can help by addressing the issue I brought up, instead.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
I'd prefer to solve the bug than put a rubber stamp on it and say, "Bug Approved"

: P

I'll continue to think about it, but this bug means that currently all unit indexing systems (not just mine) are unstable : O.


Also, we run into another issue. If I decide to iterate through a stack and run all of the indexed unit's triggers at game start, then that means those triggers won't be able to run until game start.

The point I'm making is...

JASS:
call OnUnitIndex(createObject)
call CreateUnit(....)
set unitCreatedObject.addEvent(myFunc)

Given that a trigger won't be made for that object, it'll obviously fail : |.

What's the solution??? I really don't know : D

If I follow some of your suggestions, it'll make it a hassle for me to use UnitIndexer >.<. If I follow my suggestions, I'm again faced with a hassle brought on by myself.

What to do, what to do.
 
Level 34
Joined
Sep 26, 2009
Messages
8,435
Easy way to solve:

JASS:
globals
    private trigger runT = CreateTrigger( )
    private conditionfunc runC
endglobals

function OnUnitIndex takes boolexpr b returns nothing
    local integer i = instanceCount
    if (i > 0) then
        set runC = TriggerAddCondition(runT, b)
        loop
            set eventIndex = i
            call TriggerEvaluate(runT)
            set i = i - 1
            exitwhen i == 0
        endloop
        call TriggerRemoveCondition(runT, runC)
    endif
    call TriggerAddCondition(onIndex, b)
endfunction

With this method, you could even call it after game start :)
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
yup

JASS:
//units
private integer array units
private integer count = 0
private boolean started = false

private trigger evalTrig
private trigger eventTrig
private integer indexed

private function update takes boolexpr c returns nothing
    local triggercondition tc = TriggerAddCondition(evalTrig, c)
    local integer i = count
    loop
        exitwhen i == 0
        set i = i - 1
        set indexed = units[i]
        call TriggerEvaluate(evalTrig)
    endloop
    call TriggerRemoveCondition(evalTrig, tc)
    set tc = null
endfunction

function OnUnitIndex takes boolexpr c returns nothing
    if (count > 0) then
        call update(c)
    endif
    call TriggerAddCondition(eventTrig, c)
endfunction

function OnGameStart takes nothing returns nothing
    call DestroyTrigger(evalTrig)
    set evalTrig = null
    set count = 0
endfunction

Keep in mind that the iteration over previously placed units will only occur before game start as all units placed at that time are done so at the same instant.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Updated to v3.0.2.0 with many changes (also found 1 bug)

1. All initialization is now done in the module initializer
2. Recursive now achieved through locals by Bribe's suggestion : )
3. OnUnitIndex will not go over all previously placed units when it is called before game start by Bribe's suggestion
4. PlayerManager and WorldBounds are now used

Tested both recursive indexing/deindexing and regular indexing/deindexing and everything seems to work : ).


v3.0.2.2
Fought module bug >.>
 
Last edited:
Level 34
Joined
Sep 26, 2009
Messages
8,435
Personally, I don't think PlayerManager is worth it to speed up the initialisation (in fact, it may be slower because p.end should be plenty slower as an array than i == 0). Initialisation is the least important for speed. WorldBounds serves a logical purpose, but I think, in the end, the fewer libraries a single system requires, the easier it is to implement it.
 
Level 30
Joined
Jul 10, 2007
Messages
6,307
Bribe, the PlayerManager loop is the fastest player loop possible.

You forget that it's array sets/reads instead of performing math (p = p.next, vs p = p + 1) >.>. You also failed to notice p.get instead of Player(p). So no, changing it back would make it slower ;P. Also player loops via PlayerManager are easier to write : ). Also, the exitwhen is a straight out boolean instead of a boolean expression.

I think 99% of wc3 maps should be using PlayerManager, so I'll keep it like it is right now both out of laziness, for increased speed (however inconsequential ^_^), and because it's not worth taking it since just about any map that will use this will have PlayerManager in it anyways >.>. I've never made a map w/o PlayerManager, lol.
 
Top