• 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.

[Solved] I just came across an issue I have missed all these years

Status
Not open for further replies.
Level 9
Joined
Jun 13, 2010
Messages
365
Hi guys

So I may just have realized why all my maps have always been a bit bugged. I have always used Temp_Variables. Both for units, integers, points e.g.

As an example I have these 2 triggers:

Trigger 1:
When an enemy hits the Hero, there will be created a dummy unit to make an attack animation while the Hero deals instant damage to the target.

  • Demon Hunter Counter Attack
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Demon Hunter
      • (Level of Demon Hunter R for (Triggering unit)) Greater than or equal to 1
      • (Owner of (Attacking unit)) Equal to Player 12 (Brown)
    • Actions
      • Set Unit = (Triggering unit)
      • Set Unit1 = (Attacking unit)
      • Set Integer = (Level of Demon Hunter R for Unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Integer Equal to 1
        • Then - Actions
          • Unit - Cause Unit to damage Unit1, dealing 50.00 damage of attack type Normal and damage type Normal
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Integer Equal to 2
        • Then - Actions
          • Unit - Cause Unit to damage Unit1, dealing 100.00 damage of attack type Normal and damage type Normal
        • Else - Actions
      • Set PointDefault = (Position of Unit1)
      • Unit - Create 1 Dummy Unit (Demon Hunter) for (Owner of Unit) at PointDefault facing PointDefault
      • Custom script: call RemoveLocation (udg_PointDefault)
      • Set Unit2 = (Last created unit)
      • Unit - Add a 0.40 second Generic expiration timer to Unit2
      • Set PointDefault = (Position of Unit1)
      • Unit - Make Unit2 face Unit1 over 0.00 seconds
      • Set Integer = (Random integer number between 1 and 3)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Integer Equal to 1
        • Then - Actions
          • Animation - Play Unit2's attack animation
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Integer Equal to 2
        • Then - Actions
          • Animation - Play Unit2's attack 2 animation
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Integer Equal to 3
        • Then - Actions
          • Animation - Play Unit2's spell throw animation
        • Else - Actions

Trigger 2:
This trigger is for an enemy units ability. When it is attacked, it will blink to the attacking unit.

  • Area 1 Wave 2 Abilities
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Attacking unit)) Equal to Maiden of Pain
          • (Unit-type of (Attacking unit)) Equal to Overlord
          • (Unit-type of (Attacking unit)) Equal to Demon Spawn
          • (Unit-type of (Triggering unit)) Equal to Faceless Demon
          • (Unit-type of (Attacking unit)) Equal to Ghost
    • Actions
      • Set Unit = (Attacking unit)
      • Set Unit1 = (Triggering unit)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit1) Equal to Faceless Demon
          • (Mana of Unit1) Greater than or equal to 10.00
        • Then - Actions
          • Set PointDefault = (Position of Unit)
          • Unit - Make Unit1 face Unit over 0.00 seconds
          • Unit - Order Unit1 to Night Elf Warden - Blink PointDefault
          • Custom script: call RemoveLocation (udg_PointDefault)
        • Else - Actions
      • Set Unit = No unit
      • Set Unit1 = No unit

So what I discovered was, all of a sudden my Hero, when it was attacking, it created a dummy unit to attack the attacking unit, but the dummy unit was owned by Player 12. It really should be owned by the owner of Unit, which in trigger 1 is the Hero having the ability.
So what I am curious about is:
When I set my Unit variable in a trigger, is it possible it does not only work for this individual trigger, but will also apply and affect other triggers which shares an identical event?
What I mean: The Unit variable set from trigger 2, becomes the responding unit to trigger 1?

Additionally, in another topic. Trigger 2, which is originally as shown below, causing the enemy units to freeze. At some point of which I can't explain, the unit stops taking action. It still has mana and will take damage. Whatever I do to it, it will keep standing still as if stunned. It is not paused. Any ideas what is going on?

  • Area 1 Wave 2 Abilities
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Attacking unit)) Equal to Maiden of Pain
          • (Unit-type of (Attacking unit)) Equal to Overlord
          • (Unit-type of (Attacking unit)) Equal to Demon Spawn
          • (Unit-type of (Triggering unit)) Equal to Faceless Demon
          • (Unit-type of (Attacking unit)) Equal to Ghost
    • Actions
      • Set Unit = (Attacking unit)
      • Set Unit1 = (Triggering unit)
      • Set Real = (Mana of (Attacking unit))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit) Equal to Demon Spawn
          • (Mana of Unit) Greater than or equal to 50.00
        • Then - Actions
          • Unit - Order Unit to Special Archimonde - Finger Of Death Unit1
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit) Equal to Overlord
          • (Mana of Unit) Greater than or equal to 100.00
        • Then - Actions
          • Unit - Order Unit to Orc Far Seer - Feral Spirit
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit) Equal to Maiden of Pain
          • (Mana of Unit) Greater than or equal to 50.00
        • Then - Actions
          • Unit - Order Unit to Neutral Alchemist - Acid Bomb Unit1
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit) Equal to Ghost
        • Then - Actions
          • Set Integer = (Random integer number between 1 and 2)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Integer Equal to 1
              • (Mana of Unit) Greater than or equal to 100.00
            • Then - Actions
              • Unit - Order Unit to Undead Death Knight - Death Coil Unit1
            • Else - Actions
              • Unit - Order Unit to Undead Banshee - Curse Unit1
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of Unit1) Equal to Faceless Demon
          • (Mana of Unit1) Greater than or equal to 10.00
        • Then - Actions
          • Set PointDefault = (Position of Unit)
          • Unit - Make Unit1 face Unit over 0.00 seconds
          • Unit - Order Unit1 to Night Elf Warden - Blink PointDefault
          • Custom script: call RemoveLocation (udg_PointDefault)
        • Else - Actions
      • Set Unit = No unit
      • Set Unit1 = No unit
 
Last edited:
Level 16
Joined
Mar 25, 2016
Messages
1,327
Variables are global, so they affect all triggers.
However you are setting them at the beginning of each trigger. If your triggers are not interrupted by a trigger that changes the variable, the variable will have the correct value for the trigger.
Trigger one could be interrupted when dealing damage, if you use a damage event (also if the damage kills the unit, the event "a unit dies" will trigger). If an interrupting trigger overwrites Unit or Unit1, these variables will have the wrong value once the interrupting trigger completes and the game continues with trigger one.
There are a lot of actions that can interrupt triggers:
create unit: enters region
unit order: unit is issued
...

Using local variables is the best solution. Using shadowed globals this is also somewhat GUI friendly.
You can use shared globals, if you know the trigger won't be interrupted as long as you need the values on the globals. You can also use different globals, but depending on the number of triggers, this will create a ton of variables.
 
Level 12
Joined
Mar 24, 2011
Messages
1,082
  • Unit - A unit Is attacked
This event does not fire when a hit is made. It fires when an attempt for a basic attack is made.
Find a Damage Detection Systems and use it. Not "A unit is attacked"

As far as I am aware, Triggering "whatever" works like a local for the trigger.

regards
-Ned
 
Level 9
Joined
Jun 13, 2010
Messages
365
Using local variables is the best solution. Using shadowed globals this is also somewhat GUI friendly.
You can use shared globals, if you know the trigger won't be interrupted as long as you need the values on the globals. You can also use different globals, but depending on the number of triggers, this will create a ton of variables.
So what you say is if I was to avoid this issue, I would have to make additional Unit variables. 1 set for each trigger, in order to avoid this?
I thought that when one trigger had run, the next would overwrite the values, as the variables were set in a new trigger? But ofc if they run simultaneously..... But wouldn't that actually make two units set as the variable Unit at the same time?

This is confusing.

This event does not fire when a hit is made. It fires when an attempt for a basic attack is made.
Find a Damage Detection Systems and use it. Not "A unit is attacked"
Hmm.. If it is an issue and will cause overall bugs and trouble I might have to just remove it. The intention, though, was not for the Hero to take damage before the Counter Attack goes off. That was the idea of it. Also, there will be no issues with a Player pressing attack and stop, to continuesly redo the trigger, because the enemy will always be controlled by a Computer. Do you follow? Or was that not your point?
 
Level 16
Joined
Mar 25, 2016
Messages
1,327
Triggers never run at the same time. They run in a certain sequence.
  • A
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set Unit = (Casting unit)
      • Unit - Kill (Target unit of ability being cast)
      • Game - Display to (All players) the text: ((Name of Unit) + uses Finger of Death!)
  • B
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Set Unit = (Triggering unit)
      • Floating Text - Create floating text that reads dead above Unit with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Trigger A is some kind of spell that instantly kills the target unit.
Trigger B creates a floating text for dead units.

If A is used, Unit is set to the casting unit.
Then A kills the target unit.
Killing the unit immediatly runs B.
B now sets Unit to the dying unit.
Once B is finished, A will continue and print a message.
However now Unit is the dying unit, because of B and the message will print the dying unit's name.

The trigger run in this order: ABA. The second part of A runs after B, so the variable was already changed by B.

This is not what we wanted to happen, but the variable got overwritten by another trigger.

An easy fix is to use a a local variable shadowing the global variable:
  • Custom script: local unit udg_Unit
If you put this at the beginning of a trigger, you can no longer access the global variable named Unit. Instead you will access a local variable with the same name. A local variable can obviously not changed by another trigger, so you solved the problem very safely without much effort.

You can of course create additional variables, so each triggers has its own, but that is too much work and too many variables in my opinion.

I thought that when one trigger had run, the next would overwrite the values, as the variables were set in a new trigger? But ofc if they run simultaneously..... But wouldn't that actually make two units set as the variable Unit at the same time?
Usually they should run one after each other and then everything would be fine: The variable is set to the correct value at the beginning of the trigger and will keep this value for the entire trigger. As I tried to explain above, if a trigger is interrupted by another trigger, you can longer be sure that shared global variables have the correct value.
 
Level 9
Joined
Jun 13, 2010
Messages
365
Killing the unit immediatly runs B.

Oh no are you serious?
So another trigger will immediately run, setting the Unit variable to a new unit?
Wow.. All these years have past and I never knew that. This explains so much.
I thought it would finish off the WHOLE trigger A, before even moving to B... This is bad. I am afraid I don't know what to do with my whole map now. :O
 
Level 13
Joined
Oct 12, 2016
Messages
769
You could combine both the "Demon Hunter Counter Attack" and "Area 1 Wave 2 Abilities" triggers.
They both use the same event. You can even stack the if/then/else statements for mana checks in sequence, so they aren't all being checked with each attack made.
On top of that, you can then use if/then/else statements for the conditions you have in each trigger at the top.

This may stop some freezing, and improve the efficiency of your code.

Also, is the dummy unit with attack animation thing really ... necessary? I mean, why?
If you want the demon hunter to use its spell throw animation, you can just force it to play that animation on its attack after the random integer check.

Also, are the Demon, Maiden of Pain and Overlord units computer controlled only?
 
Level 9
Joined
Jun 13, 2010
Messages
365
You could combine both the "Demon Hunter Counter Attack" and "Area 1 Wave 2 Abilities" triggers.

That is true. However, "Demon Hunter Counter Attack" is a triggered ability for one Hero. I have 12 Heroes with at least 2 triggers for each Hero.
"Area 1 Wave 2 Abilities" is the second enemy the players will encounter. It will most likely end up in 10-15 different areas with groups.. So I might want to avoid squishing everything too much together. :)

On top of that, you can then use if/then/else statements for the conditions you have in each trigger at the top.
Can you give some details, I don't really follow.

Also, is the dummy unit with attack animation thing really ... necessary? I mean, why?
If you want the demon hunter to use its spell throw animation, you can just force it to play that animation on its attack after the random integer check.
I suppose if it is too complicated an issue to fix, I might have to just forget about it.
And I guess I could make the actual Hero do this. Although it actually looks pretty damn cool with the dummy units. :D
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,011
Triggers never run at the same time. They run in a certain sequence.
  • A
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • Set Unit = (Casting unit)
      • Unit - Kill (Target unit of ability being cast)
      • Game - Display to (All players) the text: ((Name of Unit) + uses Finger of Death!)
  • B
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Set Unit = (Triggering unit)
      • Floating Text - Create floating text that reads dead above Unit with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Trigger A is some kind of spell that instantly kills the target unit.
Trigger B creates a floating text for dead units.

If A is used, Unit is set to the casting unit.
Then A kills the target unit.
Killing the unit immediatly runs B.
B now sets Unit to the dying unit.
Once B is finished, A will continue and print a message.
However now Unit is the dying unit, because of B and the message will print the dying unit's name.

The trigger run in this order: ABA. The second part of A runs after B, so the variable was already changed by B.

This is not what we wanted to happen, but the variable got overwritten by another trigger.

An easy fix is to use a a local variable shadowing the global variable:
  • Custom script: local unit udg_Unit
If you put this at the beginning of a trigger, you can no longer access the global variable named Unit. Instead you will access a local variable with the same name. A local variable can obviously not changed by another trigger, so you solved the problem very safely without much effort..
Wait, what? The actual easiest fix is just putting the display message before killing the unit. No need global shadowing variables or anything, besides that, it is completely unnecessary to use variables for such simple thing.
SnowYell keep in mind that triggers are not triggered queued, they don't depend from another, and obviously can't trigger simultaneously, because that's impossible. If you had a situation where you set the variable to 3 other units in different triggers at same time, would the variable have the the 3 values assigned? That would be buggy as hell.
 
Level 16
Joined
Mar 25, 2016
Messages
1,327
Wait, what? The actual easiest fix is just putting the display message before killing the unit. No need global shadowing variables or anything, besides that, it is completely unnecessary to use variables for such simple thing.
The display message is just a part of the example trigger. I needed an action that uses the unit variable after it was modified by the other trigger.
The example trigger is not something you would have in an actual map. It's sole purpose was to explain SnowYell how a trigger can be interrupted and what problems this can cause.

If you need the unit several times, using a variable is more efficient than calling (Triggering Unit) all the time.
 
Level 26
Joined
Aug 18, 2009
Messages
4,099
It's not just an issue between different triggers. A trigger can directly call itself or via intermediates like trigger A calls B and B calls A again. Of course, in a static context, you would rather try to avoid this but for dynamic invocations such as in event handlers like Warcraft 3 features, these circles can easily happen. This is one reason to use local variables. Local variables warrant their independence from other contexts, you only need to observe the current function execution.

If you started programming in any other real-world language, you will find that global variables are always discouraged, or more precisely, variables (and anything that could be addressed for that matter) should unfailingly be restricted as much as possible in context. Not only does this boost static analyzability by shooting down states/procedures that can never logically occur (but may be hard to prove so) and enhances readability likewise but it does not clog the namespace, either. When using variables (even more so for globals), give them meaningful names, not temp/tmp or just their type. Spread semantics.
 
Level 9
Joined
Jun 13, 2010
Messages
365
Thanks, I think I have gotten what I need to proceed and will be returning to work on the issue some time in the future. Just a bit demotivating having to re-do possibly most of the triggers.

I just don't understand why the engine works this way. Why does a trigger stop half-way and let another trigger finish before the first? Seems strange and unclever. Also the fact that you will have to stack all possible global variables and actions in the same trigger would, in my opinion, make it messy... But I guess people make it work. Or maybe I should just create unique variables for those triggers which are in the way for other triggers. Hmm..

But thank you for all the feedback guys, it really is appreciated. I gave all rep points. :)
 
Level 23
Joined
Jan 1, 2009
Messages
1,617
I just don't understand why the engine works this way.

Imho the main issue here is that GUI doesn't allow the easy creation of local variables without the usage of custom scripts, otherwise the distinction would be clearer.

Just a bit demotivating having to re-do possibly most of the triggers.

You could change your global variables to arrays and use one index per trigger. This might reduce the workload a bit.
 
Status
Not open for further replies.
Top