Flare
Stops copies me!
- Reaction score
- 662
Well, this is my first attempt at a tutorial, and I think I have covered the basics of MUI spells in GUI. If you have any suggestions on additions that can be made, please post them and I will add it in.
MUI spells in GUI
-So, you want to make MUI spells in GUI (only JASS required is for leak removal). Well, you've come to the right place
What is MUI?
--MUI is Multi-Unit-Instanceability. But what does this mean to all those who don't know? It means that if 2 or more units (owned by any player, but it still has to work with 2 units owned by Player 1 for example) cause a trigger to fire (casting a spell would be best example), the spell's effect will work properly for both units that cast the ability. Also, MUI is -nearly- impossible to achieve with waits. I've seen an MUI ability with waits before, but it was only AoE (since you can't ensure that a specific unit variable will be retained after a wait, if another unit casts the ability).
This tutorial will explain everything involved in creating an MUI spell (well, everything involved in my preferred method of doing it). This method of making spells MUI relies mainly on periodic events (you need something to cause the periodic events to fire i.e. units in a unit group), but you can do alot of things within that periodic event such as waiting (without using Wait action), a more precise periodic event (if you wanted something to occur every 1 second, but the timing had to be right i.e. cast a spell at 10.5 seconds gametime, the effect should occur at 11.5 seconds gametime. A 1 second periodic event trigger wouldn't be able to do this correctly and timing would be incorrect)
Index:
1. Where do I start?
2. What do I need?
3. MUI periodic events (DoT spell will be used as an example).
4. Avoiding waits with periodic events.
5. Buffs
6. Credits (order is a bit mixed up unfortunately)
7. Practical example of MUI periodic events and MUI wait avoidance.
8. Running trigger B from trigger A with Run Trigger action
<Additional topics will be added if they are requested>
Also, there's two big advantages to using this method of MUI:
1) If you have an MUI system (e.g. u are a noob's GUI MUI system), this will not conflict with it. You are not assigning custom values to units who are actively taking part in the game, just a 'ghost' unit, who just does nothing but lets you refer back to arrays.
2) Also, it allows the same unit to cast the spell/be affected by the spell more than once. The casting unit isn't being assigned 2 custom values (one of which will be overwritten by the other), he is just being given 2 dummies, each dummy having a different custom value.
1. Where do I start?
---Firstly, you need to make a base ability in the Object Editor which will allow the trigger to fire.
---Reasonable knowledge of GUI triggers is a bonus, but everything involved will be explained along the way so don't worry if you are a beginner.
2. What do I need?
---a) Your base ability which will cause the trigger to fire
---b) Some variables (here's a sample list for a DoT spell): CustomValue (integer, no array), Caster (unit, array), Target (unit, array), Damage (real, array), CurrentTime (real, array), MaxTime (real, array), SFX (special effect, array) DummyGroup (unit group, array), TempInteger (integer, no array) and finally, TempPt (point, no array)
---*Above is just a suggested, easy-to-recognise variable names, their variable type, and whether or not they require an array. The array size defined in the Variable Editor doesn't matter (that just defines an initial value for the array)
---c) An idea of what you want to do (in this case, a spell that does X damage per second for Y seconds). Knowing what you want to do is the hardest part once you figure out how to utilise the information in this tutorial.
---* If you are trying to make a non-MUI spell become MUI, you will need to add the arrays to existing variables, similar to those mentioned above (CustomValue, tempinteger and temppt will NEVER require arrays. temppt is just used to spawn the dummy without creating leaks, and tempinteger just makes it easy to refer back to custom value of dummies)
---Pictures of variables used in demo map
3. MUI periodic events
---First, we need a trigger that will specify all the information we need, so that we can refer back to it in the periodic event.
---Now, notice that the custom value variable is always increasing in value by 1, until it gets to 1000. Once it is at 1000, the next cast will set it back to 1, cycling the values.
---Also, notice that the dummy unit's custom value, and ALL the variable arrays have the same value. This means that we always know which dummy refers back to which unit, number or point. Dummy with custom value of 17, always refers back to Caster[17], Target[17] etc
---Also, the dummy unit was added to a unit group. Why? Well, in the periodic trigger, we want to be able to handle all existing dummy units, without a really large trigger. So, if we add them to a unit group, we can just use the action "Pick every unit in Unit Group".
Now, let's get to the periodic event.
---Now, first things first, the event. Using "Every 1.00 seconds" is a small bit inefficient in this case, since if a unit casts the spell at 10.5 seconds of gametime, the damage will be taken 0.5 seconds earlier than it should, but that's negligible.
---Second, the condition. This just prevents the trigger from firing if there are no units in DummyGroup (it MAY cause lag with numerous triggers firing needlessly, and doing absolutely nothing when firing)
---Third, we are picking every unit in the unit group. This just makes it easier than doing the actions for each unit individually (which could take forever) and more reliable than using an integer loop (since you can't attempt to refer to a dummy unit who doesn't exist with groups, or mistakenly do something that shouldn't have been done i.e. deal damage when the unit isn't under the spells effect)
---*Note the "Set tempinteger" part. This isn't required but is highly recommended because (1) it saves time, referring to "Custom value of picked unit" is very time-consuming compared to referring to a variable and (2) it allows you to pick units in another unit group, within that dummy group loop (say if you wanted to damage multiple units in an area of effect)
---The If/Then/Else just checks if the dummy unit isn't needed anymore i.e. the target is dead, it doesn't need to take more damage OR duration has expired, target should no longer take damage. In this case, the dummy is removed from the game (with Remove Unit). This frees up the custom value used by the dummy, so it can be re-used later, when the CustomValue variable goes from from 1000, back down to 1 and repeats it's cycle.
---The Damage Unit action explains itself. Caster[the array is tempinteger, and tempinteger is the picked unit's custom value. This links the dummy unit to Caster, and every other variable that is used in a similar way i.e. Target, DPS, CurrentTime] will damage Target[tempinteger]. Then a floating text is created just to prove that the damage being damage being dealt is (somewhat) unique to that unit. In my demo, it's not unique since it can only be 1 of 15 values which is fairly limited but that's not really relevant ^^.
4. Avoiding waits with periodic events.
---I'm sure that everyone here knows about waits. They are a pain, they can be useful if you only need an ability to be used once at a time, but if you want to make an MUI ability, they are your worst enemy. So, we have to work around them to make something that works in much the same way, but without the limitations that waits can impose.
---Firstly, we make a spellcast trigger, which will do the very same thing as before (set your custom value figure, our variables and assign a dummy unit to a unit etc)
---I used a different custom value variable this time, since using the same custom value variable can limit the amount of times the spells can be cast i.e. using 2 spells and 1 custom value variable can limit the MUI to alot less possible instances for each which could be potentially bad.
I also used a different unit group variable, for similar reasons and also to prevent text appears over units who have nothing to do with the testing ability (which is Slam)
*Ignore the Ticks variable, that's unused.
---WaitTime is the only big thing in this trigger, this will determine the time before each floating text appears in the periodic trigger.
Also, the variable A is the equivalent of the tempinteger variable used in section 3.
---That may look like an awfully long trigger, but everything within the if/then/else grouping is practically copy-pasting with changed values for the conditions (and the Remove Unit in the last if/then/else).
As you can see, we are progressively increasing the WaitTime variable for each unit in DummyGroup2, and when it becomes a certain value, a text will appear to display the current wait time. Also, it is advisable to do something like
NOTICE: For periodic sliding triggers, this part of very important since slide triggers aren't really used with a periodic event greater than 0.04
---If your periodic trigger goes every... 0.04 seconds, and you want a wait for 0.25 seconds, you will never be able to multiply 0.04 by any whole number and get 0.25, it's impossible. So you have to get as close as you can. From the example above, the first multiple of 0.04 between (0.25-0.03) and (0.25+0.03), which would be 0.24, would trigger the actions. It's not as precise, but if you want absolute precision, you would need a periodic event of 0.01, and that can be laggy.
---This is also needed if you want to do something such as create a special effect every 0.25 seconds, all within the slide trigger so you don't have to create another trigger, and repeat everything you've already done
*If you use another method to control when an action occurs (say you wanted to check when a slide should end, based on the unit's distance travelled from the original position), this isn't of much use since it's based around elapsed time.
Also, if you had
*Make sure the conditions are right. When you are checking WaitTime = X, you must add the wait value of your current wait, to all previous waits so
That would replace the 2 waits shown above.
5. Buffs
Some things about using buffs with this:
---Using buffs with this can be slightly problematic (if the duration of abilities is randomized, or based on a variable such as hero stats etc). Since you can't guarantee that a buff from a spell (like Slow for example) will be removed immediately once the unit is no longer under the spell's effect. If you wish to use buffs, make them so that they only use a tooltip and an icon, no special effect. This way, you can trigger the special effect so that if the buff is removed too early or too late, it will be possible to tell if a unit is under the effects of an ability or not.
---Alternatively, you could use an infinite duration ability with nullified values (e.g. Slow with 0% speed reduction). Then, when a unit is no longer affected by the spell, we set the variable it was assigned (Target for example) to No Unit. Then, we check if any dummy units are assigned to unit X (the unit under the abilities effect). If there are dummies assigned to unit X, we don't do anything to the buff, since it's still required. But, if no dummies are assigned to unit X (therefore, the unit is no longer under the abilities effect), we can safely remove the buff from unit X.
---Another problem with buffs is that they may be overwritten by another buff, from another ability (let's take slow for example). If you cast your dummy slow (which is only used to apply a buff), and another unit casts a normal slow (which actually slows the unit), your dummy buff may be overwritten so SFX could disappear.
---An alternative is using dummy auras, with all values set to 0, and add it to the unit. This will give an special FX, a buff, and from what I remember, the buff won't be overwritten by another aura, but the aura with greater values should take precedence, so your dummy aura -shouldn't- cause a unit's auras to be negated. Anyway, your dummy aura will always be level 1. Aura levels take priority from what I remember, so a level 2 normal aura should always override the dummy aura, leaving 2 buffs, but only the level 2 aura will have an effect.
6. Credits: If you use the Meteor Shower demo spell in your map, please give credits.
7. Now, a practical example of MUI periodic events, and MUI wait avoidance in action
---Beware: This is a bit more advanced than the previous stuff (nothing too great, just uses an extra unit group and a few more variables for easy referencing). Extra unit group just makes it less likely for the MUI-ness to be ruined (if I added meteor dummies to the same group as unused dummies, it could very easily cause problems i.e. if you had 200 units casting the ability at the same time, it would only take about 3.5 seconds before the meteor dummy gets a custom value of 1, which could cause conflict with ownership (MeteorDummy[1] may not be owned by Caster[1])
Basically, an ability that rains down meteors (starfall FX) every 0.75 seconds (periodic event), dealing damage upon impact (wait avoidance).
---This will also show you how to clear variables and remove the correct dummy unit when a unit stops channelling (absolute requirement if you want GUI MUI channelling spells, otherwise you could have permanent effects)
---This is just a nice example spell I thought of making, and can be used for learning purposes, or for adding to a map if you wish.
---Update: Just added an alternative meteor shower demo which uses the starfall's projectile explosion effect, rather than creating a new special effect. It's nothing major, but it just looks a little bit more natural than thunder clap (although the size of the impact is a bit large ^^). Meteor shower update is the alternate file
8. Running trigger B from trigger A using Run Trigger action
---Just thought of something else. If you want to use the Run Trigger action, you will need a boolean variable (name it whatever you want) with an array of 1000 (or whatever your instance limit is). When you initially cast the spell, set your variables, make your dummy and set its custom value, add it to your dummy group THEN set <Boolean>[Custom value variable] = True, and run the other trigger.
---Now, in the other trigger, pick every unit in the dummy group, set your reference integer variable (tempinteger = custom value of picked unit) and add an If/Then/Else. The condition will be <Boolean>[tempinteger] = True. After that, add your actions and set <Boolean>[tempinteger] back to false, to prevent issues (re-occurence of spell effect for a unit who hasn't casted recently perhaps ^^)
Credits to Tinki3 for the great spell test map template.
MUI spells in GUI
-So, you want to make MUI spells in GUI (only JASS required is for leak removal). Well, you've come to the right place
What is MUI?
--MUI is Multi-Unit-Instanceability. But what does this mean to all those who don't know? It means that if 2 or more units (owned by any player, but it still has to work with 2 units owned by Player 1 for example) cause a trigger to fire (casting a spell would be best example), the spell's effect will work properly for both units that cast the ability. Also, MUI is -nearly- impossible to achieve with waits. I've seen an MUI ability with waits before, but it was only AoE (since you can't ensure that a specific unit variable will be retained after a wait, if another unit casts the ability).
This tutorial will explain everything involved in creating an MUI spell (well, everything involved in my preferred method of doing it). This method of making spells MUI relies mainly on periodic events (you need something to cause the periodic events to fire i.e. units in a unit group), but you can do alot of things within that periodic event such as waiting (without using Wait action), a more precise periodic event (if you wanted something to occur every 1 second, but the timing had to be right i.e. cast a spell at 10.5 seconds gametime, the effect should occur at 11.5 seconds gametime. A 1 second periodic event trigger wouldn't be able to do this correctly and timing would be incorrect)
Index:
1. Where do I start?
2. What do I need?
3. MUI periodic events (DoT spell will be used as an example).
4. Avoiding waits with periodic events.
5. Buffs
6. Credits (order is a bit mixed up unfortunately)
7. Practical example of MUI periodic events and MUI wait avoidance.
8. Running trigger B from trigger A with Run Trigger action
<Additional topics will be added if they are requested>
Also, there's two big advantages to using this method of MUI:
1) If you have an MUI system (e.g. u are a noob's GUI MUI system), this will not conflict with it. You are not assigning custom values to units who are actively taking part in the game, just a 'ghost' unit, who just does nothing but lets you refer back to arrays.
2) Also, it allows the same unit to cast the spell/be affected by the spell more than once. The casting unit isn't being assigned 2 custom values (one of which will be overwritten by the other), he is just being given 2 dummies, each dummy having a different custom value.
1. Where do I start?
---Firstly, you need to make a base ability in the Object Editor which will allow the trigger to fire.
---Reasonable knowledge of GUI triggers is a bonus, but everything involved will be explained along the way so don't worry if you are a beginner.
2. What do I need?
---a) Your base ability which will cause the trigger to fire
---b) Some variables (here's a sample list for a DoT spell): CustomValue (integer, no array), Caster (unit, array), Target (unit, array), Damage (real, array), CurrentTime (real, array), MaxTime (real, array), SFX (special effect, array) DummyGroup (unit group, array), TempInteger (integer, no array) and finally, TempPt (point, no array)
---*Above is just a suggested, easy-to-recognise variable names, their variable type, and whether or not they require an array. The array size defined in the Variable Editor doesn't matter (that just defines an initial value for the array)
---c) An idea of what you want to do (in this case, a spell that does X damage per second for Y seconds). Knowing what you want to do is the hardest part once you figure out how to utilise the information in this tutorial.
---* If you are trying to make a non-MUI spell become MUI, you will need to add the arrays to existing variables, similar to those mentioned above (CustomValue, tempinteger and temppt will NEVER require arrays. temppt is just used to spawn the dummy without creating leaks, and tempinteger just makes it easy to refer back to custom value of dummies)
IMPORTANT!
You should never turn off a periodic trigger if there is a spell instance running, it is just plain wrong and will only break stuff. There is nothing wrong with setting a periodic trigger to Initially Off, then enabling it when an instance is created (and said instance is the only one running), then disabling the periodic trigger once all instances have finished---Pictures of variables used in demo map
3. MUI periodic events
---First, we need a trigger that will specify all the information we need, so that we can refer back to it in the periodic event.
Code:
MUI demonstration cast
Events
Unit - A unit Starts the effect of an ability
Conditions
(Ability being cast) Equal to DPS test
Actions
-------- This will cycle the custom value used for the particular instance of the spell, so that it will always be changing when the ability is cast. --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
CustomValue Less than 1000
Then - Actions
Set CustomValue = (CustomValue + 1)
Else - Actions
Set CustomValue = 1
-------- Setting our variables, making sure that the array value is [CustomValue] --------
Set Caster[CustomValue] = (Triggering unit)
Set Target[CustomValue] = (Target unit of ability being cast)
Set CurrentDuration[CustomValue] = 0.00
Set MaxDuration[CustomValue] = 5.00
-------- If you didn't set a value for the DPS, this will assign it a random value between 10 and 25 --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
DPS[CustomValue] Equal to 0.00
Then - Actions
Set DPS[CustomValue] = (Random real number between 10.00 and 25.00)
Else - Actions
Set temppt = (Center of (Playable map area))
-------- Creating a dummy unit, giving it a custom value of (CustomValue). We will need to refer back to this later to find out who is the correct target and what is the correct damage to be dealt. --------
Unit - Create 1 Dummy Unit for (Owner of Caster[CustomValue]) at temppt facing Default building facing degrees
Unit - Set the custom value of (Last created unit) to CustomValue
-------- Adding the dummy unit to the dummy group, so we can pick all dummy units, and refer to each of their custom values, and the DPS and Target variables to which the dummy unit is 'attached' to. --------
Unit Group - Add (Last created unit) to DummyGroup
Custom script: call RemoveLocation (udg_temppt)
---Now, notice that the custom value variable is always increasing in value by 1, until it gets to 1000. Once it is at 1000, the next cast will set it back to 1, cycling the values.
---Also, notice that the dummy unit's custom value, and ALL the variable arrays have the same value. This means that we always know which dummy refers back to which unit, number or point. Dummy with custom value of 17, always refers back to Caster[17], Target[17] etc
---Also, the dummy unit was added to a unit group. Why? Well, in the periodic trigger, we want to be able to handle all existing dummy units, without a really large trigger. So, if we add them to a unit group, we can just use the action "Pick every unit in Unit Group".
Now, let's get to the periodic event.
Code:
MUI demonstration periodic
Events
Time - Every 1.00 seconds of game time
Conditions
(Number of units in DummyGroup) Greater than 0
Actions
-------- Above condition prevents the trigger from firing when there are no units under the abilities effect --------
-------- This is going to pick every dummy unit that was added to the group in the spellcast. Once the duration ends or the target unit dies, the dummy will be totally removed from the game --------
-------- This way, the condition won't be met if there are no units under the spell's effect. --------
Unit Group - Pick every unit in DummyGroup and do (Actions)
Loop - Actions
-------- This is just to make referencing the dummy unit's custom value MUCH easier than going through the lists of integers to find it. --------
Set tempinteger = (Custom value of (Picked unit))
-------- This is checking the conditions that are necessary for the spell to end. If the target unit dies, or the duration expires, the dummy is removed so nothing more will happen. --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
Or - Any (Conditions) are true
Conditions
(Target[tempinteger] is dead) Equal to True
CurrentDuration[tempinteger] Greater than or equal to MaxDuration[tempinteger]
Then - Actions
Unit - Remove (Picked unit) from the game
Else - Actions
-------- This will deal the damage to the target unit. --------
Unit - Cause Caster[tempinteger] to damage Target[tempinteger], dealing DPS[tempinteger] damage of attack type Spells and damage type Normal
-------- This will add some floating text above the unit, to show the damage that should be dealt without armor type/reduction. --------
Floating Text - Create floating text that reads (String((Integer(DPS[tempinteger])))) above Target[tempinteger] with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
-------- And the elapsed time for the ability will increase here. --------
Set CurrentDuration[tempinteger] = (CurrentDuration[tempinteger] + 1.00)
---Now, first things first, the event. Using "Every 1.00 seconds" is a small bit inefficient in this case, since if a unit casts the spell at 10.5 seconds of gametime, the damage will be taken 0.5 seconds earlier than it should, but that's negligible.
---Second, the condition. This just prevents the trigger from firing if there are no units in DummyGroup (it MAY cause lag with numerous triggers firing needlessly, and doing absolutely nothing when firing)
---Third, we are picking every unit in the unit group. This just makes it easier than doing the actions for each unit individually (which could take forever) and more reliable than using an integer loop (since you can't attempt to refer to a dummy unit who doesn't exist with groups, or mistakenly do something that shouldn't have been done i.e. deal damage when the unit isn't under the spells effect)
---*Note the "Set tempinteger" part. This isn't required but is highly recommended because (1) it saves time, referring to "Custom value of picked unit" is very time-consuming compared to referring to a variable and (2) it allows you to pick units in another unit group, within that dummy group loop (say if you wanted to damage multiple units in an area of effect)
---The If/Then/Else just checks if the dummy unit isn't needed anymore i.e. the target is dead, it doesn't need to take more damage OR duration has expired, target should no longer take damage. In this case, the dummy is removed from the game (with Remove Unit). This frees up the custom value used by the dummy, so it can be re-used later, when the CustomValue variable goes from from 1000, back down to 1 and repeats it's cycle.
---The Damage Unit action explains itself. Caster[the array is tempinteger, and tempinteger is the picked unit's custom value. This links the dummy unit to Caster, and every other variable that is used in a similar way i.e. Target, DPS, CurrentTime] will damage Target[tempinteger]. Then a floating text is created just to prove that the damage being damage being dealt is (somewhat) unique to that unit. In my demo, it's not unique since it can only be 1 of 15 values which is fairly limited but that's not really relevant ^^.
4. Avoiding waits with periodic events.
---I'm sure that everyone here knows about waits. They are a pain, they can be useful if you only need an ability to be used once at a time, but if you want to make an MUI ability, they are your worst enemy. So, we have to work around them to make something that works in much the same way, but without the limitations that waits can impose.
---Firstly, we make a spellcast trigger, which will do the very same thing as before (set your custom value figure, our variables and assign a dummy unit to a unit etc)
Code:
Spellcast
Events
Unit - A unit Starts the effect of an ability
Conditions
(Ability being cast) Equal to Slam (Neutral Hostile)
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
CV2 Less than 1000
Then - Actions
Set CV2 = (CV2 + 1)
Else - Actions
Set CV2 = 1
Set TextUnit[CV2] = (Triggering unit)
Set WaitTime[CV2] = 0.00
Set Ticks[CV2] = 0
Set temppt = (Center of (Playable map area))
Unit - Create 1 Dummy Unit for (Owner of TextUnit[CV2]) at temppt facing Default building facing degrees
Unit - Set the custom value of (Last created unit) to CV2
Unit Group - Add (Last created unit) to DummyGroup2
Custom script: call RemoveLocation (udg_temppt)
---I used a different custom value variable this time, since using the same custom value variable can limit the amount of times the spells can be cast i.e. using 2 spells and 1 custom value variable can limit the MUI to alot less possible instances for each which could be potentially bad.
I also used a different unit group variable, for similar reasons and also to prevent text appears over units who have nothing to do with the testing ability (which is Slam)
*Ignore the Ticks variable, that's unused.
---WaitTime is the only big thing in this trigger, this will determine the time before each floating text appears in the periodic trigger.
Also, the variable A is the equivalent of the tempinteger variable used in section 3.
Code:
Text creation
Events
Time - Every 0.05 seconds of game time
Conditions
(Number of units in DummyGroup2) Greater than 0
Actions
Unit Group - Pick every unit in DummyGroup2 and do (Actions)
Loop - Actions
Set A = (Custom value of (Picked unit))
Set WaitTime[A] = (WaitTime[A] + 0.05)
-------- Create text after 0.15 seconds --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
WaitTime[A] Equal to 0.15
Then - Actions
Floating Text - Create floating text that reads (MUI wait time. Time since spell cast: + (String(WaitTime[A]))) above TextUnit[A] with Z offset 0.00, using font size 8.50, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
Else - Actions
-------- Create text after 1.25 seconds --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
WaitTime[A] Equal to 1.25
Then - Actions
Floating Text - Create floating text that reads (MUI wait time. Time since spell cast: + (String(WaitTime[A]))) above TextUnit[A] with Z offset 0.00, using font size 8.50, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
Else - Actions
-------- Create text after 2.5 seconds --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
WaitTime[A] Equal to 2.50
Then - Actions
Floating Text - Create floating text that reads (MUI wait time. Time since spell cast: + (String(WaitTime[A]))) above TextUnit[A] with Z offset 0.00, using font size 8.50, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
Unit - Remove (Picked unit) from the game
Else - Actions
---That may look like an awfully long trigger, but everything within the if/then/else grouping is practically copy-pasting with changed values for the conditions (and the Remove Unit in the last if/then/else).
As you can see, we are progressively increasing the WaitTime variable for each unit in DummyGroup2, and when it becomes a certain value, a text will appear to display the current wait time. Also, it is advisable to do something like
Code:
WaitTime greater than or equal to (your inteded wait value - 0.03)
WaitTime less than or equal to (your intended wait value + 0.03)
NOTICE: For periodic sliding triggers, this part of very important since slide triggers aren't really used with a periodic event greater than 0.04
---If your periodic trigger goes every... 0.04 seconds, and you want a wait for 0.25 seconds, you will never be able to multiply 0.04 by any whole number and get 0.25, it's impossible. So you have to get as close as you can. From the example above, the first multiple of 0.04 between (0.25-0.03) and (0.25+0.03), which would be 0.24, would trigger the actions. It's not as precise, but if you want absolute precision, you would need a periodic event of 0.01, and that can be laggy.
---This is also needed if you want to do something such as create a special effect every 0.25 seconds, all within the slide trigger so you don't have to create another trigger, and repeat everything you've already done
*If you use another method to control when an action occurs (say you wanted to check when a slide should end, based on the unit's distance travelled from the original position), this isn't of much use since it's based around elapsed time.
Also, if you had
Code:
Wait 0.3 seconds
//Some actions
Wait 0.7 seconds
*Make sure the conditions are right. When you are checking WaitTime = X, you must add the wait value of your current wait, to all previous waits so
Code:
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
WaitTime[A] Equal to 0.30
Then - Actions
Floating Text - Create floating text that reads (MUI wait time. Time since spell cast: + (String(WaitTime[A]))) above TextUnit[A] with Z offset 0.00, using font size 8.50, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
Else - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
WaitTime[A] Equal to 1.00
Then - Actions
Floating Text - Create floating text that reads (MUI wait time. Time since spell cast: + (String(WaitTime[A]))) above TextUnit[A] with Z offset 0.00, using font size 8.50, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
Else - Actions
That would replace the 2 waits shown above.
5. Buffs
Some things about using buffs with this:
---Using buffs with this can be slightly problematic (if the duration of abilities is randomized, or based on a variable such as hero stats etc). Since you can't guarantee that a buff from a spell (like Slow for example) will be removed immediately once the unit is no longer under the spell's effect. If you wish to use buffs, make them so that they only use a tooltip and an icon, no special effect. This way, you can trigger the special effect so that if the buff is removed too early or too late, it will be possible to tell if a unit is under the effects of an ability or not.
---Alternatively, you could use an infinite duration ability with nullified values (e.g. Slow with 0% speed reduction). Then, when a unit is no longer affected by the spell, we set the variable it was assigned (Target for example) to No Unit. Then, we check if any dummy units are assigned to unit X (the unit under the abilities effect). If there are dummies assigned to unit X, we don't do anything to the buff, since it's still required. But, if no dummies are assigned to unit X (therefore, the unit is no longer under the abilities effect), we can safely remove the buff from unit X.
---Another problem with buffs is that they may be overwritten by another buff, from another ability (let's take slow for example). If you cast your dummy slow (which is only used to apply a buff), and another unit casts a normal slow (which actually slows the unit), your dummy buff may be overwritten so SFX could disappear.
---An alternative is using dummy auras, with all values set to 0, and add it to the unit. This will give an special FX, a buff, and from what I remember, the buff won't be overwritten by another aura, but the aura with greater values should take precedence, so your dummy aura -shouldn't- cause a unit's auras to be negated. Anyway, your dummy aura will always be level 1. Aura levels take priority from what I remember, so a level 2 normal aura should always override the dummy aura, leaving 2 buffs, but only the level 2 aura will have an effect.
6. Credits: If you use the Meteor Shower demo spell in your map, please give credits.
7. Now, a practical example of MUI periodic events, and MUI wait avoidance in action
---Beware: This is a bit more advanced than the previous stuff (nothing too great, just uses an extra unit group and a few more variables for easy referencing). Extra unit group just makes it less likely for the MUI-ness to be ruined (if I added meteor dummies to the same group as unused dummies, it could very easily cause problems i.e. if you had 200 units casting the ability at the same time, it would only take about 3.5 seconds before the meteor dummy gets a custom value of 1, which could cause conflict with ownership (MeteorDummy[1] may not be owned by Caster[1])
Basically, an ability that rains down meteors (starfall FX) every 0.75 seconds (periodic event), dealing damage upon impact (wait avoidance).
---This will also show you how to clear variables and remove the correct dummy unit when a unit stops channelling (absolute requirement if you want GUI MUI channelling spells, otherwise you could have permanent effects)
Code:
Meteor Shower Cast
Events
Unit - A unit Starts the effect of an ability
Conditions
(Ability being cast) Equal to Meteor Shower
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
CV1 Less than 1000
Then - Actions
Set CV1 = (CV1 + 1)
Else - Actions
Set CV1 = 1
Set Caster[CV1] = (Triggering unit)
Set CasterLoc[CV1] = (Position of Caster[CV1])
Unit - Create 1 Dummy Unit for (Owner of Caster[CV1]) at CasterLoc[CV1] facing Default building facing degrees
Unit - Set the custom value of (Last created unit) to CV1
Unit Group - Add (Last created unit) to DummyGroup
-----------------------------------------------------------------
Meteor Shower Periodic
Events
Time - Every 0.05 seconds of game time
Conditions
Actions
Unit Group - Pick every unit in DummyGroup and do (Actions)
Loop - Actions
Set TempInt1 = (Custom value of (Picked unit))
Set SpawnInterval[TempInt1] = (SpawnInterval[TempInt1] + 0.05)
-------- This If/Then/Else determines how often a meteor falls --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
SpawnInterval[TempInt1] Equal to 0.75
Then - Actions
Set SpawnInterval[TempInt1] = 0.00
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
CV2 Less than 1000
Then - Actions
Set CV2 = (CV2 + 1)
Else - Actions
Set CV2 = 1
Set Temp_Point = (CasterLoc[TempInt1] offset by (Random real number between 0.00 and 500.00) towards (Random angle) degrees)
Unit - Create 1 Meteor Dummy for (Owner of Caster[TempInt1]) at Temp_Point facing Default building facing degrees
Unit - Set the custom value of (Last created unit) to CV2
Set Meteor[CV2] = (Last created unit)
-------- This determines the damage dealt by the meteors upon impact. I made it 1x (INT) to make it much easier to configure the multiplier damage. --------
Set Damage[CV2] = (1.00 x (Real((Intelligence of Caster[TempInt1] (Exclude bonuses)))))
Unit Group - Add (Last created unit) to MeteorGroup
Custom script: call RemoveLocation (udg_Temp_Point)
Else - Actions
Unit Group - Pick every unit in MeteorGroup and do (Actions)
Loop - Actions
Set TempInt2 = (Custom value of (Picked unit))
Set ImpactDelay[TempInt2] = (ImpactDelay[TempInt2] + 0.05)
-------- It is advised to leave impact delay as it is, it's almost exactly the right timing when damage/SFX occurs and meteor impacts --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
ImpactDelay[TempInt2] Equal to 0.90
Then - Actions
Set ImpactDelay[TempInt2] = 0.00
Set Temp_Point = (Position of (Picked unit))
Special Effect - Create a special effect at Temp_Point using Abilities\Spells\Human\Thunderclap\ThunderClapCaster.mdl
Special Effect - Destroy (Last created special effect)
Set TempGroup = (Units within 250.00 of Temp_Point matching ((((Matching unit) is alive) Equal to True) and (((Matching unit) belongs to an enemy of (Owner of Meteor[TempInt2])) Equal to True)))
-------- Change the "within X of" to increase/decrease the damage radius --------
Unit Group - Pick every unit in TempGroup and do (Actions)
Loop - Actions
Unit - Cause Meteor[TempInt2] to damage (Picked unit), dealing Damage[TempInt2] damage of attack type Spells and damage type Normal
Custom script: call RemoveLocation (udg_Temp_Point)
Custom script: call DestroyGroup (udg_TempGroup)
Unit - Remove Meteor[TempInt2] from the game
Else - Actions
---------------------------------------------------------------
Meteor Shower End
Events
Unit - A unit Stops casting an ability
Conditions
(Ability being cast) Equal to Meteor Shower
Actions
-------- This just checks which unit stopped casting the ability, and removes it's assigned dummy and cleans up a leak --------
Unit Group - Pick every unit in DummyGroup and do (Actions)
Loop - Actions
Set TempInt1 = (Custom value of (Picked unit))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Triggering unit) Equal to Caster[TempInt1]
Then - Actions
Set Caster[TempInt1] = No unit
Custom script: call RemoveLocation (udg_CasterLoc[udg_TempInt1])
Unit - Remove (Picked unit) from the game
Else - Actions
---This is just a nice example spell I thought of making, and can be used for learning purposes, or for adding to a map if you wish.
---Update: Just added an alternative meteor shower demo which uses the starfall's projectile explosion effect, rather than creating a new special effect. It's nothing major, but it just looks a little bit more natural than thunder clap (although the size of the impact is a bit large ^^). Meteor shower update is the alternate file
8. Running trigger B from trigger A using Run Trigger action
---Just thought of something else. If you want to use the Run Trigger action, you will need a boolean variable (name it whatever you want) with an array of 1000 (or whatever your instance limit is). When you initially cast the spell, set your variables, make your dummy and set its custom value, add it to your dummy group THEN set <Boolean>[Custom value variable] = True, and run the other trigger.
---Now, in the other trigger, pick every unit in the dummy group, set your reference integer variable (tempinteger = custom value of picked unit) and add an If/Then/Else. The condition will be <Boolean>[tempinteger] = True. After that, add your actions and set <Boolean>[tempinteger] back to false, to prevent issues (re-occurence of spell effect for a unit who hasn't casted recently perhaps ^^)
Credits to Tinki3 for the great spell test map template.