• 🏆 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.
Level 7
Joined
Jul 4, 2007
Messages
249
There is another reason: Get Handle Id in GUI does not allow selecting an unit/timer variable only the presets which are the event and enum getters.
That's actually quite nice, didn't think about that. Maybe I'll just start using it and explore the possibilites further

Using the events onIndex and onDeindex are maybe the most important feature.

Though, also an other point comes along.
The unit indexer starts values with 1 and then increases. It means one can use a unit's index as array index. Kills[index], Deaths[index], etc.
I guess that could be useful somehow, I just didn't see any problem with this in the first place. Maybe I have just not come across enough difficulties yet :D

I see now that this is very useful :D Thanks so much!
 
Last edited:
Level 9
Joined
May 5, 2007
Messages
253
Hi, i'm using this system in reforged world editor with spells that utilize it, and the game freezes every time the indexer recycles indices of units no longer in play (every 15). I set the UDexWasted value to 8196 and everything works now but I know that will probably cause problems in the future if the map is played for long periods. Is there a better solution? Is this just a problem with reforged?
 
Hi, i'm using this system in reforged world editor with spells that utilize it, and the game freezes every time the indexer recycles indices of units no longer in play (every 15). I set the UDexWasted value to 8196 and everything works now but I know that will probably cause problems in the future if the map is played for long periods. Is there a better solution? Is this just a problem with reforged?
It's best not to use it with Reforged for now. It's only most stable on 1.31.1 (latest Classic patch)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
It's best not to use it with Reforged for now. It's only most stable on 1.31.1 (latest Classic patch)
Yeah Reforged broke a bunch of my stuff, despite not actually adding new natives to the World Editor.

I will update the systems once Reforged is actually released and I know what it is I'm up against (as they are bound to change more code before then).
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hey!

Are you planning on making a Lua version of this? Or are there possibly already some other Unit Indexers that work on Lua maps? (Couldn't find any via Google)
There's a Lua Unit Indexer in the Damage Engine Lua demo map.

Here's the code for reference:

Lua:
do
   local MAX_WASTED = 1 --Every MAX_WASTED units created after map initialization, run the "garbage collector"
   
   local sUUD = SetUnitUserData
   function SetUnitUserData(whichUnit, val) end -- disallow user from changing this value.
   
   local function runEvent(dex, val)
      local pdex = udg_UDex
      udg_UDex = dex
      globals.udg_UnitIndexEvent = 0.00
      globals.udg_UnitIndexEvent = val
      udg_UDex = pdex
   end
   
   local iFuncs = {}
   function onUnitIndex(func)
      iFuncs[func] = func
   end
   onUnitIndex(function(dex) runEvent(dex, 1.00) end)
   
   local dFuncs = {}
   function onUnitDeindex(func)
      dFuncs[func] = func
   end
   onUnitDeindex(function(dex) runEvent(dex, 2.00) end)
   
   local wasted = 0
   local gen = 0
   local active = {}
   local inactive = {}
   local preplaced = true
   
   onTriggerInit(function()
      local re = CreateRegion()
      local r = GetWorldBounds()
      RegionAddRect(re, r) RemoveRect(r)
      local b = Filter(function()
         u = GetFilterUnit()
         if GetUnitUserData(u) == 0 then
            local dex
            if not preplaced then -- No need to check for removed units during the beginning of the game sequence
               wasted = wasted + 1
               if wasted > MAX_WASTED then
                  local n = #active
                  for i = n, 1, -1 do
                     if GetUnitUserData(udg_UDexUnits[dex]) == 0 then
                        active[i] = active[n]
                        active[n] = nil
                        n = n - 1
                        inactive[#inactive + 1] = dex
                        for k, f in pairs(dFuncs) do f(dex) end -- Run the deindex event
                        --print("deindexed" .. dex)
                     end
                  end
                  wasted = 0
               end
            end
            local n = #inactive
            if n == 0 then
               dex = gen + 1
               gen = dex
            else
               dex = inactive[n]
               inactive[n] = nil
            end
            active[#active + 1] = dex
           
            udg_UDexUnits[dex] = u
            sUUD(udg_UDexUnits[dex], dex)
           
            udg_IsUnitPreplaced[dex] = preplaced
            for k, f in pairs(iFuncs) do f(dex) end -- Run the index event
            --print("indexed " .. dex)
         end
         return false
      end)
      TriggerRegisterEnterRegion(CreateTrigger(), re, b)
      for i = bj_MAX_PLAYER_SLOTS - 1, 0, -1 do
         GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
      end
      preplaced = false
      globals.udg_UnitIndexEvent = 3.00
   end)
end
 
Level 9
Joined
Jul 30, 2018
Messages
445
Hmm.. It doesn't work. I have the Global Initialization script, Damage Engine and Unit Indexer. The first two work just fine, but the Unit Indexer does not (all units' CV is 0). Does it require anything else?
 
Level 6
Joined
May 29, 2013
Messages
126
Hello. I was using your is unit moving system and then using the spellbound's portal system.
However, the unit moving system used only some units that needed it, but the portal system indexes all units, which puts a burden on performance. Actually, a fatal error occurred.
because my map is a diplomacy map with a lot of units.
I would appreciate it if you could give me some advice. I'm going to use two indexers, one for the unit moving system and one for the portal system.
Will using two indexers make things worse?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hello. I was using your is unit moving system and then using the spellbound's portal system.
However, the unit moving system used only some units that needed it, but the portal system indexes all units, which puts a burden on performance. Actually, a fatal error occurred.
because my map is a diplomacy map with a lot of units.
I would appreciate it if you could give me some advice. I'm going to use two indexers, one for the unit moving system and one for the portal system.
Will using two indexers make things worse?
This has nothing to do with how many units are indexed (so don't use two indexers) and most likely has to do with unit count. Try increasing the movement interval checker to 0.25 if there continues to be a problem. I wouldn't increase the interval beyond 0.33 if I wanted to try to preserve seamlessness.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hi Bribe, is the Unit Indexer already supposed to fully work for Reforged? I'm getting quite significant framerate drops during the duration of for example this spell:
Frozen Orb v1.01

(asking because a previous comment in Dec 2019 stated to hold off with this in Reforged)
It SHOULD work in Reforged. Is the issue definitely with Unit Indexer (and are you on the latest version of it)?
 
Level 12
Joined
May 16, 2020
Messages
660
Yes, I'm using the latest version, but it seems to be only in my map;I imported the Indexer into the Frozen Orb spell map and there I don't experience the lag.

But it's definitely Unit Indexer in combination with something else, because when I deactivate the Indexer in my map, all spells run smoothly again... I will experiment some more and see what combination is at fault here.

What I use (just in case you'd have a guess to narrow the scope of possibilities):
  • TT
  • xebasic
  • xedamage
  • T32
  • SimError
  • TimerUtils
  • GroupUtils
  • Ilusion
  • DamageEvent
  • GUI Friendly Damage Detection
  • New Bonus
  • Unit Event
  • Damage Engine
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
Ah man, unfortunately "Illusion" requires "Unit Event"...
[vJASS] - [System] Illusion

Is it possible to make a JASS trigger refer to something written in GUI (= your Indexer)?

Edit: And it's not even the standard "illusion" anymore, because you adjusted in once for me to work with Damage Engine.
A Unit Indexer is a very simple system. At it's core, all it's doing is assigning a unique custom value to each unit.

The reason to use Bribe's Unit Indexer is because it has extra features like recycling unused custom values. So say a footman dies, it's custom value is now free to be used by a new unit. It's also nice and organized, containing everything you need in 1 GUI trigger. It also manages custom value for pre-placed units and newly created units.

So all you have to do is reference a unit's Custom Value to take advantage of it.

Custom Value is called UserData in Jass:
vJASS:
GetUnitUserData(whichUnit)
Remember, GUI is just Jass hidden behind a simple interface. Whenever you're using "Custom script: do stuff" you're actually writing in Jass.

  • Set Variable TempUnit = Triggering unit
  • Custom script: Set udg_TempInteger = GetUnitUserData(udg_TempUnit)
  • Display text message: "TempInteger"
The above trigger will display TempUnit's custom value. The point of this example is to show that anything you can do in GUI, you can do in Jass. It's actually the opposite problem that occurs, there's things you can only do in Jass that you can't do in GUI (without a lot of messy Custom Script).
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Ah man, unfortunately "Illusion" requires "Unit Event"...
[vJASS] - [System] Illusion

Is it possible to make a JASS trigger refer to something written in GUI (= your Indexer)?

Edit: And it's not even the standard "illusion" anymore, because you adjusted in once for me to work with Damage Engine.

I see, you are referring to the post I made here: [vJASS] - [System] Illusion

If you recall I had coded Illusion to work with GUI Unit Event, and I think that's why you now have Unit Event in your map. Losing Unit Indexer you won't miss out on anything at all. Your resources that need Unit Indexer can get away with using just Unit Event without issues.
 
Level 12
Joined
May 16, 2020
Messages
660
A Unit Indexer is a very simple system. At it's core, all it's doing is assigning a unique custom value to each unit.

The reason to use Bribe's Unit Indexer is because it has extra features like recycling unused custom values. So say a footman dies, it's custom value is now free to be used by a new unit. It's also nice and organized, containing everything you need in 1 GUI trigger. It also manages custom value for pre-placed units and newly created units.

So all you have to do is reference a unit's Custom Value to take advantage of it.

Hey Uncle, the problem is that I cannot simply remove the current indexer (Unit Event) I use, because another library (Illusion) has a dependency on it. I want to change (because I just like Bribe's work :)), but for this I need to somehow sever the dependency between Illusion and Unit Event.


I see, you are referring to the post I made here: [vJASS] - [System] Illusion

If you recall I had coded Illusion to work with GUI Unit Event, and I think that's why you now have Unit Event in your map. Losing Unit Indexer you won't miss out on anything at all. Your resources that need Unit Indexer can get away with using just Unit Event without issues.

Yes exactly this one, although I think you updated it even more than in this thread (in my map it says v1.4). Maybe that happened when I sent you my map and you fixed all the compatibility issues.

So you're basically saying that Unit Event is good enough?
What about these additional features from Unit Inexer that Uncle mentions? (i.e. recycling unused custom values). I just really like your work, so if possible I'd prefer to use Unit Indexer.
 
Level 2
Joined
Jul 8, 2017
Messages
17
So I'm using the GUI version and I think something broke, since the custom values of every unit on the map is just 0. Is there a fix for this?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
So I'm using the GUI version and I think something broke, since the custom values of every unit on the map is just 0. Is there a fix for this?

You probably have a thread crash on map initialization is my guess. Also which version WC3 are you using?

Maybe send me the map if you aren't able to isolate the issue and I can take a look at what might be the cause.
 
Level 6
Joined
Jul 12, 2021
Messages
95
What do some users mean by this system is "faster" than hashtables?

You can get a unique ID for each unit by using hashtables. Why not use those ID's instead of the ID's of this system?

If you compare this system with hashtables, what are the pros and cons of both?
 
This system is faster than hashtables because it uses an array.
Hashtables have 2 keys that can be used for indexing, and each can fit up to a total of ~2^33 combination of keys.
So looking things up on an array is quicker than looking things up on a hashtable.
Arrays, however, only support up to 32768 indices, so they don't support the large numbers used by handle IDs.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
What do some users mean by this system is "faster" than hashtables?

You can get a unique ID for each unit by using hashtables. Why not use those ID's instead of the ID's of this system?

If you compare this system with hashtables, what are the pros and cons of both?
This system is just far easier to work with. Hashtables often add extra Actions to your triggers and are hard to read (at least at first). They're both tools that a GUI user should take advantage of. Use the right tool for the job sort of thing.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
This system is faster than hashtables because it uses an array.
Hashtables have 2 keys that can be used for indexing, and each can fit up to a total of ~2^33 combination of keys.
So looking things up on an array is quicker than looking things up on a hashtable.
Arrays, however, only support up to 32768 indices, so they don't support the large numbers used by handle IDs.
Hashtables weren't even around when the first Unit Indexers were introduced (AutoIndex, PUI, AIDS). H2I was a thing, sure (now GetHandleId), but back then, people used to subtract "HandleID - 0x100000", which would break the moment the overall handle count exceeded 8192 (this number was only increased with the introduction of 1.29 or 1.30).

GetUnitUserData is certainly a faster operation than using hashtables, however it is limited to just units. Dedicated Unit Indexers decreased heavily in popularity because of this.

The main benefit of an indexing system is that it allows you to allocate/deallocate data to a unit as it enters/leaves playable scope. That's what makes UnitEvent so special - it can allow you to remove or add units to a system depending on whether they are alive, reincarnating, dead (but revivable), fully removed or simply loaded/unloaded from a transport.
 
Level 6
Joined
Jul 12, 2021
Messages
95
So looking things up on an array is quicker than looking things up on a hashtable.

I have an honest question.
Is it really important that it is quicker? I haven't had any issue yet using hashtables. Perhaps in certain specific scenarios it becomes important? Perhaps it is more taxing for the client?

I'm really into efficiency, so if it is more taxing it would be a huge deal.
 
Last edited:
I have an honest question.
Is it really important that it is quicker? I haven't had any issue yet using hashtables. Perhaps in certain specific scenarios it becomes important? Perhaps it is more taxing for the client?

I'm really into efficiency, so if it is more taxing it would be a huge deal.
Speed might be a problem if an action has to be executed hundreds of times per second.
However, I think hashtables are still very fast if you assign loaded handles to variables instead of calling the load handle function every time you want to use.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Speed might be a problem if an action has to be executed hundreds of times per second.
However, I think hashtables are still very fast if you assign loaded handles to variables instead of calling the load handle function every time you want to use.
Yes, anything that happens a LOT and all at once (or that overlaps with processes that are already steadily bogging down a map's performance) should be looked into to try to mitigate them - to a reasonable extent. Improving the efficiency of a damage-detection-system is not important in a 1v1 scenario, but when fighting hoards of units at once, one needs to look into decreasing the operation count in a damage system (and its dependent triggers) as much as is realistically possible.

I've made a lot of edits to Damage Engine and have supplied certain users with custom builds of Damage Engine in the past to help mitigate this problem.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
Yes, anything that happens a LOT and all at once (or that overlaps with processes that are already steadily bogging down a map's performance) should be looked into to try to mitigate them - to a reasonable extent. Improving the efficiency of a damage-detection-system is not important in a 1v1 scenario, but when fighting hoards of units at once, one needs to look into decreasing the operation count in a damage system (and its dependent triggers) as much as is realistically possible.

I've made a lot of edits to Damage Engine and have supplied certain users with custom builds of Damage Engine in the past to help mitigate this problem.
You wouldn't happen to have one of those custom Damage Engine versions lying around, would you? I find myself usually only needing these variables:
DamageEvent, DamageModifierEvent, DamageEventAmount, IsDamageMelee/Ranged/Code/Spell, DamageEventSource/Target.

Those seem to get the job done for whatever I need. Don't worry about it if it's too much trouble.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
While this is a bit heavy on the requirements, it shows a very basic implementation of UnitIndexer in Lua. I'm using the same garbage collector trick that @Dr Super Good introduced in his automatic memory leak destroyer resource.

Lua:
OnInit(function(require)  --https://github.com/BribeFromTheHive/Lua-Core/blob/main/Total_Initialization.lua
    require "GlobalRemap" --https://github.com/BribeFromTheHive/Lua-Core/blob/main/Global_Variable_Remapper.lua
    require "Event"       --https://github.com/BribeFromTheHive/Lua-Core/blob/main/Event.lua
    
    Event.create("OnUnitIndexed", true)
    Event.create("OnUnitRemoval", true)
    
    local unitRef = setmetatable({}, {__mode = "k"})
    local eventUnit
    local collector = {__gc = function(unit)
        eventUnit = unit[1]
        unitRef[eventUnit] = nil
        Event.OnUnitRemoval(eventUnit)
    end}

    GetUnitUserData = function(unit) return unit end
    
    GlobalRemap("udg_UDex",  function() return eventUnit end) --fools GUI into thinking unit is an integer
    GlobalRemapArray("udg_UDexUnits", function(unit) return unit end)

    local preplaced = true

    OnInit.trig(function()
        local re = CreateRegion()
        local r = GetWorldBounds()
        RegionAddRect(re, r); RemoveRect(r)
        local b = Filter(
        function()
            local u = GetFilterUnit()
            if not unitRef[u] then
                unitRef[u] = {u}
                setmetatable(unitRef[u], collector)
                if rawget(_G, "udg_IsUnitPreplaced") then
                    udg_IsUnitPreplaced[u] = preplaced
                end
                eventUnit = u
                Event.OnUnitIndexed(u)
            end
        end)
        TriggerRegisterEnterRegion(CreateTrigger(), re, b)
        for i = 0, bj_MAX_PLAYER_SLOTS -1 do
            GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
        end
        preplaced = nil
    end)
end)
 
Which data in particular are you trying to reference?
Just want to know which unit being deindexed (so, custom value of unit) so I can remove my system data on deindex.

Also, is there a way to control the order of event in the initialization? I just discovered that I need to have this system initialization done after my system initialization so the necessary data are available when unit are being indexed on init.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Just want to know which unit being deindexed (so, custom value of unit) so I can remove my system data on deindex.
You'd just use UDexUnits[UDex]. You can still get stuff like its name.

Also, is there a way to control the order of event in the initialization? I just discovered that I need to have this system initialization done after my system initialization so the necessary data are available when unit are being indexed on init.

When is your current system being initialized? There is a UnitIndexEvent == 3.00 event that exists as a replacement to Map Initialization events that need the units' indices.
 
You'd just use UDexUnits[UDex]. You can still get stuff like its name.



When is your current system being initialized? There is a UnitIndexEvent == 3.00 event that exists as a replacement to Map Initialization events that need the units' indices.
Thanks, that UDexUnits[UDex] is exactly what I need. I thought that was lost on that event.

My system uses UnitIndexEvent 1.00 to detect registration but the registration data needs to be present before the event occurs, since apparently event 1.00 occurs before event 3.00.

For more detail, my system has a unit type registration trigger and onIndex registration trigger which detects unit type to apply effects based on unit type registration. I need to have the unit type registration trigger run before the onIndex event.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Thanks, that UDexUnits[UDex] is exactly what I need. I thought that was lost on that event.

My system uses UnitIndexEvent 1.00 to detect registration but the registration data needs to be present before the event occurs, since apparently event 1.00 occurs before event 3.00.

For more detail, my system has a unit type registration trigger and onIndex registration trigger which detects unit type to apply effects based on unit type registration. I need to have the unit type registration trigger run before the onIndex event.
If you use Unit Event instead, you can use the 1.5 event that I made for SpellBound (which is called OnUnitCreated in the Lua version). This occurs after a 0 timer, so buys you the delay you're looking for I think.
 
If you use Unit Event instead, you can use the 1.5 event that I made for SpellBound (which is called OnUnitCreated in the Lua version). This occurs after a 0 timer, so buys you the delay you're looking for I think.
Does UnitEvent support 1.31? I am using that patch to develop the system.
 
Level 11
Joined
Jul 4, 2016
Messages
626
Is this correct way to reference the unit outside of the indexer events or is it not possible to reference the unit outside of events
JASS:
set udg_udex = some number

exitwhen udg_UDexUnits[udg_UDex] == null
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
Your question doesn't really make sense, Lt_Hawkeye. You will just refer to the unit by whatever way you normally refer to that unit based on the trigger's events. What scenario are you encountering or imagining where neither UDexUnits[UDex] (if it ran from a UDex event) and Triggering Unit (if it ran from something else) will be sufficient? For something like a player event you will have to do a search of units in the appropriate area, owned by the appropriate unit, matching whatever conditions need to be matched, etc., the same as you would without using this Indexer.
 
Level 11
Joined
Jul 4, 2016
Messages
626

Let me clarifty
I'm using IPool as a container for a number of unit's custom data.

I was assuming that the Unit Indexer keeps track of the units it indexes in an array and such would be able to refer that unit outside of indexer events

That is to avoid having unnecessary extraneous variables to save the unit if it already exists

I am looping through the IPool's set of values by setting the udg_UDex to the value inside the IPool to grab the unit from the Indexer Array.

JASS:
 //safety mechanism to make sure the loop will terminate
    if not s.contains(b) then
        return
    else
        call s.remove(b)
    endif
   
    call DisplayTextToPlayer(Player(0),0,0,"UNIT IS SILVA DAMAAGE")
   
    loop
        set udg_UDex = s.getItem()

        //Exitwhen there are no iterations left
        exitwhen udg_UDexUnits[udg_UDex] == null
       
        set x = GetUnitX(udg_UDexUnits[udg_UDex])
        set y = GetUnitY(udg_UDexUnits[udg_UDex])
       
       
        //Play SFX on unit's location
        call SetSoundPosition(gg_snd_MeatwagonMissileHit1,x,y,0)
        call SetSoundVolume(gg_snd_MeatwagonMissileHit1,127)
        call StartSound(gg_snd_MeatwagonMissileHit1)
       
        //Get how many slivAttacks picked unit has taken
        set slivAttacks = s.weightOf(udg_UDex)
           
        //if 10 attacks (25*10=250), it becomes AoE
        //What it would do single-target - 150, and that 150 becomes AoE now.
        if slivAttacks > 9 then
            call UnitDamageTarget(udg_Spell__Caster,udg_UDexUnits[udg_UDex],I2R(slivAttacks)*25.0-150,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
            call DamageAreaForPlayerTK(udg_Spell__CasterOwner,250.0,150,x,y)
        else//Single target dmg
            call UnitDamageTarget(udg_Spell__Caster,udg_UDexUnits[udg_UDex],I2R(slivAttacks)*25.0,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
        endif
           
        //Pure Special Effects (visual) below
           
        call DestroyEffectTimed(AddSpecialEffect("Objects\\Spawnmodels\\NightElf\\NightElfBlood\\NightElfBloodDruidBear.mdl",x,y),10)
               
        if not IsUnitType(udg_UDexUnits[udg_UDex] ,UNIT_TYPE_TAUREN) and not IsUnitType(udg_UDexUnits[udg_UDex] ,UNIT_TYPE_STRUCTURE) then
               
            if slivAttacks > 3 then
                call DestroyEffectTimed(AddSpecialEffect("Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl",x,y),10)
            endif
                   
            if slivAttacks > 5 then
                call DestroyEffectTimed(AddSpecialEffect("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl",x,y),10)
            endif
               
            if slivAttacks > 7 then
                call DestroyEffectTimed(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\UndeadLargeDeathExplode\\UndeadLargeDeathExplode.mdl",x,y),10)
            endif
                   
            if slivAttacks > 9 then
                call DestroyEffectTimed(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",x,y),10)
            endif
        else
            call DestroyEffectTimed(AddSpecialEffect("Objects\\Spawnmodels\\NightElf\\NECancelDeath\\NECancelDeath.mdl",x,y),10)
                   
            if slivAttacks > 5 then
                call DestroyEffectTimed(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",x,y),10)
            endif
        endif
               
        //Remove unit, so the next picked unit will not be the same ;)
        call s.remove(udg_UDex)
       
    endloop
   
    //destroy the group since we no longer need it.
    call s.destroy()
           
endfunction

But thanks for the response though I managed to figure out what the issue was just now.
 
Top