Multiple Cast with dummies on Spell Help

Level 8
Joined
Oct 6, 2022
Messages
213
Anyone Knows How to make my Trigger able to cast its spell multiple times without any problems?

  • Sword Strike
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to (Elaina) Geo Fury
    • Actions
      • Set SwordStrike_Caster = (Triggering unit)
      • Set SwordStrike_Caster_Point = (Target point of ability being cast)
      • Set SwordStrike_angle = (Angle from (Position of SwordStrike_Caster) to SwordStrike_Caster_Point)
      • Set SwordStrike_move_speed = 60.00
      • Set SwordStrike_cur_distance = 0.00
      • Set SwordStrike_max_distance = 1300.00
      • Set SwordStrike_Point[1] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) + 200.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[1] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[1] = (Last created unit)
      • Set SwordStrike_Point[2] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) - 200.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[2] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[2] = (Last created unit)
      • Set SwordStrike_Point[3] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) + 300.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[3] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[3] = (Last created unit)
      • Set SwordStrike_Point[4] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) - 300.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[4] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[4] = (Last created unit)
      • Set SwordStrike_Point[5] = ((Position of SwordStrike_Caster) offset by 150.00 towards ((Facing of SwordStrike_Caster) + 100.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[5] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[5] = (Last created unit)
      • Set SwordStrike_Point[6] = ((Position of SwordStrike_Caster) offset by 150.00 towards ((Facing of SwordStrike_Caster) - 100.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[6] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[6] = (Last created unit)
      • Set SwordStrike_Point[7] = ((Position of SwordStrike_Caster) offset by 50.00 towards ((Facing of SwordStrike_Caster) - 350.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[7] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[7] = (Last created unit)
      • Set SwordStrike_Point[8] = ((Position of SwordStrike_Caster) offset by 50.00 towards ((Facing of SwordStrike_Caster) + 350.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[8] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[8] = (Last created unit)
      • Set SwordStrike_Point[9] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) + 250.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[9] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[9] = (Last created unit)
      • Set SwordStrike_Point[10] = ((Position of SwordStrike_Caster) offset by 100.00 towards ((Facing of SwordStrike_Caster) - 250.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[10] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[10] = (Last created unit)
      • Set SwordStrike_Point[11] = ((Position of SwordStrike_Caster) offset by 150.00 towards ((Facing of SwordStrike_Caster) + 150.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[11] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[11] = (Last created unit)
      • Set SwordStrike_Point[12] = ((Position of SwordStrike_Caster) offset by 150.00 towards ((Facing of SwordStrike_Caster) - 150.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[12] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[12] = (Last created unit)
      • Set SwordStrike_Point[13] = ((Position of SwordStrike_Caster) offset by 50.00 towards ((Facing of SwordStrike_Caster) + 400.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[13] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[13] = (Last created unit)
      • Set SwordStrike_Point[14] = ((Position of SwordStrike_Caster) offset by 50.00 towards ((Facing of SwordStrike_Caster) - 400.00) degrees)
      • Unit - Create 1 Elaina Stone Dummy for (Owner of SwordStrike_Caster) at SwordStrike_Point[14] facing (Facing of SwordStrike_Caster) degrees
      • Set SwordStrike_Dummies[14] = (Last created unit)
      • For each (Integer A) from 1 to 14, do (Actions)
        • Loop - Actions
          • Unit - Turn collision for SwordStrike_Dummies[(Integer A)] Off
      • Trigger - Turn on Sword Strike Loop <gen>
  • Sword Strike Loop
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SwordStrike_cur_distance Less than or equal to SwordStrike_max_distance
        • Then - Actions
          • For each (Integer A) from 1 to 14, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Terrain pathing at (Position of SwordStrike_Dummies[(Integer A)]) of type Flyability is off) Equal to True
                • Then - Actions
                  • Unit - Turn collision for SwordStrike_Dummies[(Integer A)] On
                  • Unit - Add a 0.01 second Generic expiration timer to SwordStrike_Dummies[(Integer A)]
                • Else - Actions
              • Unit - Move SwordStrike_Dummies[(Integer A)] instantly to ((Position of SwordStrike_Dummies[(Integer A)]) offset by SwordStrike_move_speed towards SwordStrike_angle degrees), facing SwordStrike_angle degrees
              • Unit Group - Pick every unit in (Units within 75.00 of (Position of SwordStrike_Dummies[(Integer A)]) matching ((((Matching unit) belongs to an enemy of (Owner of SwordStrike_Caster)) Equal to True) and (((((Matching unit) is A structure) Equal to False) and (((Matching unit) is alive) Equa and do (Actions)
                • Loop - Actions
                  • Unit - Cause SwordStrike_Dummies[(Integer A)] to damage (Picked unit), dealing (6.00 x (Real((Level of (Elaina) Geo Fury for SwordStrike_Caster)))) damage of attack type Pierce and damage type Normal
                  • Unit - Interrupt (Picked unit)'s Attack
                  • Unit - Create 1 Casting Dummy for (Owner of SwordStrike_Caster) at (Position of (Picked unit)) facing (Facing of (Picked unit)) degrees
                  • Unit - Add (Altair) Silence to (Last created unit)
                  • Unit - Set level of (Altair) Silence for (Last created unit) to (Level of (Altair) Sword Strike for SwordStrike_Caster)
                  • Set SwordStrike_DummyCaster = (Last created unit)
                  • Unit - Add a 1.00 second Generic expiration timer to SwordStrike_DummyCaster
                  • Unit - Add a 0.05 second Generic expiration timer to SwordStrike_Dummies[(Integer A)]
                  • Special Effect - Create a special effect at (Position of SwordStrike_Dummies[(Integer A)]) using Abilities\Weapons\AncientProtectorMissile\AncientProtectorMissile.mdl
                  • Special Effect - Destroy (Last created special effect)
                  • Unit - Order SwordStrike_Caster to Neutral Fire Lord - Soul Burn (Picked unit)
                  • Unit - Remove SwordStrike_Dummies[(Integer A)] from the game
                  • Trigger - Turn on Sword Strike FX loop <gen>
          • Set SwordStrike_cur_distance = (SwordStrike_cur_distance + SwordStrike_move_speed)
        • Else - Actions
          • Trigger - Turn off (This trigger)
          • For each (Integer A) from 1 to 14, do (Actions)
            • Loop - Actions
              • Special Effect - Create a special effect at (Position of SwordStrike_Dummies[(Integer A)]) using Abilities\Weapons\AncientProtectorMissile\AncientProtectorMissile.mdl
              • Special Effect - Destroy (Last created special effect)
              • Unit - Remove SwordStrike_Dummies[(Integer A)] from the game
              • Set SwordStrike_Dummies[(Integer A)] = No unit
Thanks in advance
 
Uff, there're more things to discuss than just how to make the trigger cast the spell multiple times without issues.

I assume you want to be able to cast the 'Sword Strike' spell multiple times at the same time (e.g. by multiple units, etc.)? Or do you want to just make the dummy unit inside Sword Strike Loop be able to cast "(Altair) Silence" on multiple units at once?

If you want to support multiple instances of 'Sword Strike' spell, then you need to make the spell MPI (multi player instanceable) or MUI (multi unit instanceable). Making spell MUI is a bit more complex than MPI, as MPI assumes only a single unit per player (e.g. unique hero-type) can use the spell, while MUI assumes multiple units of same player can cast the spell at same time (e.g. Faerie Dragon's Mana Flare).
However in both cases you will need to use arrays to track all instance specific data. In your case, you would need to use SwordStrike_Casters[] array to keep track of all casters of all instances. Same for your other variables. See Visualize: Dynamic Indexing which shows an example spell being made MUI.

If you want a single dummy to be able to cast multiple spells at once, the first requirement is that the spell-to-be-cast is not channeled. For example Firebolt is not channeled, but Blizzard and Flame Strike are.
If you have non-channeled spell, then you also need to modify dummy's Object Editor data:
  • Give dummy the "locust" unit ability to make it non-targetable and disable its collision.
  • Set all following fields in category "Art - Animation" to "0.0": Blend Time, Cast Backswing, Cast Point. This removes artificial waits before unit casts spell
  • Set "Movement - Speed Base" to 0. This makes the dummy cast the spell without the need to turn towards the target
  • Set "Movement - Type" to "None". This leaves the dummy at the position it was created (if you leave for example "Foot", dummy will be moved out of collision of trees, structures, etc.).
  • Optionally, you can also set things like "Sound - Unit Sound Set" to "None", "Stats - Food Cost" to 0, "Stats - Hide Minimap Display" to "False" and "Stats - Sight Radius" fields to low values or 0.

Also, ensure the dummy's ability is set up properly: It has no cooldown, no mana cost. A good practice is to also give the dummy spell increased cast range to act as a range motion buffer (for standard attacks the motion buffer is 250.0 by default).

If you have everything set up correctly, you could do something like this:
  • Actions
    • Unit - Create 1 Dummy for Player 1 (Red) at some_location facing Default building facing degrees
    • Set VariableSet dummy = (Last created unit)
    • Unit - Add Firebolt (Dummy Version) to dummy
    • Unit Group - Pick every unit in some_unit_group and do (Actions)
      • Loop - Actions
        • Unit - Order dummy to Neutral - Firebolt (Picked unit)
Notice that a single dummy is ordered to cast firebolt on all units in unit group.

---
Apart from the above, there are other things to note in your triggers:

1. Memory leaks
You are leaking memory all over the place. Specifically locations and unit groups. See first post in Things That Leak to see how to clean up.

2. SwordStrike_Dummies collision
I am not sure why you are turning collision on/off for SwordStrike_Dummies. If "Elaina Stone Dummy" unit-type has locust ability, then its collision is turned off already. Unless that unit-type does not have locust because you want it to be targetable and killable.

3. SwordStrike_Dummies - no check for dead/removed dummies
Currently you iterate always over all 14 dummies and you never check if given dummy even exists alive in the game. The issue is that the action "Unit Group - Pick every unit in (Units within 75.00 of (Position of SwordStrike_Dummies[(Integer A)]) ..." picks all units nearby the dummy. If the dummy does not exist in game, the "(Position of SwordStrike_Dummies[(Integer A)])" will return coordinates [0,0] - the center of map. So you cast the spell at the bottom-left part of the map, but somehow units at the center of the map will get damaged.

4. Casting Dummy created over and over
Creating and managing many units (even invisible ones) is quite taxing. Considering your dummy casts "(Altair) Silence" spell, which seems to be based off non-channeled spell Soul Burn, you can use the approach I wrote about at the top of my post, about having a single dummy cast spell multiple times. For example, you could create the dummy in "Sword Strike" trigger and just reference it in "Sword Strike Loop" trigger.
Then just remove the dummy when the spell completely ends.

5. Casting Dummy is not used at all
You create a dummy, set it up with spell, etc. and assign it to variable "SwordStrike_DummyCaster", but you order the caster of the spell (SwordStrike_Caster) to cast Soul Burn.

6. Terrain pathing check
You are checking terrain pathing via "(Terrain pathing at (Position of SwordStrike_Dummies[(Integer A)]) of type Flyability is off) Equal to True" which seems a bit strange, considering you are moving "Elaina Stone Dummy" which based on name alone sounds like a projectile of sorts.
I just wanted to note that while the trigger action "Unit - Move unit" checks terrain pathing (and thus may move the unit to different location than you want to, if the targeted location is not pathable for the dummy), there exist functions SetUnitX(whichUnit, newX) and SetUnitY(whichUnit, newY) which move unit to specified X/Y coordinate, ignoring any pathing. The downside is that those are jass functions, so you would need to use the Custom Script trigger action.

7. The "(Owner of SwordStrike_Caster)"
This isn't an issue, but it may make your trigger more readable. In any unit-based event, you can reference (Triggering player) to automatically get the (Owner of (Triggering unit)), which in your case would be the SwordStrike_Caster.

8. Creation of 14 dummies
There's no issues there per-se, just that it is hard to read and prone to errors (e.g. if you were to add another dummy, you may copy wrong actions, forget to change index somewhere to value "15", etc.).
Best way would be to use a custom jass function, but since you are in GUI, try leveraging loops and data structures.
For example in the below I set up dummy spawn data in an "ini" trigger like this:
  • Sword Strike Ini
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet SwordStrike_SpawnCount = 7
      • -------- ----- --------
      • Set VariableSet SwordStrike_SpawnAngle[1] = 200.00
      • Set VariableSet SwordStrike_SpawnAngle[2] = 300.00
      • Set VariableSet SwordStrike_SpawnAngle[3] = 100.00
      • Set VariableSet SwordStrike_SpawnAngle[4] = 350.00
      • Set VariableSet SwordStrike_SpawnAngle[5] = 250.00
      • Set VariableSet SwordStrike_SpawnAngle[6] = 150.00
      • Set VariableSet SwordStrike_SpawnAngle[7] = 400.00
      • -------- ----- --------
      • Set VariableSet SwordStrike_SpawnOffset[1] = 100.00
      • Set VariableSet SwordStrike_SpawnOffset[2] = 100.00
      • Set VariableSet SwordStrike_SpawnOffset[3] = 150.00
      • Set VariableSet SwordStrike_SpawnOffset[4] = 50.00
      • Set VariableSet SwordStrike_SpawnOffset[5] = 100.00
      • Set VariableSet SwordStrike_SpawnOffset[6] = 150.00
      • Set VariableSet SwordStrike_SpawnOffset[7] = 50.00
And then, using loop within loop + bit of math, I can replace the 42 actions for spawning 14 dummies in "Sword Strike" trigger with just this:
  • For each (Integer loopA) from 0 to (SwordStrike_SpawnCount - 1), do (Actions)
    • Loop - Actions
      • For each (Integer loopB) from 0 to 1, do (Actions)
        • Loop - Actions
          • Set VariableSet idx = (((loopA x 2) + 1) + loopB)
          • Set VariableSet angle = ((Facing of caster) + ((Real(((2 x loopB) - 1))) x SwordStrike_SpawnAngle[loopA]))
          • Set VariableSet SwordStrike_Points[idx] = (caster_loc offset by SwordStrike_SpawnOffset[loopA] towards angle degrees.)
          • Unit - Create 1 Elaina Stone Dummy for (Triggering player) at SwordStrike_Points[idx] facing (Facing of caster) degrees
          • Set VariableSet SwordStrike_Dummies[idx] = (Last created unit)
The "(Real(((2 x loopB) - 1)))" is just a formula to turn loopB's 0 and 1 values into -1 and +1, to achieve the negative and positive angle offsets.

If I wanted to add another set of dummies, I could just update the ini trigger with new values at index 8 in SwordStrike_SpawnAngle and SwordStrike_SpawnOffset arrays and set SwordStrike_SpawnCount to 8.
 
Back
Top