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

[Spell] Dummy Unit don't cast spell at the same time with hero and idk why

Level 13
Joined
Sep 11, 2013
Messages
496
Greetings!

I found a weird problem with my trigger.
  • Shadow Strike WISP 0 MS
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Unit-type of (Casting unit)) Equal to Infernal WISP 0 MS
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Game - Display to (All players) the text: WISP 0 MS
      • Unit - Create 1 Wisp Shadow Strike Dummy 0 MS for (Owner of (Casting unit)) at (Position of (Casting unit)) facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
      • Unit - Add a 10.00 second Generic expiration timer to (Last created unit)
      • Unit - Add Shadow Strike XXX to (Last created unit)
      • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Casting unit))
      • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
So, the problem is that this dummy unit has a spell (Shadow Strike XXX) that has even more cast range than my Hero spell (Shadow Strike - INT Channel) and if the enemy unit is running away from me, but somehow I manage to cast the hero spell at maximum cast range while the enemy unit is running away, the dummy (even with 100 bonus cast range on his spell) will not release the spell on that enemy unit. Why my dummy is not cast his spell in the same time with my hero? (In this case Dummy WISP has 0 Movement Speed)

In the second case, I set Dummy WISP movement speed to 522)
Now, When I cast the spell with my hero in the same scenario, The dummy unit will start to move to the enemy unit and then release his spell.
That's make no sense for me because dummy has more cast range than me on his spell. The only explanation is that, there is a delay between spells.

BUT I tested that with the same spell on both units and seems to be instant while the enemy is not moving.
BUT When the enemy is moving, the spells are no more instant and work only on Hero. :peasant-confused:

Here is an example of dummy with 0 ms and dummy with 522 ms behavior.


If I put global cast range on dummy spell, there is another problem because if an enemy unit blink away or teleports imediately after I start casting the hero spell, the dummy spell will go the entire map after that enemy and I don't want that. What I wish is simple.. If hero manage to cast the spell and enemy blink or teleport, the poison/missile must miss the enemy. Also, If hero manage to cast the spell, the dummy must launch the second spell at the same time.

How can I do that?

I attached the map with samples.

The help will be appreciated!
 

Attachments

  • Shadow Strike delay BUG.w3m
    19.4 KB · Views: 10
Last edited:
Level 13
Joined
Sep 11, 2013
Messages
496
Is there a reason you give such a short specific range to your dummy spell? Sure it's higher than the hero's spell but you could give it e.g. 5000 range and it'll most likely solve the issue
As I said here
If I put global cast range on dummy spell, there is another problem because if an enemy unit blink away or teleports imediately after I start casting the hero spell, the dummy spell will go the entire map after that enemy and I don't want that. What I wish is simple.. If hero manage to cast the spell and enemy blink or teleport, the poison/missile must miss the enemy. Also, If hero manage to cast the spell, the dummy must launch the second spell at the same time.
This is the problem.

I still don't understand why my dummy refuse to cast the spell at the same time with my hero when the enemy is moving.. If the hero has enough range to cast the spell, why the dummy refuse to cast the spell even with bigger range?
 
Level 30
Joined
Sep 26, 2009
Messages
2,617
You may have increased dummy's spell range by 100, but that is too low.
Simply put, you may have set dummy's cast point to 0.00, but you have ignored caster's Cast point. Caster's cast point offsets time when dummy is created.
  • Your caster's cast point is 0.3 seconds.
  • Your mortar team unit is moving at 450.00 speed per second, that means it will move 135.00 range over the 0.3 second duration.

So even if your dummy unit's cast range is increased by 100 range, it will be of no use because the dummy unit is created after 0.3 seconds since caster started the spell, and during those 0.3 seconds mortar team moves away by 135.00 range, getting outside dummy's increased cast range.
 
Level 25
Joined
Feb 27, 2019
Messages
845
Theres a gameplay constant called: Spells - Target Motion Allowance (default 300 range). Its a gameplay mechanic which allows spells that have begun casting an additional 300 range until it starts the effect. Without it units would just run after the target, try to cast but fail cuz instantly out of range and repeat.

The problem you are talking about exists in vanilla and possibly all custom games but its such a rare event that basically must be triggered by something like Starts the effect -> Move target unit of ability being cast to point. When the event occurs you can consider that the missile will be spawned and will hit its target. I guess blink and possibly teleport has inbuilt behaviour to nullify missiles so if a missile is cast and the target blinks, the missile will miss the target. You may possibly be able to do some stupid shit with these abilities to nullify a missile but as I said what youre talking about is vanilla behaviour already and probably wont happen ever.

Anyway if you add 300 range to the dummy ability itll probably behave as excpected.
 
Level 13
Joined
Sep 11, 2013
Messages
496
Have you changed the dummy animation - cast point and animation - backswing value in object editor? they must be 0.00.
Yes, they are and were 0 when I tested in that video.
You may have increased dummy's spell range by 100, but that is too low.
Simply put, you may have set dummy's cast point to 0.00, but you have ignored caster's Cast point. Caster's cast point offsets time when dummy is created.
  • Your caster's cast point is 0.3 seconds.
  • Your mortar team unit is moving at 450.00 speed per second, that means it will move 135.00 range over the 0.3 second duration.

So even if your dummy unit's cast range is increased by 100 range, it will be of no use because the dummy unit is created after 0.3 seconds since caster started the spell, and during those 0.3 seconds mortar team moves away by 135.00 range, getting outside dummy's increased cast range.
That makes a lot of sense. Thank you for explanation, but the problem is still there even if I set the Caster like a dummy with cast point to 0.00. The dummy still has +100 bonus cast range on that spell, but don't cast the spell. I think this is a bug or we miss something.

Theres a gameplay constant called: Spells - Target Motion Allowance (default 300 range). Its a gameplay mechanic which allows spells that have begun casting an additional 300 range until it starts the effect. Without it units would just run after the target, try to cast but fail cuz instantly out of range and repeat.

The problem you are talking about exists in vanilla and possibly all custom games but its such a rare event that basically must be triggered by something like Starts the effect -> Move target unit of ability being cast to point. When the event occurs you can consider that the missile will be spawned and will hit its target. I guess blink and possibly teleport has inbuilt behaviour to nullify missiles so if a missile is cast and the target blinks, the missile will miss the target. You may possibly be able to do some stupid shit with these abilities to nullify a missile but as I said what youre talking about is vanilla behaviour already and probably wont happen ever.

Anyway if you add 300 range to the dummy ability itll probably behave as excpected.

I didn't know about that Target Motion Allowance. Hmm.. Interesting. Thank you for explanation. However, from what I understand, If I set caster's cast point to 0.00, the Target Motion Allowance will be 0 in that case, no? But somehow the dummy still refuse to cast the spell even with +100 bonus cast range..

Why this happen? Is this a bug? :peasant-confused: :peasant-confused: :peasant-confused:
 
Level 25
Joined
Feb 27, 2019
Messages
845
I didn't know about that Target Motion Allowance. Hmm.. Interesting. Thank you for explanation. However, from what I understand, If I set caster's cast point to 0.00, the Target Motion Allowance will be 0 in that case, no? But somehow the dummy still refuse to cast the spell even with +100 bonus cast range..

Why this happen? Is this a bug? :peasant-confused: :peasant-confused: :peasant-confused:
The Target Motion Allowance is always 300 as long as the spell has begun casting within the spells Cast Range. The time from begins casting to the event Starts the effect of an ability is Cast Point. During the Cast Point who knows how far the target will travel, as long as it doesnt travel further away than Cast Range + 300 the event Starts the effect of an ability will occur which should trigger the dummy to cast its ability because the ability was successfully cast but if when the dummy begins casting the target is not within the dummy abilitys Cast Range, it will not be cast. The Target Motion Allowance is only applied when the ability has begun casting within the Cast Range.
 
Level 13
Joined
Sep 11, 2013
Messages
496
The Target Motion Allowance is always 300 as long as the spell has begun casting within the spells Cast Range. The time from begins casting to the event Starts the effect of an ability is Cast Point. During the Cast Point who knows how far the target will travel, as long as it doesnt travel further away than Cast Range + 300 the event Starts the effect of an ability will occur which should trigger the dummy to cast its ability because the ability was successfully cast but if when the dummy begins casting the target is not within the dummy abilitys Cast Range, it will not be cast. The Target Motion Allowance is only applied when the ability has begun casting within the Cast Range.
I understand that, but my Event Caster and my dummy has both 0.00 cast point right now. Caster has 475 cast range and Dummy has 575 cast range. As long as both has cast point 0.00, why the dummy didn't cast his spell to the enemy while the enemy is moving (is still out of cast range, but why) ? I did the math and makes no sense.
 

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
I understand that, but my Event Caster and my dummy has both 0.00 cast point right now. Caster has 475 cast range and Dummy has 575 cast range. As long as both has cast point 0.00, why the dummy didn't cast his spell to the enemy while the enemy is moving (is still out of cast range, but why) ? I did the math and makes no sense.
Your Dummy unit is likely not setup correctly. Here's an example map with an ideal Dummy unit for this type of spellcasting.
  • Hero Storm Bolt
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Storm Bolt (Hero)
    • Actions
      • Unit - Create 1 Dummy for (Triggering player) at (Position of (Triggering unit)) facing Default building facing degrees
      • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
      • Unit - Add Storm Bolt (Dummy) to (Last created unit)
      • Unit - Order (Last created unit) to Human Mountain King - Storm Bolt (Target unit of ability being cast)
Note that I didn't bother dealing with the memory leaks in any of the triggers.

But if you need a Dummy unit to act like a Missile or be repositioned during your trigger(s) then you'll have to change it's Movement settings. This will prevent it from being able to cast spells "instantly", so you'll want to choose the correct type of Dummy for the job.
 

Attachments

  • Dummy Example 1.w3m
    24.9 KB · Views: 7
Last edited:
Level 25
Joined
Feb 27, 2019
Messages
845
I understand that, but my Event Caster and my dummy has both 0.00 cast point right now. Caster has 475 cast range and Dummy has 575 cast range. As long as both has cast point 0.00, why the dummy didn't cast his spell to the enemy while the enemy is moving (is still out of cast range, but why) ? I did the math and makes no sense.
Hmm... That is interesting. I see your point. My assumption was probably not entirely correct. There is something else at play that allows an ability to be cast at a further distance that is dependant on Target Movement Allowance and collision size. If Target Movement Allowance is 0 the decider is collision size so Cast Range + Collision size of Caster and Target. If Target Movement Allowance is 300 theres additional lenience that can cause the dummy with 0 move speed to fail its cast, even if it has 100 more cast range, specifically about when targeting a patrolling unit at ~max range just as it turns around. I dont know whats up with that.
 
Level 30
Joined
Sep 26, 2009
Messages
2,617
The Target Motion Allowance is always 300 as long as the spell has begun casting within the spells Cast Range. The time from begins casting to the event Starts the effect of an ability is Cast Point.
That is the crux of the problem. The hero unit starts casting spell while within the range, so target motion allowance is taken into account. But dummy unit created via trigger did not start casting its spell yet, so only raw spell range is taken into account; no target motion allowance included in this case.

I understand that, but my Event Caster and my dummy has both 0.00 cast point right now. Caster has 475 cast range and Dummy has 575 cast range. As long as both has cast point 0.00, why the dummy didn't cast his spell to the enemy while the enemy is moving (is still out of cast range, but why) ? I did the math and makes no sense.
There are many other things that should be taken into account. I can image that caster's turn rate also plays role - that has nothing to do with Cast Point, so Cast Point 0 won't resolve turn delay.
Then there's pathing - both dummy and target have 32.00 pathing size. Pathing map is moved on a 8x8 grid (the smallest squares when showing grid via top bar menu -> View -> Grid -> Small). It snaps into the grid and does not move with the unit until that unit's origin point enters next 8x8 cell - then pathing map snaps to next 8x8 cell. So even if units are close enough when simply calculating distance between their locations, if they are positioned badly on the grid there may be up to a ~16.00 range difference due to grid snapping of their respective pathing maps.

There could also be some engine implementation details as to how distance is calculated with movement speed possibly taken into account.
I added following line into your "Shadow Strike WISP 0 MS" trigger right after adding 10.00 second expiration timer to the dummy:
JASS:
IsUnitInRange( GetLastCreatedUnit(), GetSpellTargetUnit(), 575.00)
which is a function that checks distance between dummy and targeted unit, but also takes their pathing into account. In all cases where dummy unit failed to cast its spell that function returned false, meaning during evaluation both units were further than 575.00 range.

Overall, I think you are encountering an edge case by using a very short range increment for the dummy spell, not necessarily bug. I would advise just increasing dummy unit's spell's cast range not by 100.00, but by 300.00 range to match the "target motion allowance" range. It will resolve all your issues and behave like standard spell.
 
Level 13
Joined
Sep 11, 2013
Messages
496
Your Dummy unit is likely not setup correctly. Here's an example map with an ideal Dummy unit for this type of spellcasting.
  • Hero Storm Bolt
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Storm Bolt (Hero)
    • Actions
      • Unit - Create 1 Dummy for (Triggering player) at (Position of (Triggering unit)) facing Default building facing degrees
      • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
      • Unit - Add Storm Bolt (Dummy) to (Last created unit)
      • Unit - Order (Last created unit) to Human Mountain King - Storm Bolt (Target unit of ability being cast)
Note that I didn't bother dealing with the memory leaks in any of the triggers.

But if you need a Dummy unit to act like a Missile or be repositioned during your trigger(s) then you'll have to change it's Movement settings. This will prevent it from being able to cast spells "instantly", so you'll want to choose the correct type of Dummy for the job.
Thank you for your help! Sadly, this will not solve my problem, but this trigger it helped me to test/learn more about my problem.
(I didn't know I can put this kind of condition):peasant-thumbs-up:
  • (Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast))) Less than or equal to 731.00
Hmm... That is interesting. I see your point. My assumption was probably not entirely correct. There is something else at play that allows an ability to be cast at a further distance that is dependant on Target Movement Allowance and collision size. If Target Movement Allowance is 0 the decider is collision size so Cast Range + Collision size of Caster and Target. If Target Movement Allowance is 300 theres additional lenience that can cause the dummy with 0 move speed to fail its cast, even if it has 100 more cast range, specifically about when targeting a patrolling unit at ~max range just as it turns around. I dont know whats up with that.
That is the crux of the problem. The hero unit starts casting spell while within the range, so target motion allowance is taken into account. But dummy unit created via trigger did not start casting its spell yet, so only raw spell range is taken into account; no target motion allowance included in this case.


There are many other things that should be taken into account. I can image that caster's turn rate also plays role - that has nothing to do with Cast Point, so Cast Point 0 won't resolve turn delay.
Then there's pathing - both dummy and target have 32.00 pathing size. Pathing map is moved on a 8x8 grid (the smallest squares when showing grid via top bar menu -> View -> Grid -> Small). It snaps into the grid and does not move with the unit until that unit's origin point enters next 8x8 cell - then pathing map snaps to next 8x8 cell. So even if units are close enough when simply calculating distance between their locations, if they are positioned badly on the grid there may be up to a ~16.00 range difference due to grid snapping of their respective pathing maps.

There could also be some engine implementation details as to how distance is calculated with movement speed possibly taken into account.
I added following line into your "Shadow Strike WISP 0 MS" trigger right after adding 10.00 second expiration timer to the dummy:
JASS:
IsUnitInRange( GetLastCreatedUnit(), GetSpellTargetUnit(), 575.00)
which is a function that checks distance between dummy and targeted unit, but also takes their pathing into account. In all cases where dummy unit failed to cast its spell that function returned false, meaning during evaluation both units were further than 575.00 range.

Overall, I think you are encountering an edge case by using a very short range increment for the dummy spell, not necessarily bug. I would advise just increasing dummy unit's spell's cast range not by 100.00, but by 300.00 range to match the "target motion allowance" range. It will resolve all your issues and behave like standard spell.
My OCD is destroying me. Jesus. I tested hours trying to understand how this problem works... Damn the costs. I found a rabbit hole in this problem.:peasant-thumbs-down-angry:

Forget about dummy unit and let's try to simplify the problem as much as possible.

Right now I have just a <Hero> and a <Mortar team unit>.

The Hero has just Shadow Strike XXX spell lvl 1 with <500> Cast Range.
The Hero has Animation - Cast Point/Cast Backswing/Blend Time set to <0.00>
The Hero and Mortar team unit has both 32 Collision Size and movement type Foot.

With this setup I've made some tests trying to find a pattern, but i failed.
Just set the movement speed base to both units and press " f " in my map to test yourself if you want.
In all tests I use this trigger
  • Shadow Strike
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike XXX
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast))) Less than or equal to XXX.00
        • Then - Actions
          • Game - Display to (All players) the text: (String((Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast)))))
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Game - Display to (All players) the text: (String((Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast)))))
          • Unit - Order (Casting unit) to Stop.

Test 1.
Hero - 400 ms & Mortar - 300 ms

In this test, the Hero will cast Shadow Strike at 680 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Test 2.
Hero - 400 ms & Mortar - 250 ms

In this test, the Hero will cast Shadow Strike at 643 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Test 3.
Hero - 400 ms & Mortar - 200 ms

In this test, the Hero will cast Shadow Strike at 617 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Test 4.
Hero - 400 ms & Mortar - 150 ms

In this test, the Hero will cast Shadow Strike at 593 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

---------------------------------------------------------------
After those 4 tests, I decided to change the max speed in Global Constant to 500 ms and because in Test 1 is just 100 ms difference, I decided to test again with 100 ms difference but different speed.

Test 5.
Hero - 500 ms & Mortar - 400 ms

In this test, the Hero will cast Shadow Strike at 731 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Observation: Test 1 and Test 5 has only 100 ms difference, but will set a different cast range to the spell

Test 6.
Hero - 200 ms & Mortar - 100 ms

In this test, the Hero will cast Shadow Strike at 590 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Observation: Test 5 and Test 6 has only 100 ms difference, but will set a different cast range to the spell

Test 7 Final.
Hero - 522 ms & Mortar - 522 ms

In this test, the Hero will cast Shadow Strike at 773 range distance. Idk why because Cast Point is 0.00 and Cast Range of spell is 500.

Observation: This is very weird because the hero shouldn't even be able to cast the spell, but somehow he can.

This is a big problem because I don't want the hero to cast this spell at 773 range when the cast range of the spell is 500 (cast point 0.00) and it is even worse because it is not supose to be able to cast a spell on a unit that run away with same movement speed and the distance bigger than 500 (cast range)...

Here is another weird Observation... Even if my Mortar is unmoving, my hero will always change it's cast range of his spell.. idk why..

Note. If you try to repeat all my tests with " f ", do not move the hero or mortar from their position, not even a little bit because the results will be different. idk why.

So.... sure adding 300 cast range to dummy spell is a solution, but now the problem is different.. I still don't know how cast range works.. and I want that my hero spell to not be able to cast if the cast range is beyond 500..

Maybe something like this will work? What do you think??:peasant-work-work::peasant-work-work::peasant-work-work:
  • Shadow Strike WISP 0 MS
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Set VariableSet TARGET_UNIT = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast))) Less than 600.00
        • Then - Actions
          • Unit - Create 1 Wisp SS Dummy 0 MS for (Owner of (Casting unit)) at (Position of (Casting unit)) facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
          • Unit - Add a 10.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Casting unit))
          • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Unit - Order (Casting unit) to Stop.
          • Wait 0.01 seconds
          • Unit - Order (Casting unit) to Neutral - Parasite TARGET_UNIT
I attached the map for tests.
 

Attachments

  • Shadow Strike V2.w3m
    18.3 KB · Views: 3
Last edited:

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
Pretty much every video game you play is going to have some kind of imprecision due to how they're designed. In this case you're seeing a product of the pathfinding algorithm + varying movement speeds + game ticks. Basically, the developers made some compromises in their design because it was easier to code + more performant and realism is not the selling point of a game like this. The game also uses a Lockstep Determinism model that keeps everyone's game perfectly in sync, and so design choices like this are often essential to making that work.

ChatGPT summary (I can't promise 100% accuracy but it gets the idea across):

1. Game Ticks and Delayed Order Execution

Warcraft III runs its game logic at a fixed tick rate, roughly every 0.03 seconds (or 33 ticks per second). When a unit is given a command to move into range to cast a spell:
  • The engine checks every tick whether the unit is within range.
  • If the unit crosses into cast range between ticks, the game won't realize it until the next tick.
  • But in the next tick, the unit has already moved a bit further, overshooting the ideal distance.
This is a form of overshooting due to tick delay.

2. Movement Speed and Overshooting

The faster the unit, the more distance it covers between ticks. For example:
  • A unit moving at 300 units/second travels ~9 units per tick.
  • If the game detects it's not in range at tick N, it lets the unit move forward.
  • At tick N+1, the unit might now be 9 units closer than necessary.
This is why high-move-speed units tend to overshoot more than slower ones.

3. Pathfinding and Stopping Distance

The Warcraft III pathfinding system isn't pixel-perfect:
  • Units don't decelerate like physics-based systems; they stop abruptly.
  • The engine doesn't pre-calculate precise braking distance to stop right at the 500 unit mark.
  • Instead, it uses coarse logic like "move until you're in range," which, due to ticks and speed, means they often overstep.
This is particularly noticeable with projectile spells or unit-targeted abilities that don’t trigger instant movement cancelation upon entering range.
 
Last edited:
Level 13
Joined
Sep 11, 2013
Messages
496
Pretty much every video game you play is going to have some kind of imprecision due to how they're designed. In this case you're seeing a product of the pathfinding algorithm + varying movement speeds + game ticks. Basically, the developers made some compromises in their design because it was easier to code + more performant and realism is not the selling point of a game like this. The game also uses a Lockstep Determinism model that keeps everyone's game perfectly in sync, and so design choices like this are often essential to making that work.

ChatGPT summary (I can't promise 100% accuracy but it gets the idea across):

1. Game Ticks and Delayed Order Execution

Warcraft III runs its game logic at a fixed tick rate, roughly every 0.03 seconds (or 33 ticks per second). When a unit is given a command to move into range to cast a spell:
  • The engine checks every tick whether the unit is within range.
  • If the unit crosses into cast range between ticks, the game won't realize it until the next tick.
  • But in the next tick, the unit has already moved a bit further, overshooting the ideal distance.
This is a form of overshooting due to tick delay.

2. Movement Speed and Overshooting

The faster the unit, the more distance it covers between ticks. For example:
  • A unit moving at 300 units/second travels ~9 units per tick.
  • If the game detects it's not in range at tick N, it lets the unit move forward.
  • At tick N+1, the unit might now be 9 units closer than necessary.
This is why high-move-speed units tend to overshoot more than slower ones.

3. Pathfinding and Stopping Distance

The Warcraft III pathfinding system isn't pixel-perfect:
  • Units don't decelerate like physics-based systems; they stop abruptly.
  • The engine doesn't pre-calculate precise braking distance to stop right at the 500 unit mark.
  • Instead, it uses coarse logic like "move until you're in range," which, due to ticks and speed, means they often overstep.
This is particularly noticeable with projectile spells or unit-targeted abilities that don’t trigger instant movement cancelation upon entering range.
Thank you for explanation.. I guess the only option for me is to use your trigger at the Hero (Caster) spell, but I don't know how to do it optimily. Also, I'll add 300 bonus cast range to dummy spell just in case.

So, please tell me If there is a way to do this trigger more optimally. I try to not let the Hero (Caster) to cast the spell if for some random reason, the spell will increase automatically his cast range way too much like in my tests.

Here is what I did and seems to work. Right now the Hero (Caster) Spell has 475 cast range.
  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Casting unit)) and (Position of (Target unit of ability being cast))) Less than or equal to 550.00
        • Then - Actions
          • Unit - Create 1 Wisp SS Dummy 0 MS for (Owner of (Casting unit)) at (Position of (Casting unit)) facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
          • Unit - Add a 10.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Casting unit))
          • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Unit - Order (Casting unit) to Stop.
          • Wait 0.01 seconds
          • Unit - Order (Casting unit) to Neutral - Parasite Target_Unit_Shadow_Strike
That "Else - Actions" seems a little bit odd.. and that weird variable.. but works.. what do you think?

Edit: This trigger works, but not always.. idk why. Sometimes the hero just stop without casting..:peasant-sad:
 

Attachments

  • Shadow Strike V3.w3m
    19.9 KB · Views: 3
Last edited:
Level 13
Joined
Sep 11, 2013
Messages
496
Assuming that it works, the only issues I see are:
1) Memory leaks.
2) More importantly, Wait can't be used with Target_Unit_Shadow_Strike and (Casting unit), neither are local variables and can change during the delay.
Sadly is not always working.. Sometimes the hero just stop without trying to cast again.. while enemy is running away.

1) I know how to solve this
2) I don't know how to solve this.
 

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
Sadly is not always working.. Sometimes the hero just stop without trying to cast again.. while enemy is running away.

1) I know how to solve this
2) I don't know how to solve this.
#2: You can use an actual local variable, and use (Triggering unit) instead of (Casting unit) since it always acts like a local variable:
  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Triggering unit)) and Target_Unit_Shadow_Strike) Less than or equal to 550.00
        • Then - Actions
          • Custom script: set u = null
          • Unit - Create 1 Wisp SS Dummy 0 MS for (Owner of (Triggering unit)) at (Position of (Triggering unit)) facing Default building facing degrees
          • Unit - Add a 10.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Triggering unit))
          • Unit - Order (Last created unit) to Neutral - Parasite Target_Unit_Shadow_Strike
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Unit - Order (Triggering unit) to Stop.
          • Wait 0.01 seconds
          • Custom script: set udg_Target_Unit_Shadow_Strike = u
          • Custom script: set u = null
          • Unit - Order (Triggering unit) to Neutral - Parasite Target_Unit_Shadow_Strike

Also, is there a reason that you make the Wisp SS Dummy face the target's position?
  • facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
A proper Dummy unit doesn't need to turn to cast spells. Perhaps your Dummy is still not setup correctly? Or do you use the Dummy's model for visual purposes?
 
Level 13
Joined
Sep 11, 2013
Messages
496
#2: You can use an actual local variable, and use (Triggering unit) instead of (Casting unit) since it always acts like a local variable:
  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Triggering unit)) and Target_Unit_Shadow_Strike) Less than or equal to 550.00
        • Then - Actions
          • Custom script: set u = null
          • Unit - Create 1 Wisp SS Dummy 0 MS for (Owner of (Triggering unit)) at (Position of (Triggering unit)) facing Default building facing degrees
          • Unit - Add a 10.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Triggering unit))
          • Unit - Order (Last created unit) to Neutral - Parasite Target_Unit_Shadow_Strike
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Unit - Order (Triggering unit) to Stop.
          • Wait 0.01 seconds
          • Custom script: set udg_Target_Unit_Shadow_Strike = u
          • Custom script: set u = null
          • Unit - Order (Triggering unit) to Neutral - Parasite Target_Unit_Shadow_Strike

Hi and Thank you for your help!

Well.. I think in this trigger is a mistake or something is missing because I can't copy it..

You set Target_Unit_Shadow_Strike = (Target unit of ability being cast) -> So the variable is Unit type, but then you use this variable as a Point ? How?
(Distance between (Position of (Triggering unit)) and Target_Unit_Shadow_Strike) Less than or equal to 550.00 -> Now the variable is a Point?

And then the orders use the same variable as a Unit type.. I am very confused. :peasant-confused:

Edit: Is this Wait trigger MUI?
Also, is there a reason that you make the Wisp SS Dummy face the target's position?
  • if.gif
    facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
A proper Dummy unit doesn't need to turn to cast spells. Perhaps your Dummy is still not setup correctly? Or do you use the Dummy's model for visual purposes?
The original trigger looks like this. I just wanted to launch the projectile poison from the front of the hero because it looks better. That's why I make that angle. In the original map I have a good dummy (locust, cast point 0.00, etc). The dummy is invisible.
  • Shadow Strike
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Set VariableSet ShadowStrikePointX = ((Position of (Casting unit)) offset by 135.00 towards (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees.)
      • Unit - Create 1 Wisp Shadow Strike Dummy for (Owner of (Casting unit)) at ShadowStrikePointX facing (Angle from (Position of (Casting unit)) to (Position of (Target unit of ability being cast))) degrees
      • Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
      • Unit - Add Shadow Strike XXX to (Last created unit)
      • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Casting unit))
      • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
      • Custom script: call RemoveLocation(udg_ShadowStrikePointX)
 
Last edited:
Level 13
Joined
Sep 11, 2013
Messages
496
Target_Unit_Shadow_Strike is unit variable, not unit-type. He's issuing an order targeting a unit, not targeting a point.
Yes, but he use the same unit variable in this row:

    • empty.gif
      empty.gif
      line.gif
      joinbottom.gif
      if.gif
      (Distance between (Position of (Triggering unit)) and Target_Unit_Shadow_Strike) Less than or equal to 550.00
And this condition allows only point variable.. How?
 
Level 13
Joined
Sep 11, 2013
Messages
496
Here we go!
This is the final trigger that I'll put in my map.
Seems to work very good. I still don't know if is MUI or if is possible to cause other problems, but for the moment all works very good. :peasant-thumbs-up:

Cast range on Shadow Strike - INT Channel is now 500 and I need to put 575 in the condition in order to make this trigger to work good.

  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between (Position of (Triggering unit)) and (Position of Target_Unit_Shadow_Strike)) Less than or equal to 575.00
        • Then - Actions
          • Game - Display to (All players) the text: then
          • Set VariableSet ShadowStrikePointX = ((Position of (Triggering unit)) offset by 135.00 towards (Angle from (Position of (Triggering unit)) to (Position of (Target unit of ability being cast))) degrees.)
          • Unit - Create 1 Wisp Shadow Strike Dummy 0 MS for (Owner of (Triggering unit)) at ShadowStrikePointX facing (Angle from (Position of (Triggering unit)) to (Position of (Target unit of ability being cast))) degrees
          • Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Triggering unit))
          • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
          • Custom script: call RemoveLocation(udg_ShadowStrikePointX)
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Unit - Order (Triggering unit) to Stop.
          • Custom script: set udg_Target_Unit_Shadow_Strike = u
          • Custom script: set u = null
          • Wait 0.01 seconds
          • Unit - Order (Triggering unit) to Neutral - Parasite Target_Unit_Shadow_Strike
Thank you all for your amazing help!:peasant-ok-hand:
 

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
Here we go!
This is the final trigger that I'll put in my map.
Seems to work very good. I still don't know if is MUI or if is possible to cause other problems, but for the moment all works very good. :peasant-thumbs-up:

Cast range on Shadow Strike - INT Channel is now 500 and I need to put 575 in the condition in order to make this trigger to work good.
Thank you all for your amazing help!:peasant-ok-hand:
It will be MUI, although the Wait could make some awkward outcomes if things happen rapidly. But it shouldn't actually break anything.

Regarding patching up the memory leaks:
  • Custom script: set u = null
You can actually move this "null" Action to the end of the trigger. You want this to be null regardless of what happens, otherwise it'll leak. Then replace all instances of (Position of...) with a Point variable. You're leaking 7 of them at the moment. You also put some Actions before the Wait that should be after it - in fact this mistake stops it from being MUI.

Here's the final trigger:
  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • Set VariableSet TempPoint[0] = (Position of (Triggering unit))
      • Set VariableSet TempPoint[1] = (Position of Target_Unit_Shadow_Strike)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between TempPoint[0] and TempPoint[1]) Less than or equal to 575.00
        • Then - Actions
          • Game - Display to (All players) the text: then
          • Set VariableSet TempReal[0] = (Angle from TempPoint[0] and TempPoint[1])
          • Set VariableSet TempPoint[2] = (TempPoint[0] offset by 135.00 towards TempReal[0] degrees.)
          • Unit - Create 1 Wisp Shadow Strike Dummy 0 MS for (Owner of (Triggering unit)) at TempPoint[2] facing TempReal[0] degrees
          • Custom script: call RemoveLocation( udg_TempPoint[0] )
          • Custom script: call RemoveLocation( udg_TempPoint[1] )
          • Custom script: call RemoveLocation( udg_TempPoint[2] )
          • Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Triggering unit))
          • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
        • Else - Actions
          • Game - Display to (All players) the text: else
          • Custom script: call RemoveLocation( udg_TempPoint[0] )
          • Custom script: call RemoveLocation( udg_TempPoint[1] )
          • Unit - Order (Triggering unit) to Stop.
          • Wait 0.01 seconds
          • Custom script: set udg_Target_Unit_Shadow_Strike = u
          • Unit - Order (Triggering unit) to Neutral - Parasite Target_Unit_Shadow_Strike
      • Custom script: set u = null
With this setup you'll avoid all of the memory leaks. And again, you may not need to make the Dummy unit face the target, they can cast spells without turning. The only reason to do that is if the Dummy has a special model that you want to be seen.
 
Last edited:
Level 13
Joined
Sep 11, 2013
Messages
496
Then replace all instances of (Position of...) with a Point variable. You're leaking 7 of them at the moment.
Oh.. I didn't know that every (Position of...) is leaking..
------------------------------------------------------------------------------------
You also put some Actions before the Wait that should be after it - in fact this mistake stops it from being MUI.
Yeah.. I guess I wasn't very careful when I copied the trigger you created.. My bad.. :peasant-sad:
------------------------------------------------------------------------------------
With this setup you'll avoid all of the memory leaks.
Thank you!
------------------------------------------------------------------------------------
And again, you may not need to make the Dummy unit face the target, they can cast spells without turning.
So.. from what I understand, I can change "facing TempReal[0]" to Default building facing and the result will be the same.
------------------------------------------------------------------------------------

Other questions..
1. TempPoint is Point type - Array 1? >>> (I suppose so.)
2. TempReal is Real type - Array 1? >>> (I suppose so.)

3. Will be a problem if I put in "Else" [Unit - Order (Triggering unit) to Hold Position.] instead of Stop?

-----------------------------------------
  • Shadow Strike Final
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Shadow Strike - INT Channel
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Set VariableSet Target_Unit_Shadow_Strike = (Target unit of ability being cast)
      • Set VariableSet TempPoint_ShadowStrike[0] = (Position of (Triggering unit))
      • Set VariableSet TempPoint_ShadowStrike[1] = (Position of Target_Unit_Shadow_Strike)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between TempPoint_ShadowStrike[0] and TempPoint_ShadowStrike[1]) Less than or equal to 575.00
        • Then - Actions
          • Game - Display to (All players) the text: then final
          • Set VariableSet TempReal_ShadowStrike[0] = (Angle from TempPoint_ShadowStrike[0] to TempPoint_ShadowStrike[1])
          • Set VariableSet TempPoint_ShadowStrike[2] = (TempPoint_ShadowStrike[0] offset by 135.00 towards TempReal_ShadowStrike[0] degrees.)
          • Unit - Create 1 Wisp Shadow Strike Dummy 0 MS for (Owner of (Triggering unit)) at TempPoint_ShadowStrike[2] facing TempReal_ShadowStrike[0] degrees
          • Custom script: call RemoveLocation(udg_TempPoint_ShadowStrike[0])
          • Custom script: call RemoveLocation(udg_TempPoint_ShadowStrike[1])
          • Custom script: call RemoveLocation(udg_TempPoint_ShadowStrike[2])
          • Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
          • Unit - Add Shadow Strike XXX to (Last created unit)
          • Unit - Set level of Shadow Strike XXX for (Last created unit) to (Level of Shadow Strike - INT Channel for (Triggering unit))
          • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
        • Else - Actions
          • Game - Display to (All players) the text: else final
          • Custom script: call RemoveLocation(udg_TempPoint_ShadowStrike[0])
          • Custom script: call RemoveLocation(udg_TempPoint_ShadowStrike[1])
          • Unit - Order (Triggering unit) to Hold Position.
          • Wait 0.01 seconds
          • Custom script: set udg_Target_Unit_Shadow_Strike = u
          • Unit - Order (Triggering unit) to Neutral - Parasite Target_Unit_Shadow_Strike
      • Custom script: set u = null
I'll attach the latest map here for everyone who needs it.

Once again, Thank you very much!!!
 

Attachments

  • Shadow Strike delay Full Optimized Trigger by Uncle.w3m
    20.4 KB · Views: 1

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
That's looking good, but some things to fix:

1) Change this line to use the Target_Unit_Shadow_Strike variable:
  • Unit - Order (Last created unit) to Neutral - Parasite (Target unit of ability being cast)
  • Unit - Order (Last created unit) to Neutral - Parasite Target_Unit_Shadow_Strike
2) You may want to increase the Expiration Timer to last longer than the Duration of Shadow Strike:
  • Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
  • Unit - Add a 20.00 second Generic expiration timer to (Last created unit)
If the Dummy unit dies too early it might mess with the effects of the spell.


To answer your questions:

So.. from what I understand, I can change "facing TempReal[0]" to Default building facing and the result will be the same.
Yes, Dummy units with the settings that I suggested can cast spells without turning. They cast the spell "instantly".
This means that you could get rid of TempReal_ShadowStrike[] if you wanted, but it's not that big of a deal. In the future you can use "Default building facing" for your Dummy units.

1. TempPoint is Point type - Array 1? >>> (I suppose so.)
Yes, if there's brackets [] then it's an Array. The Size can almost always remain at 1. The Size exceptions are for Unit Group, Player Group, and Timer arrays. (There might be 1 or 2 other variable types that I'm forgetting about).

Keep this in the back of your mind if you ever run into issues when using Arrays -> "My trigger looks good, why doesn't it work?".
It could be a Size issue, which you can find solutions for on Hive.

2. TempReal is Real type - Array 1? >>> (I suppose so.)
Yes, although it doesn't actually need to be an Array since we only use it once -> TempReal[0].

3. Will be a problem if I put in "Else" [Unit - Order (Triggering unit) to Hold Position.] instead of Stop?
No, if it still cancels the ability then it should be fine. If it fails then you could probably issue both orders like so -> Issue Stop, Issue Hold.
 
Last edited:
Level 8
Joined
Jul 22, 2015
Messages
146
2) You may want to increase the Expiration Timer to last longer than the Duration of Shadow Strike:
  • unit.gif
    Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
  • unit.gif
    Unit - Add a 20.00 second Generic expiration timer to (Last created unit)
If the Dummy unit dies too early it might mess with the effects of the spell.
I remember this bug when i first using dummy below duration of damage over time spell, when it kill the enemies it cannot credit you a gold because dummy no longer exist. To counter of this one is just use damage from trigger periodicly also detecting the debuff.
 

Uncle

Warcraft Moderator
Level 74
Joined
Aug 10, 2018
Messages
7,938
I remember this bug when i first using dummy below duration of damage over time spell, when it kill the enemies it cannot credit you a gold because dummy no longer exist. To counter of this one is just use damage from trigger periodicly also detecting the debuff.
I wouldn't call it a bug necessarily, but yes, triggering things yourself is almost always better.

But it depends on the type of map and what other systems/triggers it's using (ie: Damage Engine, special Kill Credit triggers, etc). An expiration timer that is always longer than the buff Duration + Missile delay could be a perfectly fine solution.
 
Last edited:
Top