• 🏆 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!

Checking if my MUI is correct

Level 7
Joined
Feb 23, 2020
Messages
253
Hello, i've starting to learn making MUI, and i'm just curious if this is correct. When i use a lot of points i get a bit confused so i just want to check if i made this the right way.

  • Free Fire Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Free Fire (Huntress)
    • Actions
      • -------- --------
      • Set VariableSet FreeFire_Index = (FreeFire_Index + 1)
      • Set VariableSet FreeFire_Caster[FreeFire_Index] = (Triggering unit)
      • Set VariableSet FreeFire_Point[FreeFire_Index] = (Target point of ability being cast)
      • -------- --------
      • Unit - Pause FreeFire_Caster[FreeFire_Index]
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FreeFire_Index Equal to 1
        • Then - Actions
          • Trigger - Turn on Free Fire Loop <gen>
        • Else - Actions
  • Free Fire Loop
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • For each (Integer FreeFire_Loop) from 1 to FreeFire_Index, do (Actions)
        • Loop - Actions
          • Animation - Play FreeFire_Caster[FreeFire_Loop]'s attack animation
          • Set VariableSet FreeFire_Counter[FreeFire_Loop] = (FreeFire_Counter[FreeFire_Loop] + 0.10)
          • -------- --------
          • Set VariableSet FreeFire_DummyPoint = (Position of FreeFire_Caster[FreeFire_Loop])
          • -------- --------
          • Unit - Create 1 Ability Dummy for (Owner of FreeFire_Caster[FreeFire_Loop]) at FreeFire_DummyPoint facing Default building facing degrees
          • Set VariableSet FreeFire_Dummy[FreeFire_Loop] = (Last created unit)
          • Custom script: call RemoveLocation(udg_FreeFire_DummyPoint)
          • Unit - Add a 1.00 second Generic expiration timer to FreeFire_Dummy[FreeFire_Loop]
          • Unit - Add Free Fire Dummy Ability to FreeFire_Dummy[FreeFire_Loop]
          • -------- --------
          • Set VariableSet FreeFire_Point2 = (FreeFire_Point[FreeFire_Loop] offset by ((Random real number between -300.00 and 300.00), (Random real number between -300.00 and 300.00)))
          • Unit - Order FreeFire_Dummy[FreeFire_Loop] to Orc Tauren Chieftain - Shockwave FreeFire_Point2
          • Custom script: call RemoveLocation(udg_FreeFire_Point2)
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FreeFire_Counter[FreeFire_Loop] Greater than or equal to 10.00
            • Then - Actions
              • Animation - Reset FreeFire_Caster[FreeFire_Loop]'s animation
              • Unit - Unpause FreeFire_Caster[FreeFire_Loop]
              • -------- --------
              • Set VariableSet FreeFire_Caster[FreeFire_Loop] = FreeFire_Caster[FreeFire_Index]
              • Set VariableSet FreeFire_Counter[FreeFire_Loop] = FreeFire_Counter[FreeFire_Index]
              • Set VariableSet FreeFire_Index = (FreeFire_Index - 1)
              • Set VariableSet FreeFire_Loop = (FreeFire_Loop - 1)
              • -------- --------
              • Custom script: call RemoveLocation(udg_FreeFire_Point[udg_FreeFire_Index])
              • -------- --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FreeFire_Index Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
            • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
I see five issues:

1) You need to reset FreeFire_Counter in the Cast trigger, otherwise it can be referenced again at a much higher value later on:
  • Actions
    • Set VariableSet FreeFire_Index = (FreeFire_Index + 1)
    • Set VariableSet FreeFire_Counter[FreeFire_Index] = 0.00
2) Working with Real variables and Arithmetic can and will lead to floating point error. What happens is your Real variable will end up as something like 9.99999 instead of 10.00 after increasing it by 0.10. This can make your triggers occur one too little or one too many times. This is unavoidable with Reals, but Integers don't suffer from this issue and can be used instead. Also, Reals can still be used, just check if FreeFire_Counter is Greater than or equal to 9.99 instead of 10.00.

3) You need to both Remove and "de-index" FreeFire_Point at the end of your Loop trigger. The way you're removing it now is wrong and out of place:
  • Then - Actions
    • Custom script: call RemoveLocation( udg_FreeFire_Point[udg_FreeFire_Loop] )
    • Set VariableSet FreeFire_Point[FreeFire_Loop] = FreeFire_Point[FreeFire_Index]
4) Pausing units is generally avoided outside of cinematics and special systems. A Paused unit will have it's buff timers frozen which leads to some very weird and unwanted results. Instead, you can Stun the unit reliably using this function:
  • Custom script: call BlzPauseUnitEx(udg_FreeFire_Caster[FreeFire_Index], true)
  • Custom script: call BlzPauseUnitEx(udg_FreeFire_Caster[FreeFire_Loop], false)
When set to true it stuns the unit, false unstuns the unit, but keep in mind that this function has a special behavior. It uses a hidden counter system where it can stack trues (stuns) and falses (unstuns). In other words, if you used two trues on a unit and then a false, the unit would still remain stunned because it will still have 1 true (stun) counter remaining. Easy rule of thumb, always make sure that when you Stun a unit (true) once, you ALWAYS Unstun it (false) once.

5) FreeFire_Dummy doesn't need to be an Array. You aren't tracking it over time and it's created and referenced all in one go and will be removed by it's expiration timer. Also, and this is nitpicky as well, but you can use FreeFire_Point[0] instead of FreeFire_Point2, saving you the need for a new variable.

Other than that, you did a good job.
 
Last edited:
Level 7
Joined
Feb 23, 2020
Messages
253
3) You need to both Remove and "de-index" FreeFire_Point at the end of your Loop trigger. The way you're removing it now is wrong and out of place:
  • Then - Actions
    • Custom script: call RemoveLocation( udg_FreeFire_Point[udg_FreeFire_Loop] )
    • Set VariableSet FreeFire_Point[FreeFire_Loop] = FreeFire_Point[FreeFire_Index]
I'm not quite sure about this part, do i need to set the FreeFire_Point variable inside the loop aswell?

Because the [udg_FreeFire_Loop] is an undeclared variable right now if i want to remove it
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
So before you subtract 1 from Index and Loop, you need to run those two Actions.

If it's time to end the spell then you basically do the following:

1) Do any spell specific Actions, like Unpausing your caster in your case.
2) Clean up memory leaks (remove location, destroy group).
3) Set ALL [Loop] array variables = their [Index] counterparts.
4) Subtract 1 from Loop/Index, and check if Index equals 0 in case it's time to turn off the trigger.
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • FreeFire_Counter[FreeFire_Loop] Greater than or equal to 10.00
    • Then - Actions
      • Custom script: call RemoveLocation(udg_FreeFire_Point[udg_FreeFire_Loop])
      • Animation - Reset FreeFire_Caster[FreeFire_Loop]'s animation
      • Unit - Unpause FreeFire_Caster[FreeFire_Loop] // replace this with BlzPauseUnitEx() if you'd like
      • -------- --------
      • -------- De-index your Arrays --------
      • Set VariableSet FreeFire_Caster[FreeFire_Loop] = FreeFire_Caster[FreeFire_Index]
      • Set VariableSet FreeFire_Counter[FreeFire_Loop] = FreeFire_Counter[FreeFire_Index]
      • Set VariableSet FreeFire_Point[FreeFire_Loop] = FreeFire_Point[FreeFire_Index]
      • -------- --------
      • Set VariableSet FreeFire_Index = (FreeFire_Index - 1)
      • Set VariableSet FreeFire_Loop = (FreeFire_Loop - 1)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FreeFire_Index Equal to 0
        • Then - Actions
          • Trigger - Turn off (This trigger)
        • Else - Actions
    • Else - Actions
Remember, if you don't "de-index" all of the necessary variables, which is what I'm calling the process of setting Loop vars = Index vars, then your spell is going to have bugs. This is because when one spell instance ends, any array variables that you have failed to de-index will still have their old value, and your next in line instance will now be using that data. So by forgetting to de-index FreeFire_Counter for example, then ALL of your other instances will end prematurely since they'll be referencing that variable (which is 10.00+) instead of their own version of FreeFire_Counter.
 
Last edited:
Level 7
Joined
Feb 23, 2020
Messages
253
Ok, i just have one quick question, if i'm making it mui and i have a spell like this one. Do i put the loop inside of the Index loop?

  • Angelic Light Loop
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Set VariableSet AngelicLight_Facing = (Facing of AngelicLight_Caster)
      • Set VariableSet AngelicLight_Timer = (AngelicLight_Timer + 1)
      • Set VariableSet AngelicLight_AreaGrowth = (AngelicLight_AreaGrowth + 5.00)
      • For each (Integer A) from AngelicLight_Timer to AngelicLight_Timer, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (AngelicLight_Caster is alive) Equal to True
            • Then - Actions
              • Set VariableSet AngelicLight_Point = (Position of AngelicLight_Caster)
              • Set VariableSet AngelicLight_DMGPoint = (AngelicLight_Point offset by (100.00 + AngelicLight_AreaGrowth) towards (10.00 x (Real((Integer A)))) degrees.)
              • Set VariableSet AngelicLight_SPXPoint = AngelicLight_DMGPoint
              • Set VariableSet AngelicLight_FacingPoint = AngelicLight_DMGPoint
              • Set VariableSet AngelicLight_Group = (Units within 200.00 of AngelicLight_DMGPoint matching (((((Matching unit) is A structure) Equal to False) and (((Matching unit) is alive) Equal to True)) and (((Matching unit) belongs to an enemy of (Owner of AngelicLight_Caster).) Equal to True)).)
              • -------- - --------
              • Unit Group - Pick every unit in AngelicLight_Group and do (Actions)
                • Loop - Actions
                  • Unit - Cause AngelicLight_Caster to damage (Picked unit), dealing AngelicLight_Dmg[(Level of Angelic Light for AngelicLight_Caster)] damage of attack type Spells and damage type Normal
              • Special Effect - Create a special effect at AngelicLight_SPXPoint using Abilities\Spells\Human\HolyBolt\HolyBoltSpecialArt.mdl
              • Special Effect - Destroy (Last created special effect)
              • Unit - Make AngelicLight_Caster face AngelicLight_FacingPoint over 0.00 seconds
              • -------- - --------
              • Custom script: call RemoveLocation(udg_AngelicLight_Point)
              • Custom script: call RemoveLocation(udg_AngelicLight_FacingPoint)
              • Custom script: call RemoveLocation(udg_AngelicLight_SPXPoint)
              • Custom script: call RemoveLocation(udg_AngelicLight_DMGPoint)
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • AngelicLight_Timer Equal to 108
        • Then - Actions
          • Trigger - Turn off (This trigger)
          • Set VariableSet AngelicLight_Timer = 0
          • Set VariableSet AngelicLight_AreaGrowth = 0.00
        • Else - Actions
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
You would set this up the same exact way you did FreeFire. This means you need to figure out what variables you're going to track over time:
1) AngelicLight_Timer
2) AngelicLight_AreaGrowth
3) AngelicLight_Caster

And convert those into Arrays since you want them to be MUI.

If those variables aren't arrays then how will the trigger work with multiple units? A non-array variable can only have one value at a time, so the idea of having 2+ units share that variable doesn't make any sense. Who is the AngelicLight_Caster if two different units cast the ability at roughly the same time? It's going to be whichever unit cast it most recently, even if that's just 1 frame later.
  • Angelic Light Loop
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • For each (Integer AngelicLight_Loop) from 1 to AngelicLight_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (AngelicLight_Caster[AngelicLight_Loop] is alive) Equal to True
              • AngelicLight_Timer[AngelicLight_Loop] Less than 108
            • Then - Actions
              • Set VariableSet AngelicLight_Timer[AngelicLight_Loop] = (AngelicLight_Timer[AngelicLight_Loop] + 1)
              • Set VariableSet AngelicLight_AreaGrowth[AngelicLight_Loop] = (AngelicLight_AreaGrowth[AngelicLight_Loop] + 5.00)
              • -------- - --------
              • Set VariableSet AngelicLight_Facing = (Facing of AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Level = (Level of Angelic Light for AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Point[0] = (Position of AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Point[1] = (AngelicLight_Point[0] offset by (100.00 + AngelicLight_AreaGrowth[AngelicLight_Loop]) towards (10.00 x (Real(AngelicLight_Timer[AngelicLight_Loop]))) degrees.)
              • Set VariableSet AngelicLight_Group = (Units within 200.00 of AngelicLight_Point[1] matching (((((Matching unit) is A structure) Equal to False) and (((Matching unit) is alive) Equal to True)) and (((Matching unit) belongs to an enemy of (Owner of AngelicLight_Caster).) Equal to True)).)
              • -------- - --------
              • Unit Group - Pick every unit in AngelicLight_Group and do (Actions)
                • Loop - Actions
                  • Unit - Cause AngelicLight_Caster[AngelicLight_Loop] to damage (Picked unit), dealing AngelicLight_Dmg[AngelicLight_Level] damage of attack type Spells and damage type Normal
              • Special Effect - Create a special effect at AngelicLight_Point[1] using Abilities\Spells\Human\HolyBolt\HolyBoltSpecialArt.mdl
              • Special Effect - Destroy (Last created special effect)
              • Unit - Make AngelicLight_Caster[AngelicLight_Loop] face AngelicLight_Point[1] over 0.00 seconds
              • -------- - --------
              • Custom script: call RemoveLocation(udg_AngelicLight_Point[0])
              • Custom script: call RemoveLocation(udg_AngelicLight_Point[1])
              • Custom script: call DestroyGroup(udg_AngelicLight_Group)
            • Else - Actions
              • -------- De-index --------
              • Set VariableSet AngelicLight_Timer[AngelicLight_Loop] = AngelicLight_Timer[AngelicLight_Index]
              • Set VariableSet AngelicLight_AreaGrowth[AngelicLight_Loop] = AngelicLight_AreaGrowth[AngelicLight_Index]
              • Set VariableSet AngelicLight_Caster[AngelicLight_Loop] = AngelicLight_Caster[AngelicLight_Index]
              • -------- - --------
              • Set VariableSet AngelicLoop = AngelicLoop - 1
              • Set VariableSet AngelicIndex = AngelicIndex - 1
              • If AngelicIndex Equal to 0 then Turn off (this trigger) Else do nothing
Note that the way I'm using the Else - Actions to de-index isn't perfect as it needs to wait an extra cycle to end itself. However, in the case of this spell it seems to be fine since nothing too fancy is going on here.

You'll also need an Index trigger just like FreeFire's which increases AngelicLight_Index by 1, sets Caster[], Timer[], and AreaGrowth[] to the correct starting values, and turns on your Loop trigger if Index is equal to 1.
 
Last edited:
Level 7
Joined
Feb 23, 2020
Messages
253
You would set this up the same exact way you did FreeFire. This means you need to figure out what variables you're going to track over time:
1) AngelicLight_Timer
2) AngelicLight_AreaGrowth
3) AngelicLight_Caster

And convert those into Arrays since you want them to be MUI.

If those variables aren't arrays then how will the trigger work with multiple units? A non-array variable can only have one value at a time, so the idea of having 2+ units share that variable doesn't make any sense. Who is the AngelicLight_Caster if two different units cast the ability at roughly the same time? It's going to be whichever unit cast it most recently, even if that's just 1 frame later.
  • Angelic Light Loop
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • For each (Integer AngelicLight_Loop) from 1 to AngelicLight_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (AngelicLight_Caster[AngelicLight_Loop] is alive) Equal to True
              • AngelicLight_Timer[AngelicLight_Loop] Less than 108
            • Then - Actions
              • Set VariableSet AngelicLight_Timer[AngelicLight_Loop] = (AngelicLight_Timer[AngelicLight_Loop] + 1)
              • Set VariableSet AngelicLight_AreaGrowth[AngelicLight_Loop] = (AngelicLight_AreaGrowth[AngelicLight_Loop] + 5.00)
              • -------- - --------
              • Set VariableSet AngelicLight_Facing = (Facing of AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Level = (Level of Angelic Light for AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Point[0] = (Position of AngelicLight_Caster[AngelicLight_Loop])
              • Set VariableSet AngelicLight_Point[1] = (AngelicLight_Point[0] offset by (100.00 + AngelicLight_AreaGrowth[AngelicLight_Loop]) towards (10.00 x (Real(AngelicLight_Timer[AngelicLight_Loop]))) degrees.)
              • Set VariableSet AngelicLight_Group = (Units within 200.00 of AngelicLight_Point[1] matching (((((Matching unit) is A structure) Equal to False) and (((Matching unit) is alive) Equal to True)) and (((Matching unit) belongs to an enemy of (Owner of AngelicLight_Caster).) Equal to True)).)
              • -------- - --------
              • Unit Group - Pick every unit in AngelicLight_Group and do (Actions)
                • Loop - Actions
                  • Unit - Cause AngelicLight_Caster[AngelicLight_Loop] to damage (Picked unit), dealing AngelicLight_Dmg[AngelicLight_Level] damage of attack type Spells and damage type Normal
              • Special Effect - Create a special effect at AngelicLight_Point[1] using Abilities\Spells\Human\HolyBolt\HolyBoltSpecialArt.mdl
              • Special Effect - Destroy (Last created special effect)
              • Unit - Make AngelicLight_Caster[AngelicLight_Loop] face AngelicLight_Point[1] over 0.00 seconds
              • -------- - --------
              • Custom script: call RemoveLocation(udg_AngelicLight_Point[0])
              • Custom script: call RemoveLocation(udg_AngelicLight_Point[1])
              • Custom script: call DestroyGroup(udg_AngelicLight_Group)
            • Else - Actions
              • -------- De-index --------
              • Set VariableSet AngelicLight_Timer[AngelicLight_Loop] = AngelicLight_Timer[AngelicLight_Index]
              • Set VariableSet AngelicLight_AreaGrowth[AngelicLight_Loop] = AngelicLight_AreaGrowth[AngelicLight_Index]
              • Set VariableSet AngelicLight_Caster[AngelicLight_Loop] = AngelicLight_Caster[AngelicLight_Index]
              • -------- - --------
              • Set VariableSet AngelicLoop = AngelicLoop - 1
              • Set VariableSet AngelicIndex = AngelicIndex - 1
              • If AngelicIndex Equal to 0 then Turn off (this trigger) Else do nothing
Note that the way I'm using the Else - Actions to de-index isn't perfect as it needs to wait an extra cycle to end itself. However, in the case of this spell it seems to be fine since nothing too fancy is going on here.

You'll also need an Index trigger just like FreeFire's which increases AngelicLight_Index by 1, sets Caster[], Timer[], and AreaGrowth[] to the correct starting values, and turns on your Loop trigger if Index is equal to 1.
This spell was for a single player unit in a map that i made, therefor not MUI in this case, i’m well aware. But i was not sure how this spell would work when making the MUI loop, since this spell ”from X to Y” in the loop increases by 1 each time intervall.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
I don't fully understand what you mean but what I provided is a fully MUI version of the spell along with other improvements.

But I'll tell you that this loop here doesn't make any sense:
  • For each (Integer A) from AngelicLight_Timer to AngelicLight_Timer, do (Actions)
Looping from X to X is pointless. The reason to use a Loop is to do things more than once or at least be capable of doing so. So a Loop should go from X to Y.

For instance, let's say AngelicLight_Timer is equal to 5. Your Loop would run once (5 to 5) and set (Integer A) to be equal to 5 during that single cycle.

The question is, why bother with that when you can just reference AngelicLight_Timer directly:
  • Set VariableSet AngelicLight_DMGPoint = (AngelicLight_Point offset by (100.00 + AngelicLight_AreaGrowth) towards (10.00 x (Real(AngelicLight_Timer))) degrees.)
 
Last edited:
Top