1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

GUI Unit Indexer 1.4.0.0

Submitted by Bribe
This bundle is marked as approved. It works and satisfies the submission rules.
Why Unit Indexing is Important

Read

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


How to take advantage of UnitIndexEvent

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
Moderator
16:29, 28th Jun 2011 Maker: Approved. Can be of great use in creating MUI spells and systems.
  1. 16:29, 28th Jun 2011
    Maker: Approved. Can be of great use in creating MUI spells and systems.
     
  2. nafre

    nafre

    Joined:
    Oct 16, 2010
    Messages:
    189
    Resources:
    4
    Maps:
    4
    Resources:
    4
    Does this mean that this trigger can count units for you and give the unit a special code?
     
  3. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    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.
     
  4. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,005
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    This really deserves a DC
    A UnitIndexer is ok, but a UnitIndexer for GUIers?? That is above and beyond :)
    The only problem is that some GUIers use custom values for other stuff :p
    If they do, it's their loss xD
    Good Job dude sir Bribe :p 5/5 ^^
     
  5. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    LOL what is this?! You know that GUIers doesn't use unit indexers for some what unknown reason.

    The filter should filter out units with locust btw.
     
  6. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    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)
     
  7. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    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.
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    His GUI version of AIDS only uses a timer, it doesn't have an option for the undefend bug.

    GetUnitUserData returns 0 for null/removed/decayed units. The wc3 system does some internal deallocation when units pass from scope - an event Blizzard forgot couldn't be bothered with to give us access to.
     
  9. Jazztastic

    Jazztastic

    Joined:
    Apr 4, 2011
    Messages:
    895
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    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
     
  10. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Those other systems shouldn't be using
    SetUnitUserData
    , but should rather just be assuming that a unit indexer is in the map and make use of
    GetUnitUserData


    = )
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    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.
     
  12. nafre

    nafre

    Joined:
    Oct 16, 2010
    Messages:
    189
    Resources:
    4
    Maps:
    4
    Resources:
    4
    i still do not understand what does a indexer do? Could someone explain this a little more if you have the time?
     
  13. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    It gives each unit a unique id that can be used as an array index.


    So rather than (hashtable)

    SaveInteger(table,GetHandleId(unit),0,value)


    you can do (array)

    set a[GetUnitUserData(unit)]=value
     
  14. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Updated description with a short text on why unit indexing is important.
     
  15. tenchisouzou

    tenchisouzou

    Joined:
    Oct 15, 2008
    Messages:
    17
    Resources:
    0
    Resources:
    0
    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 =)
     

    Attached Files:

    Last edited: Jun 19, 2011
  16. Jazztastic

    Jazztastic

    Joined:
    Apr 4, 2011
    Messages:
    895
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    Make sure you properly affix the map . . . Because it isn't showing up. Once you attach the file you have to also click the little attachment button to put in the attachment in the post somewhere.
     
  17. tenchisouzou

    tenchisouzou

    Joined:
    Oct 15, 2008
    Messages:
    17
    Resources:
    0
    Resources:
    0
    yeh just fixed it, i attached the wrong map b4 so i edited it lol
     
  18. Jazztastic

    Jazztastic

    Joined:
    Apr 4, 2011
    Messages:
    895
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    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.
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,748
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    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.