• 🏆 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!

GUI Unit Indexer 1.4.0.0

Why Unit Indexing is Important


For those not familiar with unit indexers such as UnitIndexingUtils, AutoIndex,
Perfect Unit Indexing (PUI), Advanced Indexing and Data Storage (AIDS) or
Unit Indexer, this can give you a bit of a crash-course.

You definitely want to make your spell MUI. To do that, you either use a hashtable or use some
complex form of indexing. It is very good to choose hashtable because you can load data
from a unit instead of from a list (ie. making it very easy to retrieve an index on death).

Using a hashtable has a bit of a learning curve. There are two keys - a child key, a
parent key and, finally, the actual data being stored. Saving 100 to the casting
unit looks like this:

  • Hashtable - Save 100 as (Key(Damage)) of (Key(Triggering unit)) in (Last created hashtable)
Using a unit indexer, you can super-simplify it to just this:

  • Set Damage[(Custom value of (Triggering unit))] = 100
Instead of using a hashtable with multiple keys, this simply uses an array with the
custom value of the unit as the array index.

Have you ever used unit custom value before? If so, you may be familiar with this method:

  • Unit - Set custom value of (Triggering unit) to 100
That is definitely the easiest way to attach 100 to the unit, but you really limit yourself
by doing that. What if you wanted to remember other things, such as the target unit and the
target location?

Using a Unit Indexer, it's like this:

  • Set Key = (Custom value of (Triggering unit))
  • Set Damage[Key] = 100
  • Set Target[Key] = (Target unit of ability being cast)
  • Set TargetPoint[Key] = (Target point of ability being cast)
  • Unit Group - Add (Triggering unit) to SpellGroup
Recalling this data whenever you want becomes truly easy and even easy to read.

  • Timed Spell
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in SpellGroup and do (Actions)
        • Loop - Actions
          • Set Key = (Custom value of (Picked unit))
          • Unit - Cause (Picked unit) to damage Target[Key] for Damage[Key] of attack type Chaos and damage type normal
The possiblities are literally endless. Indexing this way is pretty fun as well, even helping
to reduce total in-game hashtable count (the limit is 256).


Requiring no NewGen, JassHelper or even knowledge of JASS, this is the easiest unit indexer
to install into a map and start using to its fullest right away.
  1. Make sure "automatically create variables" is checked under "File -> Preferences"
  2. Copy and paste the following trigger (from the test map)
  3. It's installed!

  • Unit Indexer
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call ExecuteFunc("InitializeUnitIndexer")
      • Custom script: endfunction
      • -------- --------
      • -------- This is the core function - it provides an index all existing units and for units as they enter the map --------
      • -------- --------
      • Custom script: function IndexUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • Custom script: local integer ndex
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • IsUnitPreplaced[0] Equal to False
        • Then - Actions
          • -------- --------
          • -------- Check for removed units for every (32) new units created --------
          • -------- --------
          • Set UDexWasted = (UDexWasted + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexWasted Equal to 32
            • Then - Actions
              • Set UDexWasted = 0
              • Set UDex = UDexNext[0]
              • Custom script: loop
              • Custom script: exitwhen udg_UDex == 0
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Custom value of UDexUnits[UDex]) Equal to 0
                • Then - Actions
                  • -------- --------
                  • -------- Remove index from linked list --------
                  • -------- --------
                  • Custom script: set ndex = udg_UDexNext[udg_UDex]
                  • Custom script: set udg_UDexNext[udg_UDexPrev[udg_UDex]] = ndex
                  • Custom script: set udg_UDexPrev[ndex] = udg_UDexPrev[udg_UDex]
                  • Set UDexPrev[UDex] = 0
                  • Set IsUnitPreplaced[UDex] = False
                  • -------- --------
                  • -------- Fire deindex event for UDex --------
                  • -------- --------
                  • Set UnitIndexEvent = 2.00
                  • Set UnitIndexEvent = 0.00
                  • -------- --------
                  • -------- Recycle the index for later use --------
                  • -------- --------
                  • Set UDexUnits[UDex] = No unit
                  • Set UDexNext[UDex] = UDexRecycle
                  • Set UDexRecycle = UDex
                  • Custom script: set udg_UDex = ndex
                • Else - Actions
                  • Set UDex = UDexNext[UDex]
              • Custom script: endloop
            • Else - Actions
        • Else - Actions
      • -------- --------
      • -------- You can use the boolean UnitIndexerEnabled to protect some of your undesirable units from being indexed --------
      • -------- - Example: --------
      • -------- -- Set UnitIndexerEnabled = False --------
      • -------- -- Unit - Create 1 Dummy for (Triggering player) at TempLoc facing 0.00 degrees --------
      • -------- -- Set UnitIndexerEnabled = True --------
      • -------- --------
      • -------- You can also customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UnitIndexerEnabled Equal to True
          • (Custom value of (Matching unit)) Equal to 0
        • Then - Actions
          • -------- --------
          • -------- Generate a unique integer index for this unit --------
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexRecycle Equal to 0
            • Then - Actions
              • Set UDex = (UDexGen + 1)
              • Set UDexGen = UDex
            • Else - Actions
              • Set UDex = UDexRecycle
              • Set UDexRecycle = UDexNext[UDex]
          • -------- --------
          • -------- Link index to unit, unit to index --------
          • -------- --------
          • Set UDexUnits[UDex] = (Matching unit)
          • Unit - Set the custom value of UDexUnits[UDex] to UDex
          • Set IsUnitPreplaced[UDex] = IsUnitPreplaced[0]
          • -------- --------
          • -------- Use a doubly-linked list to store all active indexes --------
          • -------- --------
          • Set UDexPrev[UDexNext[0]] = UDex
          • Set UDexNext[UDex] = UDexNext[0]
          • Set UDexNext[0] = UDex
          • -------- --------
          • -------- Fire index event for UDex --------
          • -------- --------
          • Set UnitIndexEvent = 0.00
          • Set UnitIndexEvent = 1.00
          • Set UnitIndexEvent = 0.00
        • Else - Actions
      • Custom script: set udg_UDex = pdex
      • Custom script: return false
      • Custom script: endfunction
      • -------- --------
      • -------- The next function initializes the core of the system --------
      • -------- --------
      • Custom script: function InitializeUnitIndexer takes nothing returns nothing
      • Custom script: local integer i = 0
      • Custom script: local region re = CreateRegion()
      • Custom script: local rect r = GetWorldBounds()
      • Custom script: local boolexpr b = Filter(function IndexUnit)
      • Set UnitIndexEvent = -1.00
      • Set UnitIndexerEnabled = True
      • Set IsUnitPreplaced[0] = True
      • Custom script: call RegionAddRect(re, r)
      • Custom script: call TriggerRegisterEnterRegion(CreateTrigger(), re, b)
      • Custom script: call RemoveRect(r)
      • Custom script: set re = null
      • Custom script: set r = null
      • Custom script: loop
      • Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
      • Custom script: set i = i + 1
      • Custom script: exitwhen i == bj_MAX_PLAYER_SLOTS
      • Custom script: endloop
      • Custom script: set b = null
      • -------- --------
      • -------- This is the "Unit Indexer Initialized" event, use it instead of "Map Initialization" for best results --------
      • -------- --------
      • Set IsUnitPreplaced[0] = False
      • Set UnitIndexEvent = 3.00

So if you want to have an event fire when a unit is created, when it is leaves the game, or when the unit indexer is initialized, you should take advantage of the UnitIndexEvent real variable.

  • Unit Indexed Event
    • Events
      • Game - UnitIndexEvent becomes Equal to 1.00
    • Conditions
      • UDexUnits[UDex] is A Hero Equal To true
    • Actions
      • Item - Create 1 Tome of Power and give it to UDexUnits[UDex]
  • Unit Deindexed Event
    • Events
      • Game - UnitIndexEvent becomes Equal to 2.00
    • Conditions
    • Actions
      • Game - Display to (All Players) the text - Unit (UDex) was deindexed!
  • Unit Indexer Initialized Event
    • Events
      • Game - UnitIndexEvent becomes Equal to 3.00
    • Conditions
    • Actions
      • Unit - Create 1 Paladin for (Player 1) at TempPoint facing 0.00 degrees
      • Item - Create 1 Ankh of Reincarnation and give it to (Last created unit)
      • Set HeroItem[(Custom value of (Last created unit))] = (Last created item)

Thanks
Nestharus for creating the powerful vJass UnitIndexer.
The community for encouraging me to use a unit indexer when I was learning.​

Examples of scripts using this

Pros
  • Gives units a unique custom value for use as array indexes (1-8190)
  • Recycles indexes of decayed/removed units
  • Very extensible library that can be used for not just spells but systems as well.
  • Extremely easy to implement (cnp)
  • No object editor data needed
  • Does not need a timer
  • No knowledge of JASS required. You can toggle the boolean UnitIndexerEnabled to filter unwanted units, and you can even customize the filter with more conditions than just that if you want to do something like filter summoned units or not allow any units to be indexed with locust, for example.
  • Has "OnIndex" (UnitIndexEvent == 1.00) and "OnDeindex" (UnitIndexEvent == 2.00) events
Cons
  • You can only get the custom value of units, not set it. Work with the unit indexer - not
    against it. You can easily create an array and reference all the data you need (custom value of unit as the array index) instead of using custom value directly.
  • Use the event - "Game - UnitIndexEvent Equal to 3.00" instead of "Map Initialization" if you want to make sure all of your units have custom value when used in that trigger's actions.
  • Units are not "in scope" when deindexed, so things like checking if it's summoned or if it's a hero will not work. Use the seperate resource "Unit Event" with the event "DeathEvent becomes Equal to 3.00" to catch a unit as soon as it's removed.
Keywords:
unit indexer, custom value, aids, autoindex, getunituserdata, unituserdata, mui, indexing, is unit moving, unitindexer, indexer, index, id generator,
Contents

GUI Unit Indexer Testmap (Map)

Reviews
16:29, 28th Jun 2011 Maker: Approved. Can be of great use in creating MUI spells and systems.

Moderator

M

Moderator

16:29, 28th Jun 2011
Maker: Approved. Can be of great use in creating MUI spells and systems.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
If you mean counting how many units are in the map at once, no this is not what it's used for.

The custom value of the unit is a mere integer used in array lookups to make indexing more automated (user-friendly).

  • Set MyData[(Custom value of (Triggering unit))] = 1000
^The above example effectively allows you assign "1000" to the unit as a pseudo-attribute. Normally in this case, data attachment would involve hashtables.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
It intentionally enumerates units with locust, because dummy units like projectiles can benefit from indexes.

However, I should make it clearer what users can add to make it filter out unwanted units.

Jesus4Lyf used a very similar approach in his "GUI AIDS" so I cannot take credit for the idea. This is simply a different implementation and has some pros and cons against his:

Jesus4Lyf

- Uses a timer throughout the game to check if units should be removed
- As a consequence, only removes up to 32 dead instances per second, making it (in a stretch of the imagination) possible that the digit 8190 can be exceeded (more a logical problem than a practical one)
- Ends up being more efficient when units are created
- Pretty easy to implement (3 sets of cnp)
Bribe

- Checks for removed units only when a new unit is added to the map
- Does a quick iteration over all indexed units each time a unit is added
- Ends up being more efficient over the course of the game, using fewer handles
- Easier to implement (two cnp instead of 3)
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
But the timer option in AIDS is optional from my experience. Else it uses the classic "undefend" ability to detect leave/removed instances.

Yeah you're probably right about the dummies now when I think about it, GUIers will gain alot by this.

Does the userdata always return 0 when a unit is removed/decayed? Cool.
 
Level 16
Joined
Apr 4, 2011
Messages
995
I'm really, really confused by this. But from what I gather, this replaces hashtables right? So you could put the custom value of whatever unit in a spell, and it will make it MUI, so long as you don't have 8190 units on the field, right? If that's what it does only problem I see with it is that is uses custom values. A lot of system use custom values
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Update to 1.0.0.1 - added an optional filter and an optional initializer.

Before, it was unclear how to filter units.
Now, you can filter units from the UnitIndexFilter function in the custom script.

Before, if you needed to get the custom value of a unit during map initialization, it may have returned zero.
Now, you can run any of those triggers from the designated spot in the Unit Indexer trigger (where it says "run initialization triggers after this line").

With these changes, this system is now perfect unless the user calls SetUnitUserData (sets the custom value of the unit) themselves. However, users should not be doing that if they want to use this system (only calling GetUnitUserData).

Finally, no map that exceeds 8190 units can be taken seriously. The array limit in that case is out of the question.
 
Level 2
Joined
Oct 15, 2008
Messages
17
hi i'm new to mapping and am having trouble on how to use this system in my map.

i followed the steps and added the system to my map.

i didn't edit anything, i.e, Unit Indexer trig and the jass script located on map name.

i tried setting custom integer variable to triggering units custom variable to my spell

and when i try to multi cast the spell with 2 different units with 2 different custom values, it doesn't work. The last casted spell overwrites the first one.

could someone help me fix this?

i have attached the map to this post.

thanks =)
 

Attachments

  • TestMap.w3x
    74.9 KB · Views: 478
Last edited:
Level 16
Joined
Apr 4, 2011
Messages
995
You have waits in your spell. Even with a unit indexer, waits will overwrite the data previously stored. To avoid this, DON'T USE WAITS. Look up some tutorials on making spells MUI. Just in case you don't, heres a simple breakdown. Don't ever use waits in spells unless you use local variables. Any spell that isn't instant needs a unit indexer (this very thing :), hashtables, or recycling. Next time you post something about triggers it should probably be under the "triggers and scripts" part of the help section.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Updated to 1.0.1.0, this version now recycles every 15 new units as an optimization. The demo map has been updated to be more dynamic - you can type commands to create new units, display their indices and click on them to remove them from the game. This is to better show off the index recycling as the old demo I had won't work with the new "recycle every 15 new units" setup.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
The point of a unit indexer is to use the same custom value of a unit for all systems, instead of relying on hashtables. "Custom value" is fine as long as it keeps in mind that other systems might also want to use it, which is exactly what this does.

Edit: Fixed a potential indexing problem and made the test map *way* better. You can play around with this thing a lot more than before.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Exellent tool for any GUI user. I'm pretty sure it will soon be used commonly.
Deserves DC. Since I can not give more oyu Bride at least you get: 5/5

Trust me. Eventually, the entire GUI community on this site will begin to use it ;)
It is true, and whoever doubts it, will burn.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
The only problem is that this will only be used in maps, as using it in spells uploaded here will break the rules since spells must not use unit custom values.

There is no spell rule that says you can't use
custom value. In fact, I should add a rule that
says "custom value" is encouraged, as long as
the only thing that sets it is a unit indexer of
some kind.

The thing about a unit indexer is it sets the
custom value for a unit only once, to give it a
unique ID, which can be shared by all systems.
The value is 1-8190 so it will be perfect for
array indices. Instead of setting the custom
value, now you just have to set an array by
array index of the custom value.

If a system sets the custom value and doesn't
consider that other resources might depend on
it, then I'll definitely reject it, because that's
breaking compatibility.
 
Level 22
Joined
Feb 3, 2009
Messages
3,292
There is no spell rule that says you can't use
custom value. In fact, I should add a rule that
says "custom value" is encouraged, as long as
the only thing that sets it is a unit indexer of
some kind.

The thing about a unit indexer is it sets the
custom value for a unit only once, to give it a
unique ID, which can be shared by all systems.
The value is 1-8190 so it will be perfect for
array indices. Instead of setting the custom
value, now you just have to set an array by
array index of the custom value.

If a system sets the custom value and doesn't
consider that other resources might depend on
it, then I'll definitely reject it, because that's
breaking compatibility.

Well in that case this is great, might actually make some spells of my own sometime:thumbs_up:.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
IsUnitMoving and UnitIndexer are pretty different resources. There
is unlimited potential for UnitIndexer while IsUnitMoving serves a
pretty specific purpose. At most I'd like to "examples" of "what you
can do", but I wouldn't consider IsUnitMoving an add-on.

However, as inspired by Maker, I will be making some changes to
both IsUnitMoving as well as UnitIndexer ;)
 
Level 16
Joined
Apr 4, 2011
Messages
995
My mistake, sorry. But I think any other resources that utilize the UnitIndexer as a base should be mentioned somewhere, which you said yourself

Bribe said:
At most I'd like to "examples" of "what you
can do", but I wouldn't consider IsUnitMoving an add-on.

IsUnitMoving is a great example of what you can do with the UnitIndexer. You are definitely right in that it serves a specific purpose, and I guess add-on was the wrong term for it, but it shows what the UnitIndexer can do :) Also, EvilCrispy has a spell that runs on UnitIndexer I believe.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Updated to version 1.1.0.0 - this now has three custom events that GUI
users can take advantage of:

  • Unit Indexer Events
    • Events
      • -------- When a unit is indexed: --------
      • Game - UnitIndexEvent becomes Equal to 1.00
      • -------- When a unit is deindexed: --------
      • Game - UnitIndexEvent becomes Equal to 2.00
      • -------- When Unit Indexer has been initialized: --------
      • Game - UnitIndexEvent becomes Equal to 3.00
    • Conditions
    • Actions
      • -------- UDexUnits[UDex] refers to the indexed/deindexed unit --------
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I'm pretty sure they are, although you can disable indexing new unit using:

  • Set UnitIndexerEnabled = False
  • Unit - <Create unit>
  • Set UnitIndexerEnabled = True
About summonable and illusions - they are still just normal Created units - with some modification ofcourse (like instantly added expiration timer).
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Update to 1.2.0.0 - now can be installed with just one copy and paste (I have removed the dependency on the map header script).

Edit: Added a "how to" for the index events, and fixed a possible recursion bug that was in all Event-based versions.

Edit 2: Fixed all possible recursion bugs.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
It's both.

Using arrays with Custom value of (unit) as the index is using fewer hashtables (you can only have a max of 256 in the map), is more logical (because each array has a name of its own) and is more practical because it's way faster than using hashtables.

For spells I typically recommend Hanky's Dynamic Indexing or I'll recommend this or hashtables if the user is more comfortable with that approach.

http://www.hiveworkshop.com/forums/spells-569/gui-dynamic-indexing-template-144325/
http://www.hiveworkshop.com/forums/...9/complete-beginners-guide-hashtables-197381/
 
  • Like
Reactions: nGy
Top