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

[Trigger] Buff not being correctly removed (The Swarm ability from Dota)

Status
Not open for further replies.
Level 12
Joined
May 16, 2020
Messages
660
Hi guys,

I'm re-creating "The Swarm" ability from Dota 2 in GUI:

Weaver launches a swarm of 12 young Weavers that latch on any enemy unit in their path, attacking and reducing armor until it is killed.
  • Max Travel Distance: 3000
  • Beetles Spawn Radius: 300
  • Beetles Latch Radius: 100
  • Number of Beetles: 12
  • Tick Interval: 1.15/1.0/0.85/0.7
  • Damage per Tick: 18/20/22/24
  • Armor Reduction per Tick: 1
  • Beetle Duration: 16

For the spell I use 12 projectile units which I move across the map.
If they encounter an enemy within 100 range, I add this enemy to a group, reduce their armor and display a debuff via the tornado ability.
The problem is that sometimes enemy units retain their armor reduction and aura ability indefinitely.

I narrowed the problem down to the "The Swarm Loop Attack" trigger (all triggers are within the spoiler tag)
In the image below you can see that 10 units have been hit by the ability (every time a unit it supposed to be hit, I use Z_CounterBuff[Z_CV_Target] = (Z_CounterBuff[Z_CV_Target] + 1))
However, when cycling trough the enemies (for example when the parasite dies), only 9 units are found...

1643362723811.png



Can someone please help find the error?
If it helps, I can attach the map with the trigger.

  • The Swarm
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to The Swarm
    • Actions
      • Set VariableSet Z_Index = (Z_Index + 1)
      • Set VariableSet Z_Caster[Z_Index] = (Triggering unit)
      • Set VariableSet Z_PointInitial[Z_Index] = (Position of Z_Caster[Z_Index])
      • Set VariableSet Z_Point[1] = (Target point of ability being cast)
      • Set VariableSet Z_Angle[Z_Index] = (Angle from Z_PointInitial[Z_Index] to Z_Point[1])
      • Set VariableSet Z_Damage[Z_Index] = (16.00 + (2.00 x (Real((Level of The Swarm for Z_Caster[Z_Index])))))
      • Set VariableSet Z_Distance[Z_Index] = 0.00
      • Set VariableSet Z_Boolean_End[Z_Index] = False
      • -------- --------
      • Custom script: set udg_Z_GroupSwarm[udg_Z_Index] = CreateGroup()
      • Custom script: set udg_Z_GroupHit[udg_Z_Index] = CreateGroup()
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Z_Caster[Z_Index] belongs to an ally of Player 1 (Red).) Equal to True
        • Then - Actions
          • Set VariableSet Z_Player = Player 1 (Red)
        • Else - Actions
          • Set VariableSet Z_Player = Player 7 (Green)
      • For each (Integer Z_Integer_B) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Z_Point[2] = (Z_PointInitial[Z_Index] offset by (Random real number between 0.00 and 300.00) towards (Random angle) degrees.)
          • Unit - Create 1 Swarm Parasite for Z_Player at Z_Point[2] facing Z_Angle[Z_Index] degrees
          • Set VariableSet Z_Parasite = (Last created unit)
          • Unit - Change color of Z_Parasite to Black
          • Unit - Make Z_Parasite Invulnerable
          • Unit - Pause Z_Parasite
          • Unit Group - Add Z_Parasite to Z_GroupSwarm[Z_Index]
          • Custom script: call RemoveLocation (udg_Z_Point[2])
      • Custom script: call RemoveLocation (udg_Z_Point[1])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Z_Index Equal to 1
        • Then - Actions
          • Game - Display to (All players) the text: ON
          • Countdown Timer - Start Z_Timer_A as a Repeating timer that will expire in 0.03 seconds
          • Trigger - Turn on The Swarm Loop <gen>
        • Else - Actions
  • The Swarm Loop
    • Events
      • Time - Z_Timer_A expires
    • Conditions
    • Actions
      • For each (Integer Z_Integer_A) from 1 to Z_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Z_Boolean_End[Z_Integer_A] Equal to False
              • Z_Distance[Z_Integer_A] Less than 2700.00
            • Then - Actions
              • Set VariableSet Z_Distance[Z_Integer_A] = (Z_Distance[Z_Integer_A] + 22.50)
              • Set VariableSet Z_PointTemp = (Z_PointInitial[Z_Integer_A] offset by Z_Distance[Z_Integer_A] towards Z_Angle[Z_Integer_A] degrees.)
              • -------- --------
              • Set VariableSet Z_Pos_X = (X of Z_PointTemp)
              • Set VariableSet Z_Pos_Y = (Y of Z_PointTemp)
              • Custom script: set udg_Z_Boolean_Map = IsPointInMap(udg_Z_Pos_X, udg_Z_Pos_Y)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Z_Boolean_Map Equal to True
                • Then - Actions
                  • Unit Group - Pick every unit in Z_GroupSwarm[Z_Integer_A] and do (Actions)
                    • Loop - Actions
                      • Set VariableSet Z_Parasite = (Picked unit)
                      • Set VariableSet Z_Point[1] = (Position of Z_Parasite)
                      • Set VariableSet Z_Point[2] = (Z_Point[1] offset by 22.50 towards Z_Angle[Z_Integer_A] degrees.)
                      • Unit - Move Z_Parasite instantly to Z_Point[2]
                      • Custom script: set bj_wantDestroyGroup = true
                      • Unit Group - Pick every unit in (Units within 100.00 of Z_Point[2].) and do (Actions)
                        • Loop - Actions
                          • Set VariableSet Z_Unit = (Picked unit)
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Z_Unit is A structure) Equal to False
                              • (Z_Unit is alive) Equal to True
                              • (Z_Unit belongs to an enemy of (Owner of Z_Caster[Z_Integer_A]).) Equal to True
                              • (Z_Unit is in Z_GroupHit[Z_Integer_A].) Equal to False
                            • Then - Actions
                              • Unit Group - Add Z_Unit to Z_GroupHit[Z_Integer_A]
                              • Unit Group - Remove Z_Parasite from Z_GroupSwarm[Z_Integer_A].
                              • -------- --------
                              • Unit - Make Z_Parasite Vulnerable
                              • Unit - Unpause Z_Parasite
                              • Unit - Add a 16.00 second Generic expiration timer to Z_Parasite
                              • -------- --------
                              • Set VariableSet Z_CV_Parasite = (Custom value of Z_Parasite)
                              • Set VariableSet Z_Target[Z_CV_Parasite] = Z_Unit
                              • -------- --------
                              • Unit - Cause Z_Caster[Z_Integer_A] to damage Z_Unit, dealing Z_Damage[Z_Integer_A] damage of attack type Normal and damage type Normal
                              • Set VariableSet Z_Counter[Z_CV_Parasite] = 1
                              • Set VariableSet Z_Modulo[Z_CV_Parasite] = (11 + (3 x (Level of The Swarm for Z_Caster[Z_Integer_A])))
                              • Set VariableSet Z_DamageAttack[Z_CV_Parasite] = Z_Damage[Z_Integer_A]
                              • Set VariableSet Z_Armor[Z_CV_Parasite] = 1
                              • Custom script: call AddUnitBonus(udg_Z_Unit, BONUS_ARMOR, -1)
                              • -------- --------
                              • Set VariableSet Z_CV_Target = (Custom value of Z_Unit)
                              • Set VariableSet Z_CounterBuff[Z_CV_Target] = (Z_CounterBuff[Z_CV_Target] + 1)
                              • Game - Display to (All players) the text: (String(Z_CounterBuff[Z_CV_Target]))
                              • Unit - Add The Swarm (Undispellable) to Z_Unit
                              • -------- --------
                              • Set VariableSet Z_CV_Caster = (Custom value of Z_Caster[Z_Integer_A])
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (Number of units in Z_GroupAttack[Z_CV_Caster]) Equal to 0
                                • Then - Actions
                                  • Custom script: set udg_Z_GroupAttack[udg_Z_CV_Caster] = CreateGroup()
                                • Else - Actions
                              • Unit Group - Add Z_Parasite to Z_GroupAttack[Z_CV_Caster]
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (Z_Caster[Z_Integer_A] is in Z_GroupCaster.) Equal to False
                                • Then - Actions
                                  • Unit Group - Add Z_Caster[Z_Integer_A] to Z_GroupCaster
                                • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (The Swarm Loop Attack <gen> is on) Equal to False
                                • Then - Actions
                                  • Countdown Timer - Start Z_Timer_B as a Repeating timer that will expire in 0.05 seconds
                                  • Trigger - Turn on The Swarm Attack <gen>
                                  • Trigger - Turn on The Swarm Loop Attack <gen>
                                • Else - Actions
                            • Else - Actions
                      • Custom script: call RemoveLocation (udg_Z_Point[1])
                      • Custom script: call RemoveLocation (udg_Z_Point[2])
                • Else - Actions
                  • -------- OUTSIDE WORLD BOUNDS --------
                  • Set VariableSet Z_Boolean_End[Z_Integer_A] = True
              • Custom script: call RemoveLocation (udg_Z_PointTemp)
            • Else - Actions
              • Unit Group - Pick every unit in Z_GroupSwarm[Z_Integer_A] and do (Actions)
                • Loop - Actions
                  • Unit - Remove (Picked unit) from the game
              • -------- --------
              • Custom script: call DestroyGroup(udg_Z_GroupHit[udg_Z_Integer_A])
              • Custom script: call DestroyGroup(udg_Z_GroupSwarm[udg_Z_Integer_A])
              • Custom script: call RemoveLocation (udg_Z_PointInitial[udg_Z_Integer_A])
              • -------- --------
              • Set VariableSet Z_Caster[Z_Integer_A] = Z_Caster[Z_Index]
              • Set VariableSet Z_PointInitial[Z_Integer_A] = Z_PointInitial[Z_Index]
              • Set VariableSet Z_Angle[Z_Integer_A] = Z_Angle[Z_Index]
              • Set VariableSet Z_Damage[Z_Integer_A] = Z_Damage[Z_Index]
              • Set VariableSet Z_Distance[Z_Integer_A] = Z_Distance[Z_Index]
              • Set VariableSet Z_Boolean_End[Z_Integer_A] = Z_Boolean_End[Z_Index]
              • Set VariableSet Z_GroupHit[Z_Integer_A] = Z_GroupHit[Z_Index]
              • Set VariableSet Z_GroupSwarm[Z_Integer_A] = Z_GroupSwarm[Z_Index]
              • -------- --------
              • Set VariableSet Z_Index = (Z_Index - 1)
              • Set VariableSet Z_Integer_A = (Z_Integer_A - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Z_Index Equal to 0
                • Then - Actions
                  • Game - Display to (All players) the text: OFF
                  • Countdown Timer - Pause Z_Timer_A
                  • Trigger - Turn off (This trigger)
                • Else - Actions
  • The Swarm Loop Attack
    • Events
      • Time - Z_Timer_B expires
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Z_GroupCaster and do (Actions)
        • Loop - Actions
          • Set VariableSet Z_Unit = (Picked unit)
          • Set VariableSet Z_CV_Caster = (Custom value of Z_Unit)
          • -------- --------
          • Unit Group - Pick every unit in Z_GroupAttack[Z_CV_Caster] and do (Actions)
            • Loop - Actions
              • Set VariableSet Z_Parasite = (Picked unit)
              • Set VariableSet Z_CV_Parasite = (Custom value of Z_Parasite)
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Z_Target[Z_CV_Parasite] is alive) Equal to True
                  • (Z_Parasite is alive) Equal to True
                • Then - Actions
                  • Set VariableSet Z_PointTemp = (Position of Z_Target[Z_CV_Parasite])
                  • Unit - Move Z_Parasite instantly to Z_PointTemp
                  • Custom script: call RemoveLocation (udg_Z_PointTemp)
                  • Set VariableSet Z_Counter[Z_CV_Parasite] = (Z_Counter[Z_CV_Parasite] + 1)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Z_Counter[Z_CV_Parasite] mod Z_Modulo[Z_CV_Parasite]) Equal to 0
                    • Then - Actions
                      • Set VariableSet Z_Armor[Z_CV_Parasite] = (Z_Armor[Z_CV_Parasite] + 1)
                      • Unit - Cause Z_Unit to damage Z_Target[Z_CV_Parasite], dealing Z_DamageAttack[Z_CV_Parasite] damage of attack type Normal and damage type Normal
                      • Custom script: call AddUnitBonus(udg_Z_Target[udg_Z_CV_Parasite], BONUS_ARMOR, -1)
                    • Else - Actions
                • Else - Actions
                  • Custom script: call AddUnitBonus(udg_Z_Target[udg_Z_CV_Parasite], BONUS_ARMOR, udg_Z_Armor[udg_Z_CV_Parasite])
                  • Set VariableSet Z_CV_Target = (Custom value of Z_Target[Z_CV_Parasite])
                  • Set VariableSet Z_CounterBuff[Z_CV_Target] = (Z_CounterBuff[Z_CV_Target] - 1)
                  • Game - Display to (All players) the text: (String(Z_CounterBuff[Z_CV_Target]))
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Z_CounterBuff[Z_CV_Target] Equal to 0
                    • Then - Actions
                      • Unit - Remove The Swarm (Undispellable) from Z_Target[Z_CV_Parasite]
                      • Unit - Remove The Swarm (Undispellable) buff from Z_Target[Z_CV_Parasite]
                    • Else - Actions
                  • Unit Group - Remove Z_Parasite from Z_GroupAttack[Z_CV_Caster].
                  • Unit - Remove Z_Parasite from the game
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in Z_GroupAttack[Z_CV_Caster]) Equal to 0
                    • Then - Actions
                      • Unit Group - Remove Z_Unit from Z_GroupCaster.
                      • Custom script: call DestroyGroup (udg_Z_GroupAttack[udg_Z_CV_Caster])
                    • Else - Actions
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in Z_GroupCaster) Equal to 0
        • Then - Actions
          • Trigger - Turn off The Swarm Attack <gen>
          • Trigger - Turn off (This trigger)
        • Else - Actions

I didn't list the "The Swarm Attack" trigger, since it's just used to ensure heroes/units require 4/8 attacks to kill the parasite.


Edit: BTW, do I still need the "is the unit in the map boundary" check in 2022?
 
Last edited:
I took a quick look at it and it's hard for me to see something obviously wrong.
However, have you figured out if it is the first unit hit, or last unit hit, that always bugs out?

It seems likely to be an "off-by-one" error and if you know that it's the "last unit" (for example), then it's easier to try to figure out why.

--

Also, I can't see where you add units to "Z_GroupCaster", so maybe you are missing some part of some trigger?
A unit group can only hold one unit once, so if you add same unit multiple times, and remove it once, it will still get removed as far as I know. That's why I looked at that group.

Does the bug only happen if you cast it multiple times with the same unit "quickly"?
 
Level 12
Joined
May 16, 2020
Messages
660
However, have you figured out if it is the first unit hit, or last unit hit, that always bugs out?
Does the bug only happen if you cast it multiple times with the same unit "quickly"?

I did some more testing. I didn't find a consistent way to replicate the bug nor any logic in which order it happens for units that are hit. It also doesn't matter how quick I cast the spell repeatedly.

But the bug happens when there is NO parasite attached to the victim.
So, what I observe:
  • The enemy takes the first tick of damage
  • The enemy gets -1 armor and the debuff
  • BUT no parasite is allocated to the enemy...
It seems the "The Swarm Loop" trigger correctly recognizes a viable enemy with "Pick every unit in 100 range" and subsequent unit check, but doesn't find the respective parasite. If indeed no parasite is allocated to the enemy, then of course Set VariableSet Z_CV_Parasite = (Custom value of Z_Parasite) will return no value, and hence the subsequent "The Swarm Loop Attack" trigger will fail...

But no idea why that happens.........

Also, I can't see where you add units to "Z_GroupCaster", so maybe you are missing some part of some trigger?
It's a bit in the middle of "The Swarm Loop" trigger:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Z_Caster[Z_Integer_A] is in Z_GroupCaster.) Equal to False
    • Then - Actions
      • Unit Group - Add Z_Caster[Z_Integer_A] to Z_GroupCaster
    • Else - Actions
Attached the map, if you want to test. The hero is in the middle of the map. I recommend casting it on the pink soldiers (a bit top right) first, kill a soldier with the parasite, and then cast the ability on the "Damage Testers" until the bug appears.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
It's a real pain launching and navigating your map, it'd be nice if you had a test map for these sort of things.

Anyway, does this ability stack in DotA 2? Can a unit have multiple beetles latched onto it at once? Also, the beetles shouldn't be visible/selectable until they actually latch onto something. It's just a special effect until it hits the unit.

Also, from what I can gather the bug happens when you have multiple casts running at once. I couldn't recreate it otherwise.
 
Level 12
Joined
May 16, 2020
Messages
660
It's a real pain launching and navigating your map, it'd be nice if you had a test map for these sort of things.
Yeah, I think it's time that I create a separate map to test stuff.. it takes always too long to load and test currently lol

Anyway, does this ability stack in DotA 2? Can a unit have multiple beetles latched onto it at once? Also, the beetles shouldn't be visible/selectable until they actually latch onto something. It's just a special effect until it hits the unit.
No, I read the description again and it shouldn't stack.
But the beetles do provide vision while travelling, so I will need them to stay as units while travelling (not just special effects).

Also, from what I can gather the bug happens when you have multiple casts running at once. I couldn't recreate it otherwise.
I did have cases where I cast it only once and the bug happened - but it's rare and I didn't find any logic to it.
Let me make it non-stackable and then test again.. maybe this fixes it.
Or did you see anything that would cause the bug in certain situations?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
Yeah, I think it's time that I create a separate map to test stuff.. it takes always too long to load and test currently lol


No, I read the description again and it shouldn't stack.
But the beetles do provide vision while travelling, so I will need them to stay as units while travelling (not just special effects).


I did have cases where I cast it only once and the bug happened - but it's rare and I didn't find any logic to it.
Let me make it non-stackable and then test again.. maybe this fixes it.
Or did you see anything that would cause the bug in certain situations?
You don't need them to stay as units, just trigger the vision or add a Dummy unit that moves along with the swarm to provide the vision.

But no, I didn't see any noticeable issues, but it's hard when you're reading a wall of text you didn't write yourself :p
 
Will update this when I have the testmap ready!


What do you mean with this? Is there a way to create vision across a specific range of a point without a unit?
I only know the one which gives visibility of a specific enemy (I think that's basically sharing the vision).
You can trigger vision using the following triggers (found under "Visibility - X")
  • visibility
    • Events
    • Conditions
    • Actions
      • Visibility - Create an initially Enabled visibility modifier for Player 1 (Red) emitting Visibility from (Center of (Playable map area)) to a radius of 512.00
      • Set visibility_modifier = (Last created visibility modifier)
      • Visibility - Enable visibility_modifier
      • Visibility - Disable visibility_modifier
      • Visibility - Destroy visibility_modifier
 
Level 12
Joined
May 16, 2020
Messages
660
Attached the test map.
I can still replicate the bug there, without casting the ability multiple times on the same enemies or in fast succession.
The bug appears more often when casting the spell on the soldiers, not sure why.

Regarding using 12 special effects instead of 12 units when starting the spell, would you guys use a hashtable? (Parent = index, Child = 1 to 12?)
Or is there a simpler method?
 

Attachments

  • Land of Legends Test Map - Weaver.w3m
    442.3 KB · Views: 7

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
Attached the test map.
I can still replicate the bug there, without casting the ability multiple times on the same enemies or in fast succession.
The bug appears more often when casting the spell on the soldiers, not sure why.

Regarding using 12 special effects instead of 12 units when starting the spell, would you guys use a hashtable? (Parent = index, Child = 1 to 12?)
Or is there a simpler method?
On second thought, you could give the Swarm units the Locust ability and then remove it upon contact with an enemy.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
But it's better to create 12 SFX, no?
(as I read that "create - unit" is the most resource intensive action)
If I can limit the unit creation to only the ones that are needed, then I would rather do this.

Separate question: Do you know if I still need the "is the unit in the map boundary" check for projectiles?
I read that if a unit it moved to out-of-bounds, it can cause crashes. But I never experienced this when testing. Maybe that has been fixed in some patch?
 
Status
Not open for further replies.
Top