[General] Unit Indexing and Game time ( updated )

Level 16
Joined
Feb 22, 2025
Messages
315
Hi, this is supposed to be just a detection and warning system that would give the player a visual warning when a certain abomination is about to cast meat hook ( dummy is the caster). Since I'm using the Unit Indexer, I suppose that each unit is given a different integer custom value. I need advice regarding indexing and recycling, so is it better to use a hashtable for this instance, also is it recommended to use a spare unit group when doing instancing? Feedback is appriciated, cheers ppl :vw_death:
PS Is there a GUI option to get real - Game time ? Couldn't find it so i ussed jass f

Update 1: I'm not sure why setting 1 unit group equal other unit group never makes them even. I couldn't fix it by adding all units from abo group to abo temp group, so i sticked with the original group. The core problem i think i found is that the conditions never meet to finish the travel time, they get stuck and get cleared only upon death, and I suppose this has to do with the measuring the elapsed game time and given conditions. I added a hashtable, and now I'm really not sure how to get this to work apart from creating a timer that would run forever then doing the conditionals .... Updated triggers below

Update 2: Instead relying on time elapsed (f) created 2 real array variables, then did the math, okay so this now works, but I'm not sure is it reliable, also why is bj_gameStartedTimer for elapsed time not doing the trick, I believe someone with JASS knowledge knows a workaround to this, I couldn't find what I was looking for on the forums. Updated triggers below:


  • Test group
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set Abomination_Index = 0
      • Set Abomination_Recycle_Count = 0
      • Hashtable - Create a hashtable
      • Set Abomination_Hashtable = (Last created hashtable)
      • Unit Group - Add Fel Abomination 0028 <gen> to AbominationGroup
      • Unit Group - Add Fel Abomination 0029 <gen> to AbominationGroup
      • Unit Group - Add Fel Abomination 0027 <gen> to AbominationGroup
      • Wait 2.00 seconds
      • Trigger - Turn on MeatHook Scan <gen>
  • MeatHook Scan
    • Events
      • Time - Every 5.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in AbominationGroup and do (Actions)
        • Loop - Actions
          • Set Abomination_TempUnit = (Picked unit)
          • Custom script: set udg_Abomination_TempInteger = LoadInteger(udg_Abomination_Hashtable, GetHandleId(udg_Abomination_TempUnit), 0)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Abomination_TempUnit is alive) Equal to True
              • Abomination_TempInteger Equal to 0
            • Then - Actions
              • Set Abomination_TempUnit2 = No unit
              • Set Abomination_TempPoint = (Position of Abomination_TempUnit)
              • Set Abomination_TargetGroup = (Units within 900.00 of Abomination_TempPoint matching ((((Matching unit) is alive) Equal to True) and ((((Matching unit) is A Hero) Equal to True) and (((Matching unit) belongs to an enemy of (Owner of Abomination_TempUnit).) Equal to True))).)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Number of units in Abomination_TargetGroup) Greater than 0
                • Then - Actions
                  • Set Abomination_TempTarget = No unit
                  • Set Abomination_TempReal = 1000.00
                  • Set Abomination_TempPoint2 = Abomination_TempPoint
                  • Unit Group - Pick every unit in Abomination_TargetGroup and do (Actions)
                    • Loop - Actions
                      • Set Abomination_TempPoint3 = (Position of (Picked unit))
                      • Set Abomination_TempReal2 = (Distance between Abomination_TempPoint2 and Abomination_TempPoint3)
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Abomination_TempReal2 Less than Abomination_TempReal
                        • Then - Actions
                          • Set Abomination_TempReal = Abomination_TempReal2
                          • Set Abomination_TempUnit2 = (Picked unit)
                          • Game - Display to (All players) for 2.00 seconds the text: abo picked
                        • Else - Actions
                      • Custom script: call RemoveLocation( udg_Abomination_TempPoint3)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_TempUnit2 Not equal to No unit
                    • Then - Actions
                      • Set Abomination_TempInteger = 0
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Abomination_Recycle_Count Greater than 0
                        • Then - Actions
                          • Set Abomination_TempInteger = Abomination_RecycleStack[Abomination_Recycle_Count]
                          • Set Abomination_Recycle_Count = (Abomination_Recycle_Count - 1)
                        • Else - Actions
                          • Set Abomination_Index = (Abomination_Index + 1)
                          • Set Abomination_TempInteger = Abomination_Index
                      • Custom script: call SaveInteger(udg_Abomination_Hashtable, GetHandleId(udg_Abomination_TempUnit), 0, udg_Abomination_TempInteger)
                      • Set Abomination_Unit[Abomination_TempInteger] = Abomination_TempUnit
                      • Set Abomination_Target[Abomination_TempInteger] = Abomination_TempUnit2
                      • Set Abomination_State[Abomination_TempInteger] = 1
                      • Set Abomination_Active[Abomination_TempInteger] = True
                      • Set Abomination_Warning_Countdown[Abomination_TempInteger] = 1.70
                      • Special Effect - Create a special effect attached to the overhead of Abomination_Unit[Abomination_TempInteger] using ExcMark_Red_Emergency.mdx
                      • Set Abomination_WarningSFX[Abomination_TempInteger] = (Last created special effect)
                      • Sound - Play ChainsWhoosh <gen> at 100.00% volume, located at Abomination_TempPoint with Z offset 0.00
                      • Game - Display to (All players) for 2.00 seconds the text: abo paused
                      • Unit - Make Abomination_Unit[Abomination_TempInteger] face Abomination_Target[Abomination_TempInteger] over 0.00 seconds
                      • Unit - Pause Abomination_Unit[Abomination_TempInteger]
                      • Animation - Play Abomination_Unit[Abomination_TempInteger]'s attack 2 animation
                    • Else - Actions
                • Else - Actions
              • Custom script: call RemoveLocation( udg_Abomination_TempPoint)
              • Custom script: call DestroyGroup( udg_Abomination_TargetGroup)
            • Else - Actions
  • Abo Loop
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer Abomination_LoopIndex) from 1 to Abomination_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Abomination_Active[Abomination_LoopIndex] Equal to True
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Abomination_State[Abomination_LoopIndex] Equal to 1
                • Then - Actions
                  • -------- Warning Finished, create dummy for cast --------
                  • Set Abomination_Warning_Countdown[Abomination_LoopIndex] = (Abomination_Warning_Countdown[Abomination_LoopIndex] - 0.03)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_Warning_Countdown[Abomination_LoopIndex] Less than or equal to 0.00
                    • Then - Actions
                      • Set Abomination_TempPoint4 = (Position of Abomination_Unit[Abomination_LoopIndex])
                      • Set Abomination_TempPoint5 = (Position of Abomination_Target[Abomination_LoopIndex])
                      • Unit - Create 1 Meat Hook Dummy for (Owner of Abomination_Unit[Abomination_LoopIndex]) at Abomination_TempPoint4 facing Default building facing degrees
                      • Set Abomination_Dummy_Caster = (Last created unit)
                      • Unit - Add a 2.00 second Generic expiration timer to Abomination_Dummy_Caster
                      • Unit - Order Abomination_Dummy_Caster to Human Mortar Team - Flare Abomination_TempPoint5
                      • Custom script: call RemoveLocation( udg_Abomination_TempPoint4)
                      • Custom script: call RemoveLocation( udg_Abomination_TempPoint5)
                      • -------- Travel State --------
                      • Set Abomination_State[Abomination_LoopIndex] = 2
                      • Set Abomination_Travel_Duration[Abomination_LoopIndex] = 1.70
                    • Else - Actions
                • Else - Actions
              • -------- Check travel state --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Abomination_State[Abomination_LoopIndex] Equal to 2
                • Then - Actions
                  • Game - Display to (All players) for 0.50 seconds the text: "Travel finished, c...
                  • -------- Travel Finished, cleanup --------
                  • Set Abomination_Travel_Duration[Abomination_LoopIndex] = (Abomination_Travel_Duration[Abomination_LoopIndex] - 0.03)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_Travel_Duration[Abomination_LoopIndex] Less than or equal to 0.00
                    • Then - Actions
                      • Unit - Unpause Abomination_Unit[Abomination_LoopIndex]
                      • Animation - Reset Abomination_Unit[Abomination_LoopIndex]'s animation
                      • Special Effect - Destroy Abomination_WarningSFX[Abomination_LoopIndex]
                      • Custom script: call SaveInteger(udg_Abomination_Hashtable, GetHandleId(udg_Abomination_Unit[udg_Abomination_LoopIndex]), 0, 0)
                      • Set Abomination_Unit[Abomination_LoopIndex] = No unit
                      • Set Abomination_Target[Abomination_LoopIndex] = No unit
                      • Set Abomination_Active[Abomination_LoopIndex] = False
                      • Set Abomination_Recycle_Count = (Abomination_Recycle_Count + 1)
                      • Set Abomination_RecycleStack[Abomination_Recycle_Count] = Abomination_LoopIndex
                    • Else - Actions
                • Else - Actions
            • Else - Actions
  • Abo CleanupOnDeath
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Dying unit) is in AbominationGroup.) Equal to True
    • Actions
      • Set Abomination_TempUnit = (Dying unit)
      • Custom script: set udg_Abomination_TempInteger = LoadInteger(udg_Abomination_Hashtable, GetHandleId(udg_Abomination_TempUnit), 0)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Abomination_TempInteger Greater than 0
          • Abomination_TempInteger Less than or equal to Abomination_Index
          • Abomination_Unit[Abomination_TempInteger] Equal to Abomination_TempUnit
        • Then - Actions
          • Special Effect - Destroy Abomination_WarningSFX[Abomination_TempInteger]
          • Unit - Unpause Abomination_TempUnit
          • Set Abomination_Unit[Abomination_TempInteger] = No unit
          • Set Abomination_Target[Abomination_TempInteger] = No unit
          • Set Abomination_Active[Abomination_TempInteger] = False
          • Custom script: call SaveInteger(udg_Abomination_Hashtable, GetHandleId(udg_Abomination_TempUnit), 0, 0)
          • Set Abomination_Recycle_Count = (Abomination_Recycle_Count + 1)
          • Set Abomination_RecycleStack[Abomination_Recycle_Count] = Abomination_TempInteger
        • Else - Actions
      • Unit Group - Remove Abomination_TempUnit from AbominationGroup.
 
Last edited:
I'm not sure why setting 1 unit group equal other unit group never makes them even
I'll address the issue with unit groups first, since I saw your post before your edit.
What you had was this:
  • Set TempGroup = AbominationGroup
  • ... do something with temp group
  • Custom script: call DestroyGroup(udg_TempGroup)
What this does is that it assigns the group referenced by AbominationGroup variable into variable TempGroup.
This does not create new group, it just means that two separate variables (TempGroup and AbominationGroup) now point to same unit group. So when you call the "DestroyGroup" function, it destroys the group that both TempGroup and AbomiationGroup were referencing. So first execution of trigger will work, but from then on it will not work since your "Set TempGroup = AbominationGroup" action basically does "Set TempGroup = null".

Similar situation will happen even if your code looks like this:
  • Unit Group - Add all units of AbominationGroup to TempGroup
  • ... do something with temp group
  • Custom script: call DestroyGroup(udg_TempGroup)
You do not create a new unit group here, you add units to an existing unit group. Since at the bottom of the script you destroy that group and do not recreate it, nothing is added anymore.
Why it works the first time is because of convenience: the game creates unit groups at map start and assigns them into all global unit group variables.

---
About unit indexers:
When you are using standard indexing approach you assign a unique index to each instance of spell/system (usually by just doing new_index = current_index + 1) and then you use a bunch of array variables to track whatever data the instance needs. The important thing is that data for given instance are stored under same index in all array variables. So if a spell's instance ID is 142, then data in all related array variables will be stored under index 142, etc.
When you iterate over instances, you usually use a for-loop to iterate from 1 till maxIndex.
  • Set Index = Index + 1
  • Set Duration[Index] = 1.50
  • Set Target[Index] = (Target unit of ability being cast)
  • Set Casters[Index] = (Triggering unit)
When you are using unit indexer, all units already have a unique number assigned to their custom value (CV) field. As such, there is no need to try to determine new index for instance of a spell/system unless a single unit may have multiple instances active at the same time. But for all other situations, like your meat hook, a unit may have only a single instance of a spell active.
The usual approach is to track all owners of the instance in a unit group variable and to iterate over all instances what you do is you iterate the unit group and load related data from arrays using custom value of the owner as index.
  • Set Index = Custom Value of (Triggering unit)
  • Set Duration[Index] = 1.50
  • Set Target[Index] = (Target unit of ability being cast)
  • Unit - Add (Triggering unit) to Casters
In your case, storing id of instance into Custom Value of the abomination is not needed, since the Meat Hook ability can have only a single instance active per abomination.
But if you had a spell where a single unit could have multiple instances active, then the custom value approach would also not work, since your unit could have for example 2 active instances, but Custom Value field would track only one of them.

---
Indexing:
You are using quite inefficient way of indexing. From what I see you basically do not clean up anything, instead you track more things so that you can skip over obsolete/finished instances. You have:
  • Abomination_Index, which only ever goes up, never down
  • Abomination_Active, a boolean which is used to track if instance is finished (and can be skipped in loop) or not
  • Abomination_RecycleStack and Abomination_Recycle_Count which are used to track finished instances so that you can reuse them.

Although the above may work, it just creates a convoluted system. The standard approach is to index new instance and deindex finished instance.
Indexing:
  • Increase value of MaxIndex by one
  • Store all instance related data in arrays under MaxIndex
  • Set VariableSet AH_Index = (AH_Index + 1)
  • Set VariableSet AH_Caster[AH_Index] = (Triggering unit)
  • Set VariableSet AH_Target[AH_Index] = (Target unit of ability being cast)
  • Set VariableSet AH_Duration[AH_Index] = 12.50
Deindexing:
  • Swap finished instance's data with last instance
  • Decrease MaxIndex by one
  • If deindexing is happening inside for-loop, then also decrease loop's integer variable by one to run that same loop again.
  • For each (Integer loopVar) from 1 to AH_Index, do (Actions)
    • Loop - Actions
      • ... some actions that determine if instance was finished or not
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • instanceFinished Equal to True
        • Then - Actions
          • Set VariableSet AH_Caster[loopVar] = AH_Caster[AH_Index]
          • Set VariableSet AH_Target[loopVar] = AH_Target[AH_Index]
          • Set VariableSet AH_Duration[loopVar] = AH_Duration[AH_Index]
          • Set VariableSet AH_Index = (AH_Index - 1)
          • Set VariableSet loopVar = (loopVar - 1)
        • Else - Actions
See for example this tutorial: https://www.hiveworkshop.com/threads/visualize-dynamic-indexing.241896/

---
Game time:
I don't know if there is a GUI way to access the "Game started" timer, although I imagine you could at map start just assign "bj_gameStartedTimer" into a global timer variable and then reference the global variable in GUI.

Anyway, I don't think using game time makes sense here. It would make sense if you wanted to measure time of something event-based. For example how long it took a unit to reach some specific region once quest started, etc.
But in your spell it does not make sense, because you already have a timer there: The 0.03 timer that starts your loop trigger. So instead of calculating elapsed game time, you can just decrement value in Abomination_Warning_Duration by 0.03 every time the loop trigger runs and check if value is less than or equal zero.
 
I'll address the issue with unit groups first, since I saw your post before your edit.
What you had was this:
  • Set TempGroup = AbominationGroup
  • ... do something with temp group
  • Custom script: call DestroyGroup(udg_TempGroup)
What this does is that it assigns the group referenced by AbominationGroup variable into variable TempGroup.
This does not create new group, it just means that two separate variables (TempGroup and AbominationGroup) now point to same unit group. So when you call the "DestroyGroup" function, it destroys the group that both TempGroup and AbomiationGroup were referencing. So first execution of trigger will work, but from then on it will not work since your "Set TempGroup = AbominationGroup" action basically does "Set TempGroup = null".

Similar situation will happen even if your code looks like this:
  • Unit Group - Add all units of AbominationGroup to TempGroup
  • ... do something with temp group
  • Custom script: call DestroyGroup(udg_TempGroup)
You do not create a new unit group here, you add units to an existing unit group. Since at the bottom of the script you destroy that group and do not recreate it, nothing is added anymore.
Why it works the first time is because of convenience: the game creates unit groups at map start and assigns them into all global unit group variables.

---
About unit indexers:
When you are using standard indexing approach you assign a unique index to each instance of spell/system (usually by just doing new_index = current_index + 1) and then you use a bunch of array variables to track whatever data the instance needs. The important thing is that data for given instance are stored under same index in all array variables. So if a spell's instance ID is 142, then data in all related array variables will be stored under index 142, etc.
When you iterate over instances, you usually use a for-loop to iterate from 1 till maxIndex.
  • Set Index = Index + 1
  • Set Duration[Index] = 1.50
  • Set Target[Index] = (Target unit of ability being cast)
  • Set Casters[Index] = (Triggering unit)
When you are using unit indexer, all units already have a unique number assigned to their custom value (CV) field. As such, there is no need to try to determine new index for instance of a spell/system unless a single unit may have multiple instances active at the same time. But for all other situations, like your meat hook, a unit may have only a single instance of a spell active.
The usual approach is to track all owners of the instance in a unit group variable and to iterate over all instances what you do is you iterate the unit group and load related data from arrays using custom value of the owner as index.
  • Set Index = Custom Value of (Triggering unit)
  • Set Duration[Index] = 1.50
  • Set Target[Index] = (Target unit of ability being cast)
  • Unit - Add (Triggering unit) to Casters
In your case, storing id of instance into Custom Value of the abomination is not needed, since the Meat Hook ability can have only a single instance active per abomination.
But if you had a spell where a single unit could have multiple instances active, then the custom value approach would also not work, since your unit could have for example 2 active instances, but Custom Value field would track only one of them.

---
Indexing:
You are using quite inefficient way of indexing. From what I see you basically do not clean up anything, instead you track more things so that you can skip over obsolete/finished instances. You have:
  • Abomination_Index, which only ever goes up, never down
  • Abomination_Active, a boolean which is used to track if instance is finished (and can be skipped in loop) or not
  • Abomination_RecycleStack and Abomination_Recycle_Count which are used to track finished instances so that you can reuse them.

Although the above may work, it just creates a convoluted system. The standard approach is to index new instance and deindex finished instance.
Indexing:
  • Increase value of MaxIndex by one
  • Store all instance related data in arrays under MaxIndex
  • Set VariableSet AH_Index = (AH_Index + 1)
  • Set VariableSet AH_Caster[AH_Index] = (Triggering unit)
  • Set VariableSet AH_Target[AH_Index] = (Target unit of ability being cast)
  • Set VariableSet AH_Duration[AH_Index] = 12.50
Deindexing:
  • Swap finished instance's data with last instance
  • Decrease MaxIndex by one
  • If deindexing is happening inside for-loop, then also decrease loop's integer variable by one to run that same loop again.
  • For each (Integer loopVar) from 1 to AH_Index, do (Actions)
    • Loop - Actions
      • ... some actions that determine if instance was finished or not
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • instanceFinished Equal to True
        • Then - Actions
          • Set VariableSet AH_Caster[loopVar] = AH_Caster[AH_Index]
          • Set VariableSet AH_Target[loopVar] = AH_Target[AH_Index]
          • Set VariableSet AH_Duration[loopVar] = AH_Duration[AH_Index]
          • Set VariableSet AH_Index = (AH_Index - 1)
          • Set VariableSet loopVar = (loopVar - 1)
        • Else - Actions
See for example this tutorial: https://www.hiveworkshop.com/threads/visualize-dynamic-indexing.241896/

---
Game time:
I don't know if there is a GUI way to access the "Game started" timer, although I imagine you could at map start just assign "bj_gameStartedTimer" into a global timer variable and then reference the global variable in GUI.

Anyway, I don't think using game time makes sense here. It would make sense if you wanted to measure time of something event-based. For example how long it took a unit to reach some specific region once quest started, etc.
But in your spell it does not make sense, because you already have a timer there: The 0.03 timer that starts your loop trigger. So instead of calculating elapsed game time, you can just decrement value in Abomination_Warning_Duration by 0.03 every time the loop trigger runs and check if value is less than or equal zero.
Ty kind sir :ogre_datass: That game time attempt was from before when i tried to compare the current game time with the game time of detecting... All makes sense now ty again :D
 
Another thing to note: Floating point error.

You have to be careful when checking for exact Real values, they're not reliable when working with Arithmetic. For instance, let's say that I have a real variable called MyReal. Now let's say that I set MyReal to 3.00. That'll work fine, assigning it an exact value has no issue:
  • Set Variable MyReal = 3.00
However, let's say that I have a trigger that periodically reduces MyReal:
  • Set Variable MyReal = (MyReal - 0.03)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • MyReal Less than or equal to 0.00
    • Then - Actions
      • -------- do something --------
    • Else - Actions
This is problematic and could be game breaking depending on the situation.

The "Less than" is smart though, it helps avoid the major issue of this failing to fire, but you may end up with results that are imprecise.

Instead, you can check if MyReal is <= 0.01. This will protect you from instances where it goes from 0.03 -> 0.00001, which happens fairly often. The same is true if you were adding to it and checking if it's >= 3.00, it could end up being 2.99999 instead. Adding Text Messages that display your Real values while testing these kinds of triggers will help shine a light on this issue.
 
Last edited:
Another thing to note: Floating point error.

Becareful when checking for exact Real values, they're not reliable when working with Arithmetic.

For instance, let's say that I have a real variable called MyReal.

Now let's say I set MyReal to 5.00. That'll work fine, assigning it an exact value has no issue:
  • Set Variable MyReal = 5.00
However, now let's say that I have a trigger that periodically reduces MyReal:
  • Set Variable MyReal = (MyReal - 0.03)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • MyReal Less than or equal to 0.00
    • Then - Actions
    • Else - Actions
This is problematic, although not necessary game breaking (but it could be).

The "Less than" is smart, it helps avoid the major issue of this failing to fire, but you're now also imprecise.

Instead, to retain some precision you can check if MyReal is <= 0.01. This will protect you from instances where it goes from 0.03 -> 0.0001, which happens fairly often. The same is true if you were adding to it and checking if it's >= 5.00, it could end up being 4.9999 instead.
Ty for pointing that out, I had simmilar thoughts about the issue, just didn't give it enough attention, So I supposed that by setting Less than or equal to rather than Equal to I could avoid the potential problem. Also in conversion I added +0.99 so that integer conversion never rounds down prematurely. For example :

  • AboAI Decisions
    • Events
      • Time - Every 2.00 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in AbominationGroup and do (Actions)
        • Loop - Actions
          • Set Abomination_TempUnit = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Abomination_TempUnit is alive) Equal to True
              • (Abomination_TempUnit is paused) Equal to False
            • Then - Actions
              • Set Abomination_TempPoint = (Position of Abomination_TempUnit)
              • Set Abomination_TempGroup = (Units within 1500.00 of Abomination_TempPoint matching ((((Matching unit) belongs to an enemy of (Owner of Abomination_TempUnit).) Equal to True) and ((((Matching unit) is alive) Equal to True) and ((((Matching unit) is A Hero) Equal to True) and (((Matching
              • Set Abomination_TempUnit2 = No unit
              • Set Abomination_TempReal = 1500.00
              • Unit Group - Pick every unit in Abomination_TempGroup and do (Actions)
                • Loop - Actions
                  • Set Abomination_TempPoint2 = (Position of (Picked unit))
                  • Set Abomination_TempReal2 = (Distance between Abomination_TempPoint and Abomination_TempPoint2)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_TempReal2 Less than Abomination_TempReal
                    • Then - Actions
                      • Set Abomination_TempReal = Abomination_TempReal2
                      • Set Abomination_TempUnit2 = (Picked unit)
                    • Else - Actions
                  • Custom script: call RemoveLocation( udg_Abomination_TempPoint2)
              • Custom script: call DestroyGroup( udg_Abomination_TempGroup)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Abomination_TempUnit2 Not equal to No unit
                • Then - Actions
                  • Set Abomination_Temp_Boolean[1] = False
                  • Set Abomination_Temp_Boolean[2] = False
                  • Set Abomination_Temp_Boolean[3] = False
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability Cooldown Remaining of Abomination_TempUnit for ability Fel Charge ..) Less than or equal to 0.00
                    • Then - Actions
                      • Set Abomination_Temp_Boolean[1] = True
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability Cooldown Remaining of Abomination_TempUnit for ability Earthshatter (Abomination)..) Less than or equal to 0.00
                    • Then - Actions
                      • Set Abomination_Temp_Boolean[3] = True
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability Cooldown Remaining of Abomination_TempUnit for ability |cffff0000Meat Hook|r (Abomination)..) Less than or equal to 0.00
                    • Then - Actions
                      • Set Abomination_Temp_Boolean[2] = True
                    • Else - Actions
                  • Set Abomination_TempInteger = 0
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_Temp_Boolean[1] Equal to True
                      • Abomination_TempReal Less than or equal to 1200.00
                    • Then - Actions
                      • Set Abomination_TempInteger = (Abomination_TempInteger + 1)
                      • Set Abomination_Eligible[Abomination_TempInteger] = 1
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_Temp_Boolean[2] Equal to True
                      • Abomination_TempReal Greater than or equal to 1200.00
                      • Abomination_TempReal Greater than 600.00
                    • Then - Actions
                      • Set Abomination_TempInteger = (Abomination_TempInteger + 1)
                      • Set Abomination_Eligible[Abomination_TempInteger] = 2
                    • Else - Actions
                  • Set Abomination_TempGroup2 = (Units within 400.00 of Abomination_TempPoint matching ((((Matching unit) is alive) Equal to True) and (((Matching unit) belongs to an enemy of (Owner of Abomination_TempUnit).) Equal to True)).)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_Temp_Boolean[3] Equal to True
                      • (Number of units in Abomination_TempGroup2) Greater than or equal to 3
                    • Then - Actions
                      • Set Abomination_TempInteger = (Abomination_TempInteger + 1)
                      • Set Abomination_Eligible[Abomination_TempInteger] = 3
                    • Else - Actions
                  • Custom script: call DestroyGroup( udg_Abomination_TempGroup2)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Abomination_TempInteger Greater than 0
                    • Then - Actions
                      • Set Abomination_TempReal2 = (Random real number between 1.00 and ((Real(Abomination_TempInteger)) + 0.99))
                      • Set Abomination_TempInteger2 = (Integer(Abomination_TempReal2))
                      • Set Abomination_TempInteger3 = Abomination_Eligible[Abomination_TempInteger2]
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Abomination_TempInteger3 Equal to 1
                        • Then - Actions
                          • Set Abomination_TempPoint2 = (Position of Abomination_TempUnit2)
                          • Unit - Order Abomination_TempUnit to Human Mortar Team - Flare Abomination_TempPoint2
                          • Custom script: call RemoveLocation( udg_Abomination_TempPoint2)
                        • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Abomination_TempInteger3 Equal to 2
                        • Then - Actions
                          • Unit - Order Abomination_TempUnit to Chain Lightning Abomination_TempUnit2
                        • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Abomination_TempInteger3 Equal to 3
                        • Then - Actions
                          • Unit - Order Abomination_TempUnit to Thunderclap.
                        • Else - Actions
                    • Else - Actions
                  • Custom script: call RemoveLocation( udg_Abomination_TempPoint)
                • Else - Actions
            • Else - Actions
Based on your suggestion, I'll change that to <= 0.01 to add a safety margin against floating point drift. Same for any other "countdown reaches zero" checks ... I'll use a small threshold rather than zero. Thanks for the heads-up! :ogre_datass:
 
Ty for pointing that out, I had simmilar thoughts about the issue, just didn't give it enough attention, So I supposed that by setting Less than or equal to rather than Equal to I could avoid the potential problem. Also in conversion I added +0.99 so that integer conversion never rounds down prematurely. For example :
I often use Integers instead of Reals, even if they're not as clear. This guarantees success. But yeah, a small epsilon of 0.01 will definitely protect against the drift.

When converting a Real to Integer, you may want to add 0.5 to manage the rounding, or do what others have suggested here:
 
Back
Top