• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

How can I stop this trigger from being abused?

Status
Not open for further replies.
Level 6
Joined
Mar 9, 2023
Messages
75
Hi! I have an enemy which uses the hydra ability to split into copies. Once it reaches a certain amount, they fuse into a larger enemy. Problem is that abilities that travel and hits multiple times will severely abuse it. What happens is that too many manage to spawn before it procs.

  • Smaile
    • Events
      • Time - Every 2.00 seconds of game time
    • Conditions
      • (Number of units in (Units in (Playable map area) matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r))) Greater than or equal to (Players x 7)
    • Actions
      • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
      • Set VariableSet SlimeGroup = (Units of type |c0000FF00[Slush]|r)
      • Unit Group - Pick every unit in SlimeGroup and do (Actions)
        • Loop - Actions
          • Unit - Remove (Picked unit) from the game
          • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
          • Special Effect - Destroy (Last created special effect)
      • Custom script: call DestroyGroup(udg_SlimeGroup)
      • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
      • Custom script: call RemoveLocation(udg_SlimeLocation)
My initial thought is to make it a unit-dies event, but performance is a worry. How can I improve this trigger? What is the ideal solution?

Edit: Here's the final trigger after getting help.
  • SlimeFuse
    • Events
      • Unit - A unit enters BOSSARENA <gen>
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |c0000FF00[Slush]|r
    • Actions
      • Set VariableSet SlushCounter = (SlushCounter + 1)
      • Unit Group - Add (Triggering unit) to SlushGroup
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SlushCounter Greater than or equal to (Players x 7)
        • Then - Actions
          • Set VariableSet TempGroup = (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r))
          • Unit Group - Add all units of TempGroup to SlushGroup
          • Custom script: call DestroyGroup(udg_TempGroup)
          • Unit Group - Pick every unit in SlushGroup and do (Actions)
            • Loop - Actions
              • Set VariableSet TempPoint = (Position of (Picked unit))
              • Unit - Remove (Picked unit) from the game
              • Special Effect - Create a special effect at TempPoint using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
              • Special Effect - Destroy (Last created special effect)
              • Custom script: call RemoveLocation(udg_TempPoint)
          • Unit Group - Remove all units from SlushGroup.
          • Set VariableSet SlushCounter = 0
          • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
          • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
          • Custom script: call RemoveLocation(udg_SlimeLocation)
        • Else - Actions
and
  • SlimeDie
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |c0000FF00[Slush]|r
    • Actions
      • Set VariableSet SlushCounter = (SlushCounter - 1)
      • Unit Group - Remove (Triggering unit) from SlushGroup.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I would use a more Event based approach:
  • Events
    • Unit - A unit enters playable map area
  • Conditions
    • Unit-Type of (Triggering unit) Equal to Slush
  • Actions
    • Set Variable SlushCounter = SlushCounter + 1
    • Unit Group - Add (Triggering unit) to SlushGroup
    • If then else
      • If - Conditions
        • SlushCounter Greater than or equal to (Players x 7)
      • Then - Actions
        • Unit Group - Pick every unit in SlushGroup and do (Actions)
          • Loop - Actions
            • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
            • Special Effect - Destroy (Last created special effect)
            • Unit - Remove (Picked unit) from the game
        • Unit Group - Remove all units from SlushGroup
        • Set Variable SlushCounter = 0
        • Set Variable SlimeLocation = ...
        • Unit - Create 1 Slime at SlimeLocation
        • Custom script: call RemoveLocation(udg_SlimeLocation)
      • Else - Actions
  • Events
    • Unit - A unit dies
  • Conditions
    • Unit-Type of (Triggering unit) Equal to Slush
  • Actions
    • Set Variable SlushCounter = SlushCounter - 1
    • Unit Group - Remove (Triggering unit) from SlushGroup
Your trigger picks EVERY Slush in in the map regardless of how many there are. So it's not creating 1 Slime per (Players x 7) Slushes, instead it's creating 1 Slime IF there's at least (Players x 7) Slushes.

Also, this line leaks a Unit Group:
  • (Number of units in (Units in (Playable map area) matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r))) Greater than or equal to (Players x 7)
It creates a Unit Group filled with whatever units match the conditions. It then counts the number of units in said Unit Group and gives you that number. Unfortunately, the Unit Group isn't destroyed (I'm not sure why Blizzard didn't destroy it). It's also an expensive task to keep recreating these Unit Groups as the game has to test every single Unit in the map to determine whether they belong in it or not. If you're concerned with performance than this Unit Group method is a big red flag!
 
Last edited:
Level 6
Joined
Mar 9, 2023
Messages
75
Thanks for the response @Uncle
The solution looks great, and I had no idea about how much the condition was leaking!
Your trigger picks EVERY Slush in in the map regardless of how many there are. So it's not creating 1 Slime per (Players x 7) Slushes, instead it's creating 1 Slime IF there's at least (Players x 7) Slushes.

That's exactly the what the function is supposed to do. It's a really big slime ;)
 
Level 14
Joined
Jan 10, 2023
Messages
247
That's exactly the what the function is supposed to do. It's a really big slime ;)

What Uncle is saying is that because you were checking how many slimes there were every 2.00 seconds, it was possible that you reached 35 slimes right after one of the 2.00 sec intervals, for an example, lets say 40 slimes were spawned before the next 2.00 sec interval expired.

Your function would take all slimes that exist (40 slimes) and remove them, instead of 35 slimes turning into a big slime.

Not sure if you allowed two big slimes to be made, but IF that was in the cards, it would have taken 5 slimes away from the second mega-slime and it would have taken longer
OR if you were going to only do one boss, it would seemed like it required more than 35 slimes, but at least 35 (the error it seemed you were describing).
 
Level 6
Joined
Mar 9, 2023
Messages
75
What Uncle is saying is that because you were checking how many slimes there were every 2.00 seconds, it was possible that you reached 35 slimes right after one of the 2.00 sec intervals, for an example, lets say 40 slimes were spawned before the next 2.00 sec interval expired.

Your function would take all slimes that exist (40 slimes) and remove them, instead of 35 slimes turning into a big slime.

Not sure if you allowed two big slimes to be made, but IF that was in the cards, it would have taken 5 slimes away from the second mega-slime and it would have taken longer
OR if you were going to only do one boss, it would seemed like it required more than 35 slimes, but at least 35 (the error it seemed you were describing).
I see. That is the intention, to remove all smal and create a large one in its stead. After that, no more will appear.

It seems as if there was a bit of miscommunication, so the offered solution didn't quite work as intended.
 
Level 14
Joined
Jan 10, 2023
Messages
247
Gotcha, so from reading, Uncle's system looked great to me and wouldn't seem to overspawn slush because that event should trigger right when the 35th is made.

Is it too fast so that you only see 34 slushes before the change? if so, make it happen at 36 slushes.

If all else fails, you could treat the trigger like a "create unit action".

When you to creat a slush/maybe a slime, use the trigger action:
  • Trigger - Run trigger (checking conditions)
on this modified version of Uncle's trigger:

  • Events
    • (no events needed, it will be executed/run as part of another trigger's action)
  • Conditions
    • (no conditions needed, but you could use them)
  • Actions
    • If then else
      • If - Conditions
        • Number of units in (SlimeGroup) Greater than or equal to (Players x 7)
      • Then - Actions
        • Unit Group - Pick every unit in SlushGroup and do (Actions)
          • Loop - Actions
            • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
            • Special Effect - Destroy (Last created special effect)
            • Unit - Remove (Picked unit) from the game
        • Unit Group - Remove all units from SlushGroup
        • Set Variable SlimeLocation = ...
        • Unit - Create 1 Slime at SlimeLocation
        • Custom script: call RemoveLocation(udg_SlimeLocation)
      • Else - Actions
        • Set Variable SlushLocation = ...
        • Unit - Create 1 Slush at SlushLocation
        • Unit Group - Add (Last created unit) to SlushGroup
        • Custom script: call RemoveLocation(udg_SlushLocation)
I switched it back to counting the number of units in a group because if it isn't a condition, or as long as the group has a reference (a global variable in this case) you don't have to worry about leaking.

The problem is only if you MAKE a group in a Condition, but it's fine to refer to an existing group.

Keep the following trigger that Uncle shared, even if you do the "Run Trigger" option I suggested, but if you start using the "number of units in Slime Group" condition again, remove the line
  • Set Variable SlushCounter = SlushCounter - 1
(it won't be necessary if you are not leaking groups).
  • Events
    • Unit - A unit dies
  • Conditions
    • Unit-Type of (Triggering unit) Equal to Slush
  • Actions
    • Set Variable SlushCounter = SlushCounter - 1
    • Unit Group - Remove (Triggering unit) from SlushGroup

If all of that has nothing to do with the current trouble/bug, let us know what's going on.

PS: I did want to add, you were leaking locations like crazy, but Uncle quietly fixed it since the group leak was more prevalent. Think of locations as objects (they are objects) every time you make one, you made a thing, if you create a location called 'vegas' and then create another location called 'vegas' we can no longer refer to the first 'vegas' to remove it. So either:
1. give the old one a new name before making a new 'vegas', ("set oldVegas = vegas" then make a new 'vegas)
2. give the new one a new name, not the old name (create location 'vegas', then create a new location called 'newVegas').

In both of those cases you can still refer to both, OR

3. Remove (delete) 'vegas' before making a new 'vegas', if you don't need old 'vegas' anymore.
 
Last edited:
Level 6
Joined
Mar 9, 2023
Messages
75
Clever alternative, thanks! I've gotten to think a lot.

Here is my updated trigger:
  • SlimeFuse
    • Events
      • Unit - A unit enters BOSSARENA <gen>
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |c0000FF00[Slush]|r
    • Actions
      • Set VariableSet SlushCounter = (SlushCounter + 1)
      • Unit Group - Add (Triggering unit) to SlushGroup
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SlushCounter Greater than or equal to (Players x 7)
        • Then - Actions
          • Unit Group - Add all units of (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r)) to SlushGroup
          • Unit Group - Pick every unit in SlushGroup and do (Actions)
            • Loop - Actions
              • Unit - Remove (Picked unit) from the game
              • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
              • Special Effect - Destroy (Last created special effect)
          • Unit Group - Remove all units of SlushGroup from SlushGroup.
          • Set VariableSet SlushCounter = 0
          • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
          • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
          • Custom script: call RemoveLocation(udg_SlimeLocation)
        • Else - Actions
What happened with Uncle's trigger was that it didn't catch all Slush, The results by adding
  • Unit Group - Add all units of (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r)) to SlushGroup
made it so that they are caught, and so far it hasn't lagged during my testing. But here I'm worried:

The problem is only if you MAKE a group in a Condition, but it's fine to refer to an existing group.

Am I not creating a new group with that action, and putting that group in a variable?

Also PS, so that I can learn: why does my initial trigger leak locations? Is it because of the variable or condition?
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
This leaks a Unit Group, same issue as before:
  • Unit Group - Add all units of (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r)) to SlushGroup
In order for the game to reference (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r)) it needs to do the following:

1) Create a new Unit Group object.
2) Check each Unit in the game and see if it matches your conditions. In this case the unit needs to be a Slush.
3) Add the matching Units to the Unit Group.
4) Return the new Unit Group to either the function that created it or to a variable that's now tracking it.

The issue is that the function (Add all units of group x to group y) doesn't destroy the Unit Group that it creates and as a result it creates a memory leak. However, if you were to Set a variable to this newly created Unit Group first, THEN reference it, you would have the option to destroy it yourself afterwards thus avoiding the memory leak.

Here's the fix:
  • Set Variable TempGroup = (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r))
  • Unit Group - Add all units of TempGroup to SlushGroup
  • Custom script: call DestroyGroup(udg_TempGroup)

But I'm a little confused, how does the "A slush enters the map" trigger not catch all of the Slushes? Are some preplaced? If so, just add those manually at the start of the game.

Or is it that multiple Slush spawn at once and the Event fires once for each of them with the potential of skipping some of the later ones? In that case you can use a Timer to delay the removal process until all of them have spawned:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SlushCounter Greater than or equal to (Players x 7)
    • Then - Actions
      • Countdown Timer - Start SlushRemoval as a one-shot timer that will expire in 0.00 seconds
    • Else - Actions
Timers that are already running will restart if you try to Start them again. So this will result in the Timer expiring one frame (0.00 seconds) after the very last Slush has arrived.

You can then make a new trigger which detects when this Timer expires and then handle the Removal/Creation process there.
  • Events
    • Time - SlushRemoval expires
  • Conditions
  • Actions
    • Unit Group - Pick every unit in SlushGroup and do (Actions)
      • Loop - Actions
        • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
        • Special Effect - Destroy (Last created special effect)
        • Unit - Remove (Picked unit) from the game
    • Unit Group - Remove all units from SlushGroup
    • Set VariableSet SlushCounter = 0
    • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
    • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
    • Custom script: call RemoveLocation(udg_SlimeLocation)

Also, that's not the proper way to clear a Unit Group. That Action is for removing units in X group from Y group. You just need to remove all units from X group.
  • Unit Group - Remove all units from SlushGroup
I believe it's called Clear Units or something along those lines.
 
Last edited:
Level 6
Joined
Mar 9, 2023
Messages
75
Hm, alright I get group issue. I also got the Clear Units now, thanks.



But I'm a little confused, how does the "A slush enters the map" trigger not catch all of the Slushes? Are some preplaced? If so, just add those manually at the start of the game.

So the Hydra ability spawns 2 Slush whenever 1 die. The first Slushs spawn in, so the trigger should calculate all of them.

I followed your recommendation and created these two triggers:
  • SlimeFuse
    • Events
      • Unit - A unit enters BOSSARENA <gen>
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |c0000FF00[Slush]|r
    • Actions
      • Set VariableSet SlushCounter = (SlushCounter + 1)
      • Unit Group - Add (Triggering unit) to SlushGroup
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SlushCounter Greater than or equal to (Players x 7)
        • Then - Actions
          • Unit Group - Pick every unit in SlushGroup and do (Actions)
            • Loop - Actions
              • Unit - Remove (Picked unit) from the game
              • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
              • Special Effect - Destroy (Last created special effect)
          • Unit Group - Remove all units of SlushGroup from SlushGroup.
          • Set VariableSet SlushCounter = 0
          • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
          • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
          • Custom script: call RemoveLocation(udg_SlimeLocation)
        • Else - Actions
  • SlimeDie
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |c0000FF00[Slush]|r
    • Actions
      • Set VariableSet SlushCounter = (SlushCounter - 1)
      • Unit Group - Remove (Triggering unit) from SlushGroup.
I could obviously have missed something, if it's supposed to work perfectly. What happened was that 5~ Slimes (big fused unit) spawned, and it still had multiple Slush (small units) remaining on the map. Lag obviously occurred.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I've updated my last post with what should be a proper solution.

Also, to answer one of the questions I missed.

This line leaks a Point (location):
  • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
Similar to Unit Groups, the game needs to create a Point object in order to get the "Position of a unit". The problem is that it never destroys this Point (aka Location) so unfortunately we're left with clean up duty.

Here's the fix:
  • Set Variable TempPoint = (Position of (Picked unit))
  • Special Effect - Create a special effect at TempPoint using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
  • Custom script: call RemoveLocation(udg_TempPoint)

Note that Units in Warcraft 3 are always standing at a pair of x/y coordinates on what is considered the game grid. This information is accessible at all times and is stored directly to the Unit. However, working with x/y coordinates can be a little awkward so in GUI (the basic trigger editor) we rely on Points instead.

Points are an object that contains both an X and Y coordinate.

These Point objects allow you to do this:
  • Special Effect - Create a special effect at (Position of (Picked unit)) using...
Instead of relying on coordinates like this:
  • Special Effect - Create a special effect at (X coordinate of (Picked unit)) and (Y coordinate of (Picked unit)) using...
The first is easier to understand and work with because it has combined the two separate pieces of data (x/y) into a single piece of data (Point). In other words, Points only exist to help simplify things. If you ever get into Warcraft 3 coding then you'll see that there is often both a coordinate and Point version of many functions. These coordinate functions are objectively better than their Point counterparts as they go straight to the source rather than adding extra steps.
 
Last edited:
Level 14
Joined
Jan 10, 2023
Messages
247
I'm also confused by the map entering event isn't catching all of the slushes, but I strongly believe Uncle's timer suggestion is the way to go.

I'm afraid that the Unit is being added to the group but isn't registering as a member of the group until AFTER the trigger picks every unit in the group for some reason. The only other thing I can think (shouldn't happen in this case using 'triggering unit') is that when the new Slime is made, it enters the region and messes up the trigger event natives like can happen with dummy casting abilities in the actions of triggers that are using casting events...

Here's the fix:
  • Set Variable TempGroup = (Units in BOSSARENA <gen> matching ((Unit-type of (Matching unit)) Equal to |c0000FF00[Slush]|r))
  • Unit Group - Add all units of TempGroup to SlushGroup
  • Custom script: call DestroyGroup(udg_TempGroup)

But I'm a little confused, how does the "A slush enters the map" trigger not catch all of the Slushes?
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SlushCounter Greater than or equal to (Players x 7)
    • Then - Actions
      • Countdown Timer - Start SlushRemoval as a one-shot timer that will expire in 0.00 seconds
    • Else - Actions
  • Events
    • Time - SlushRemoval expires
  • Conditions
  • Actions
    • Unit Group - Pick every unit in SlushGroup and do (Actions)
      • Loop - Actions
        • Special Effect - Create a special effect at (Position of (Picked unit)) using Abilities\Spells\Undead\DeathandDecay\DeathandDecayTarget.mdl
        • Special Effect - Destroy (Last created special effect)
        • Unit - Remove (Picked unit) from the game
    • Unit Group - Remove all units from SlushGroup
    • Set VariableSet SlushCounter = 0
    • Set VariableSet SlimeLocation = (Center of BOSSARENA <gen>)
    • Unit - Create 1 |c008C0000[Slime]|r for Player 12 (Brown) at SlimeLocation facing Default building facing degrees
    • Custom script: call RemoveLocation(udg_SlimeLocation)

To speak to what you were doing that was leaking points, (I'm wondering if you updated your post or if I was just remembering wrong.. if I'm remembering wrong that explains why Uncle said nothing lol) your latest trigger is not leaking points, but your original trigger (I thought) was making a location in the first line and then not using it, and then again making locations in the for-group-loop.

Again, I think I may have been totally mistaken now because I don't see you making locations in the for-group-loop.

What it would have been is a case of making a location/point, then making another one and not keeping a reference to the first location because they used the same name so making the second one stole the first one's reference.

Not sure what I was smoking... let me know (for my sanity's sake) if that was ever something that was later edited or if I'm just crazy lol.
 
Level 6
Joined
Mar 9, 2023
Messages
75
I used a location for the Spawn, but not for the special effect. I edited my post to add the updated trigger, but the original trigger is still there. In the updated trigger, I use locations in the loop. No locations in the loop in the first post.

Understandable enough? :p
 
Status
Not open for further replies.
Top