• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

MUI Spells Using Artificial Waits

Introduction

Creating delayed spell effects that are MUI without the use of waits is fairly
simple. It just requires two variables and one periodic trigger. The first variable
is an integer that represents the current spell instance, whereas the second variable
is an integer that represents the total number of spell instances.

Avoiding Waits

This is an example of a spell that uses a Wait action in order to create a delayed effect:

  • Example
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Example
    • Actions
      • Set TempUnit = (Triggering unit)
      • Set TempPoint = (Target point of ability being cast)
      • Wait 2.00 seconds
      • Set TempGroup = (Units within 300.00 of TempPoint)
      • Unit Group - Pick every unit in TempGroup and do (Actions)
        • Loop - Actions
          • Unit - Cause TempUnit to damage (Picked unit), dealing 50.00 damage of attack type Spells and damage type Normal
      • Custom script: call DestroyGroup(udg_TempGroup)
      • Custom script: call RemoveLocation(udg_TempPoint)
This spell is not MUI and will give an inaccurate delay. In order to fix this, we
need to split this spell into two triggers. The first trigger will execute when the
spell is cast, and it will store spell data such as the caster, the level, the owner
of the caster, etc... The second trigger is going to have a periodic event. Let's make
it run every 1 second for simplicity as opposed to the 0.03 seconds used by most spell
makers. If you're going to make a timed spell, use 0.03. What we're going to do is add
one spell instance when the spell is cast, and iterate over all the spell instances in
the periodic trigger. It's basic indexing, and it looks like this:

  • Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Animate Dead
    • Actions
      • Set MaxIndex = (MaxIndex + 1)
      • -------- Here's the bit we had in the first place before the wait --------
      • Set TempUnit[MaxIndex] = (Triggering unit)
      • Set TempPoint[MaxIndex] = (Position of TempUnit[MaxIndex])
      • -------- ----------------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • MaxIndex Equal to 1
        • Then - Actions
          • Trigger - Turn on Loop <gen>
        • Else - Actions
  • Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • For each (Integer CurrentIndex) from 1 to MaxIndex, do (Actions)
        • Loop - Actions
          • -------- Here's the bit we had in the first place after the wait --------
          • Set TempGroup = (Units within 200.00 of TempPoint[CurrentIndex])
          • Unit Group - Pick every unit in TempGroup and do (Actions)
            • Loop - Actions
              • Unit - Cause TempUnit[CurrentIndex] to damage (Picked unit), dealing 50.00 damage of attack type Spells and damage type Normal
          • Custom script: call DestroyGroup(udg_TempGroup)
          • -------- This is recycling. It's unimportant in terms of spell effects, but it is important in terms of MU-Instancability --------
          • Custom script: call RemoveLocation(udg_TempPoint[udg_CurrentIndex])
          • Set TempPoint[CurrentIndex] = TempPoint[MaxIndex]
          • Set TempUnit[CurrentIndex] = TempUnit[MaxIndex]
          • Set MaxIndex = (MaxIndex - 1)
          • Set CurrentIndex = (CurrentIndex - 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • MaxIndex Equal to 0
            • Then - Actions
              • Trigger - Turn off (This trigger)
            • Else - Actions
There we go, this is our spell. 100% MUI and functional.

Using a Counter

This spell will wait 1 second, not 2. This is a problem that can
be addressed by using a counter.

In the caster trigger, we're going to set our counter to 0, and
in the periodic trigger, we're going to increment our counter by 1.
If the counter reaches our desired value, we execute the remaining
code and end the spell instance.

This is what our final product would look like:

  • Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Animate Dead
    • Actions
      • Set MaxIndex = (MaxIndex + 1)
      • -------- Here's the bit we had in the first place before the wait --------
      • Set TempUnit[MaxIndex] = (Triggering unit)
      • Set TempPoint[MaxIndex] = (Position of TempUnit[MaxIndex])
      • Set Counter[MaxIndex] = 0
      • -------- ----------------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • MaxIndex Equal to 1
        • Then - Actions
          • Trigger - Turn on Loop <gen>
        • Else - Actions
  • Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • For each (Integer CurrentIndex) from 1 to MaxIndex, do (Actions)
        • Loop - Actions
          • Set Counter[CurrentIndex] = (Counter[CurrentIndex] + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Counter[CurrentIndex] Equal to 2
            • Then - Actions
              • -------- Here's the bit we had in the first place after the wait --------
              • Set TempGroup = (Units within 200.00 of TempPoint[CurrentIndex])
              • Unit Group - Pick every unit in TempGroup and do (Actions)
                • Loop - Actions
                  • Unit - Cause TempUnit[CurrentIndex] to damage (Picked unit), dealing 50.00 damage of attack type Spells and damage type Normal
              • Custom script: call DestroyGroup(udg_TempGroup)
              • -------- Recycling --------
              • Custom script: call RemoveLocation(udg_TempPoint[udg_CurrentIndex])
              • Set TempPoint[CurrentIndex] = TempPoint[MaxIndex]
              • Set TempUnit[CurrentIndex] = TempUnit[MaxIndex]
              • Set Counter[CurrentIndex] = Counter[MaxIndex]
              • Set MaxIndex = (MaxIndex - 1)
              • Set CurrentIndex = (CurrentIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
            • Else - Actions
Overview

This is a small explanation concerning what will actually happen during the
execution of the spell to clear things up for you if you didn't get it yet.

Let MaxCounter = 2.

1) The spell has been cast.

2) After 1 second:
  • Counter is initially 0.
  • Counter = Counter + 1
  • Counter is now 1.
  • Counter < MaxCounter
  • Nothing happens.

3) After 2 seconds:
  • Counter is initially 1.
  • Counter = Counter + 1
  • Counter is now 2.
  • Counter == MaxCounter
  • Actions execute.
  • Spell instance recycled.

This spell is now MUI and works for our desired delay duration.


Wrap-up

If you still find it hard to understand, feel free to PM me any time.
Thank you for reading.

~Tank-Commander
 
Thank you for submitting this.

I replaced the content in my tutorial in the "MUI without Waits" section with a link to this.

Since my tutorial was approved, I guess this one is supposed to be approved by default as well, but as a form of respect to Purgeandfire111, I'll leave this in the Submissions section for him approve himself :p
 
Level 16
Joined
Jul 31, 2012
Messages
2,217
I am not good at MUI but there is something i know good:

Loop with 1 second loop is not accurate he does not start looping when you turn it on if he was off

i knew that by accident when i had a loop trigger every 30 s if i turned it on the 20 s of game time he will execute after 10 seconds

So the only "accurate loop" is 0.01 s [spells]
 
I am not good at MUI but there is something i know good:

Loop with 1 second loop is not accurate he does not start looping when you turn it on if he was off

i knew that by accident when i had a loop trigger every 30 s if i turned it on the 20 s of game time he will execute after 10 seconds

So the only "accurate loop" is 0.01 s [spells]

Tank-Commander said:
Let's make
it run every 1 second for simplicity as opposed to the 0.03 seconds used by most spell
makers. If you're going to make a timed spell, use 0.03.

Read context first, then comment
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
so something like this?
  • Custom script: call RemoveLocation(udg_TempPoint[udg_Current_Index])
hey mag, don't talk about jass please..
i don't understand any of it :vw_death:
super confusing............ at least to me :grin:

back to topic so everything related to variable use the udg_prefix i suppose?

that custom script line is jass thats y he said referenced in jass
 
Level 20
Joined
Apr 14, 2012
Messages
2,901
Hello Tank_Commander :D
I know you've told me the answer to this question, but I would really appreciate it again if you or someone else with experience reiterated the answer to me again:

Your tutorial only tells me how to efficiently use 1 counter in an MUI spell; what if I wanted to use multiple counters to differentiate and separate different stages of my spell?

Also, I've pondered on this question for a while now:
In the case that I have to use more than a Cast and a Loop trigger, and I add a Loop 2 trigger, where do I de-index or recycle my variables? Or do most cases in spell making avoid having a Loop 2 trigger because of multiple counters?

I would love some feedback here, I would really appreciate it.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
im on phone so ill do best i can. basically u just have a few ITEs were ur smallest value is last. this way it will only run once.
example:
  • Set counter = counter + 1
  • if
    • counter equal to 200
  • then
  • else
    • if
      • counter equal to 150
    • then
    • else
      • if
        • counter equal to 100
      • then
      • else
        • if
          • counter equal to 50
        • then
        • else
this way the counter counts up and hits the first option selected. i use equal to which means that it will only run when its equal to that number. if u want actions in there then u would need to use greater than or equal to. dont use less than or equal to in this case.
u can use less than ( notequal to) but u would have to switch the code around so that the smaller numbers go first. then u can use less than
 
Level 4
Joined
Aug 18, 2013
Messages
71
Blink Variation Not Working

Hey Guys, Long time Wc3 Map editor.
Quick Query, can you proof-read my triggers, the "wait" isn't working. I believe its not even catching the ability.

  • Blink Catch
    • Events
      • Unit - A unit Finishes casting an ability
    • Conditions
      • (Ability being cast) Equal to Blink
    • Actions
      • Set Blink_Max_Index = (Blink_Max_Index + 1)
      • Set Blink_Temp_Unit[Blink_Max_Index] = (Triggering unit)
      • Set Blink_Counter[Blink_Max_Index] = 0
      • Unit - Add Blink Immunity (Spell Immunity) to (Triggering unit)
      • Unit - Set level of Blink Immunity (Spell Immunity) for Blink_Temp_Unit[Blink_Current_Index] to 1
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Blink_Max_Index Equal to 1
        • Then - Actions
          • Trigger - Turn on Blink Loop <gen>
        • Else - Actions
  • Blink Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • For each (Integer Blink_Current_Index) from 1 to Blink_Max_Index, do (Actions)
        • Loop - Actions
          • Set Blink_Counter[Blink_Current_Index] = (Blink_Counter[Blink_Current_Index] + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 3
            • Then - Actions
              • Unit - Remove Blink Immunity (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • Set Blink_Temp_Unit[Blink_Current_Index] = Blink_Temp_Unit[Blink_Max_Index]
              • Set Blink_Counter[Blink_Current_Index] = Blink_Counter[Blink_Max_Index]
              • Set Blink_Max_Index = (Blink_Max_Index - 1)
              • Set Blink_Current_Index = (Blink_Current_Index - 1)
            • Else - Actions
              • Unit - Set level of Blink Immunity (Spell Immunity) for Blink_Temp_Unit[Blink_Current_Index] to Blink_Counter[Blink_Current_Index]
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Blink_Max_Index Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
Spell Immunity isn't even being applied to the caster during debug test. The ability is supposed to give the caster "Blink Immunity" for 3 seconds after cast (Levels are used as a countdown via Tooltip display)

I've also Attempted This to no avail.

  • Blink Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • For each (Integer Blink_Current_Index) from 1 to Blink_Max_Index, do (Actions)
        • Loop - Actions
          • Set Blink_Counter[Blink_Current_Index] = (Blink_Counter[Blink_Current_Index] + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 1
            • Then - Actions
              • Unit - Remove Blink Immunity - 3 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • Unit - Add Blink Immunity - 2 (Spell Immunity) to (Triggering unit)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 2
            • Then - Actions
              • Unit - Remove Blink Immunity - 2 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • Unit - Add Blink Immunity - 1 (Spell Immunity) to (Triggering unit)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 3
            • Then - Actions
              • Unit - Remove Blink Immunity - 1 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • Set Blink_Temp_Unit[Blink_Current_Index] = Blink_Temp_Unit[Blink_Max_Index]
              • Set Blink_Counter[Blink_Current_Index] = Blink_Counter[Blink_Max_Index]
              • Set Blink_Max_Index = (Blink_Max_Index - 1)
              • Set Blink_Current_Index = (Blink_Current_Index - 1)
            • Else - Actions
              • Unit - Set level of Blink Immunity - 3 (Spell Immunity) for Blink_Temp_Unit[Blink_Current_Index] to Blink_Counter[Blink_Current_Index]
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Blink_Max_Index Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
This variation consists of 3 separate "Blink Immunity" spells being interchanged during the for loop.
I know the solution is probably simple, but I don't see it.


Found it!
(Ability being cast) Equal to Blink
Was using the wrong Blink
Also Changed
Unit - A unit Finishes casting an ability
to
Unit - A unit Begins casting an ability
Further Discover also brought me to notice in my second attempt without a level system,
Blink Loop
Events
Time - Every 1.00 seconds of game time
Conditions
Actions
...
Unit - Add Blink Immunity - 2 (Spell Immunity) to (Triggering unit)
Changed that and everything works fine. Free MUI Code for a Blink + Spell Immunity Here
  • Blink Catch
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • (Ability being cast) Equal to Blink
    • Actions
      • Set Blink_Max_Index = (Blink_Max_Index + 1)
      • Set Blink_Temp_Unit[Blink_Max_Index] = (Casting unit)
      • Set Blink_Counter[Blink_Max_Index] = 0
      • -------- --------
      • -------- Spell Immunity 1/3 - Name Change to "Spell Immunity - 3" to signify a countdown --------
      • Unit - Add Blink Immunity - 3 (Spell Immunity) to (Casting unit)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Blink_Max_Index Equal to 1
        • Then - Actions
          • Trigger - Turn on Blink Loop <gen>
        • Else - Actions
This requires 3 Spell Immunity spells in the data editor
  • Blink Loop
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • For each (Integer Blink_Current_Index) from 1 to Blink_Max_Index, do (Actions)
        • Loop - Actions
          • Set Blink_Counter[Blink_Current_Index] = (Blink_Counter[Blink_Current_Index] + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 1
            • Then - Actions
              • -------- --------
              • -------- Removes Previous Immunity (1/3) --------
              • Unit - Remove Blink Immunity - 3 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • -------- Spell Immunity 2/3 - Name Change to "Spell Immunity - 2" to signify a countdown --------
              • Unit - Add Blink Immunity - 2 (Spell Immunity) to Blink_Temp_Unit[Blink_Current_Index]
              • -------- --------
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 2
            • Then - Actions
              • -------- --------
              • -------- Removes Previous Immunity (2/3) --------
              • Unit - Remove Blink Immunity - 2 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • -------- Spell Immunity 3/3 - Name Change to "Spell Immunity - 1" to signify a countdown --------
              • Unit - Add Blink Immunity - 1 (Spell Immunity) to Blink_Temp_Unit[Blink_Current_Index]
              • -------- --------
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Blink_Counter[Blink_Current_Index] Equal to 3
            • Then - Actions
              • -------- --------
              • -------- Removes Previous Immunity (3/3) --------
              • Unit - Remove Blink Immunity - 1 (Spell Immunity) from Blink_Temp_Unit[Blink_Current_Index]
              • -------- --------
              • -------- Cleanup below --------
              • Set Blink_Temp_Unit[Blink_Current_Index] = Blink_Temp_Unit[Blink_Max_Index]
              • Set Blink_Counter[Blink_Current_Index] = Blink_Counter[Blink_Max_Index]
              • Set Blink_Max_Index = (Blink_Max_Index - 1)
              • Set Blink_Current_Index = (Blink_Current_Index - 1)
            • Else - Actions
              • Unit - Set level of Blink Immunity - 3 (Spell Immunity) for Blink_Temp_Unit[Blink_Current_Index] to Blink_Counter[Blink_Current_Index]
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Blink_Max_Index Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
 
Last edited:
Your code has one problem. The trigger-turn-off block should be executed immediately after the instance deallocation code. The trigger never turns off in its current state. (I can tell by reading it, I haven't tested anything)

Move this:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • Blink_Max_Index Equal to 0
    • Then - Actions
      • Trigger - Turn off (This trigger)
    • Else - Actions
to:

  • Set Blink_Temp_Unit[Blink_Current_Index] = Blink_Temp_Unit[Blink_Max_Index]
  • Set Blink_Counter[Blink_Current_Index] = Blink_Counter[Blink_Max_Index]
  • Set Blink_Max_Index = (Blink_Max_Index - 1)
  • Set Blink_Current_Index = (Blink_Current_Index - 1)
  • // Here.
 
But that way, it runs 32x a second regardless of whether the instance count changed or not. With mine, it only does the check when required /o/

So, for a 3-second spell, your way runs it 96 times, while mine runs it 1 time.
For a 3-second spell with 10 instances, yours runs 96 times, mine runs 10 times.

In general, mine runs much, much less /o/
 
Level 1
Joined
Jun 19, 2013
Messages
9
Tank- Commander, if the event of the " loop" trigger is - Every 2 seconds of game time , will the actions of trigger be executed only after 2 seconds after the trigger is turned on ?
Why cant we simply do it like this ?
 
Level 1
Joined
Jun 19, 2013
Messages
9
deathismyfriend , what i meant was if any trigger with the event that i have mentioned is turned on , will the action be executed only on 2 seconds after the trigger is turned on ? Or will they be executed immediately ? Anyway , thanks for trying to help me.
 
Nice tut TC! This will surely be of help to those aspiring coders out there.

Changed that and everything works fine. Free MUI Code for a Blink + Spell Immunity Here
No offense, but it's GUI, it's not like we could copy paste it to our maps, right?

EDIT: And also instead of lots of ITEs, you could array those immunity abilities.
 
the only thing confusing to me are the variables.. If someone just make a tutorial about MUI's with a list of variables you need to use like variable types and stuff, i would be freaking happy.

I made a tutorial that discusses the same topic as applied to a specific spell:
http://www.hiveworkshop.com/forums/...orials-279/visualize-dynamic-indexing-241896/

It is quite long, but it might be worth giving a shot! Otherwise, the best way to learn it is honestly to: (1) come up with a spell idea (2) try making it. If you get stuck, ask for help on the forums and we'll gladly help you get it going.

MUI is one of those issues that is really hard to understand until you experience it. Sometimes it can be helpful to make the spell not-MUI, then try casting it with multiple units to see what goes wrong. Just reading a tutorial won't be enough to fully understand it. But if you apply it to a spell, you'll begin to understand it much better. :)
 
Level 12
Joined
May 28, 2015
Messages
382
I made a tutorial that discusses the same topic as applied to a specific spell:
http://www.hiveworkshop.com/forums/...orials-279/visualize-dynamic-indexing-241896/

It is quite long, but it might be worth giving a shot! Otherwise, the best way to learn it is honestly to: (1) come up with a spell idea (2) try making it. If you get stuck, ask for help on the forums and we'll gladly help you get it going.

MUI is one of those issues that is really hard to understand until you experience it. Sometimes it can be helpful to make the spell not-MUI, then try casting it with multiple units to see what goes wrong. Just reading a tutorial won't be enough to fully understand it. But if you apply it to a spell, you'll begin to understand it much better. :)

Thanks dude, and btw.. I have a question, it is not really related to this tutorial but is it okay for a spell to be not MUI as long as its only for one unit?? Is it bad to have it in multiplayer maps?

Edit: Is it also bad if I use the same variables with arrays?? Like for example.. If I use Caster_Loc[1] on my first spell and also use it on my other spell. Sorry bout the unrelated questions, I'm too lazy to search the forums and I just want quick answers.
 
Last edited:
Top