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

Need help making this MUI

Status
Not open for further replies.
Level 37
Joined
Jul 22, 2015
Messages
3,485
I'm making this spell as a request for someone, and for some reason it gets really buggy when the caster casts it on another group of units while the other is still running. The way the spell works is almost like the default Disease Cloud except its a point-target spell to get it started versus just walking next to them. I've been working on a bunch of other things so I'm a little tired... I may have missed something really obvious.

  • SP Init
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Cripple
    • Actions
      • Set SP_Target = (Target unit of ability being cast)
      • Set SP_MaxIndex = (SP_MaxIndex + 1)
      • Set SP_Caster[SP_MaxIndex] = (Triggering unit)
      • Set SP_Counter[SP_MaxIndex] = 0.00
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (SP_Target is in SP_BurnGroup[SP_MaxIndex]) Equal to False
        • Then - Actions
          • Unit Group - Add SP_Target to SP_BurnGroup[SP_MaxIndex]
          • Special Effect - Create a special effect attached to the overhead of SP_Target using Units\Undead\PlagueCloud\PlagueCloudtarget.mdl
          • Animation - Change SP_Target's vertex coloring to (65.00%, 100.00%, 65.00%) with 0.00% transparency
          • Set SP_SFXTarget[SP_MaxIndex] = (Last created special effect)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • SP_MaxIndex Equal to 1
            • Then - Actions
              • Trigger - Turn on SP Loop <gen>
            • Else - Actions
        • Else - Actions
  • SP Loop
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • For each (Integer SP_CurrentIndex) from 1 to SP_MaxIndex, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • SP_Counter[SP_CurrentIndex] Less than 6.00
            • Then - Actions
              • Unit Group - Pick every unit in SP_BurnGroup[SP_CurrentIndex] and do (Actions)
                • Loop - Actions
                  • Set SP_PickedUnitBG = (Picked unit)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (SP_PickedUnitBG is dead) Equal to True
                    • Then - Actions
                      • Unit Group - Remove SP_PickedUnitBG from SP_BurnGroup[SP_CurrentIndex]
                      • Special Effect - Destroy SP_SFXTarget[SP_CurrentIndex]
                    • Else - Actions
                      • Set SP_CheckGroup[SP_CurrentIndex] = (Units within 100.00 of (Position of SP_PickedUnitBG))
                      • Unit Group - Pick every unit in SP_CheckGroup[SP_CurrentIndex] and do (Actions)
                        • Loop - Actions
                          • Set SP_PickedUnitCG = (Picked unit)
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (SP_PickedUnitCG is A structure) Equal to False
                              • (SP_PickedUnitCG is in SP_BurnGroup[SP_CurrentIndex]) Equal to False
                            • Then - Actions
                              • Unit Group - Add SP_PickedUnitCG to SP_BurnGroup[SP_CurrentIndex]
                              • Animation - Change SP_PickedUnitCG's vertex coloring to (65.00%, 100.00%, 65.00%) with 0.00% transparency
                              • Special Effect - Create a special effect attached to the origin of SP_PickedUnitCG using Abilities\Spells\Undead\DeathCoil\DeathCoilSpecialArt.mdl
                              • Special Effect - Destroy (Last created special effect)
                            • Else - Actions
                      • Custom script: call DestroyGroup (udg_SP_CheckGroup[udg_SP_CurrentIndex])
              • Set SP_Counter[SP_CurrentIndex] = (SP_Counter[SP_CurrentIndex] + 0.50)
            • Else - Actions
              • Special Effect - Destroy SP_SFXTarget[SP_CurrentIndex]
              • Unit Group - Pick every unit in SP_BurnGroup[SP_CurrentIndex] and do (Actions)
                • Loop - Actions
                  • Set SP_PickedUnitBG = (Picked unit)
                  • Animation - Change SP_PickedUnitBG's vertex coloring to (100.00%, 100.00%, 100.00%) with 0.00% transparency
                  • Unit - Cause SP_Caster[SP_CurrentIndex] to damage SP_PickedUnitBG, dealing 10.00 damage of attack type Spells and damage type Normal
              • Custom script: call DestroyGroup (udg_SP_BurnGroup[udg_SP_CurrentIndex])
              • Set SP_Caster[SP_CurrentIndex] = SP_Caster[SP_MaxIndex]
              • Set SP_SFXTarget[SP_CurrentIndex] = SP_SFXTarget[SP_MaxIndex]
              • Set SP_Counter[SP_CurrentIndex] = SP_Counter[SP_MaxIndex]
              • Set SP_MaxIndex = (SP_MaxIndex - 1)
              • Set SP_CurrentIndex = (SP_CurrentIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • SP_MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
EDIT: Thank you to purge for the insight on unit group arrays
  • SP Init
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Cripple
    • Actions
      • Set SP_Target = (Target unit of ability being cast)
      • Set SP_MaxIndex = (SP_MaxIndex + 1)
      • Set SP_Caster[SP_MaxIndex] = (Triggering unit)
      • Set SP_Counter[SP_MaxIndex] = 0.00
      • Custom script: if udg_SP_BurnGroup[udg_SP_MaxIndex] == null then
      • Custom script: set udg_SP_BurnGroup[udg_SP_MaxIndex] = CreateGroup()
      • Custom script: endif
      • Unit Group - Add SP_Target to SP_BurnGroup[SP_MaxIndex]
      • Special Effect - Create a special effect attached to the overhead of SP_Target using Units\Undead\PlagueCloud\PlagueCloudtarget.mdl
      • Animation - Change SP_Target's vertex coloring to (65.00%, 100.00%, 65.00%) with 0.00% transparency
      • Set SP_SFXTarget[SP_MaxIndex] = (Last created special effect)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SP_MaxIndex Equal to 1
        • Then - Actions
          • Trigger - Turn on SP Loop <gen>
        • Else - Actions
  • SP Loop
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • For each (Integer SP_CurrentIndex) from 1 to SP_MaxIndex, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • SP_Counter[SP_CurrentIndex] Less than 6.00
            • Then - Actions
              • Unit Group - Pick every unit in SP_BurnGroup[SP_CurrentIndex] and do (Actions)
                • Loop - Actions
                  • Set SP_PickedUnitBG = (Picked unit)
                  • Set SP_PickedUnitBGLoc = (Position of SP_PickedUnitBG)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (SP_PickedUnitBG is dead) Equal to True
                    • Then - Actions
                      • Unit Group - Remove SP_PickedUnitBG from SP_BurnGroup[SP_CurrentIndex]
                      • Special Effect - Destroy SP_SFXTarget[SP_CurrentIndex]
                    • Else - Actions
                      • Set SP_CheckGroup[SP_CurrentIndex] = (Units within 100.00 of SP_PickedUnitBGLoc)
                      • Unit Group - Pick every unit in SP_CheckGroup[SP_CurrentIndex] and do (Actions)
                        • Loop - Actions
                          • Set SP_PickedUnitCG = (Picked unit)
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (SP_PickedUnitCG is A structure) Equal to False
                              • (SP_PickedUnitCG is in SP_BurnGroup[SP_CurrentIndex]) Equal to False
                            • Then - Actions
                              • Unit Group - Add SP_PickedUnitCG to SP_BurnGroup[SP_CurrentIndex]
                              • Animation - Change SP_PickedUnitCG's vertex coloring to (65.00%, 100.00%, 65.00%) with 0.00% transparency
                              • Special Effect - Create a special effect attached to the origin of SP_PickedUnitCG using Abilities\Spells\Undead\DeathCoil\DeathCoilSpecialArt.mdl
                              • Special Effect - Destroy (Last created special effect)
                            • Else - Actions
                      • Custom script: call RemoveLocation (udg_SP_PickedUnitBGLoc)
                      • Custom script: call DestroyGroup (udg_SP_CheckGroup[udg_SP_CurrentIndex])
              • Set SP_Counter[SP_CurrentIndex] = (SP_Counter[SP_CurrentIndex] + 0.50)
            • Else - Actions
              • Special Effect - Destroy SP_SFXTarget[SP_CurrentIndex]
              • Unit Group - Pick every unit in SP_BurnGroup[SP_CurrentIndex] and do (Actions)
                • Loop - Actions
                  • Set SP_PickedUnitBG = (Picked unit)
                  • Animation - Change SP_PickedUnitBG's vertex coloring to (100.00%, 100.00%, 100.00%) with 0.00% transparency
                  • Unit - Cause SP_Caster[SP_CurrentIndex] to damage SP_PickedUnitBG, dealing 10.00 damage of attack type Spells and damage type Normal
              • Custom script: call DestroyGroup (udg_SP_BurnGroup[udg_SP_CurrentIndex])
              • Custom script: set udg_SP_BurnGroup[udg_SP_CurrentIndex] = null
              • Set SP_Caster[SP_CurrentIndex] = SP_Caster[SP_MaxIndex]
              • Set SP_SFXTarget[SP_CurrentIndex] = SP_SFXTarget[SP_MaxIndex]
              • Set SP_Counter[SP_CurrentIndex] = SP_Counter[SP_MaxIndex]
              • Set SP_MaxIndex = (SP_MaxIndex - 1)
              • Set SP_CurrentIndex = (SP_CurrentIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • SP_MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
 
Last edited:
The first problem I see is that SP_BurnGroup should be initialized to an empty group.

When you create a group array in the variable editor, it has a Size field. This determines how many empty groups are created for the array. Blizzard didn't want to waste unnecessary memory, so they only create as many as you need. So if you have a size of 1, then only the first index (or the first 2?) are actually assigned to an empty group. If you try to access any other index, it'll just give you null. And if you try to work with a null variable, most of your actions will end up doing nothing.

In GUI, there isn't a function to create an empty group. You'll have to resort to JASS. When you are setting your variables, add this custom script block:
  • Set SP_Target = (Target unit of ability being cast)
  • Set SP_MaxIndex = (SP_MaxIndex + 1)
  • Set SP_Caster[SP_MaxIndex] = (Triggering unit)
  • Set SP_Counter[SP_MaxIndex] = 0.00
  • Custom script: if udg_SP_BurnGroup[udg_SP_MaxIndex] == null then
  • Custom script: set udg_SP_BurnGroup[udg_SP_MaxIndex] = CreateGroup()
  • Custom script: endif
Next, when you are finished with your spell, add this line of code immediately after you destroy the burn group:
  • Custom script: call DestroyGroup (udg_SP_BurnGroup[udg_SP_CurrentIndex])
  • Custom script: set udg_SP_BurnGroup[udg_SP_CurrentIndex] = null
What does this do? The first custom script block checks if the group points to null. If it does, then it will assign the variable to an empty group. The second script assigns the variable to null after you destroy it. The next time the spell runs for that index, it'll create the group (since the previous one was destroyed). :)

Hopefully this solves your issue.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
The first problem I see is that SP_BurnGroup should be initialized to an empty group.

When you create a group array in the variable editor, it has a Size field. This determines how many empty groups are created for the array. Blizzard didn't want to waste unnecessary memory, so they only create as many as you need. So if you have a size of 1, then only the first index (or the first 2?) are actually assigned to an empty group. If you try to access any other index, it'll just give you null. And if you try to work with a null variable, most of your actions will end up doing nothing.

In GUI, there isn't a function to create an empty group. You'll have to resort to JASS. When you are setting your variables, add this custom script block:
  • Set SP_Target = (Target unit of ability being cast)
  • Set SP_MaxIndex = (SP_MaxIndex + 1)
  • Set SP_Caster[SP_MaxIndex] = (Triggering unit)
  • Set SP_Counter[SP_MaxIndex] = 0.00
  • Custom script: if udg_SP_BurnGroup[udg_SP_MaxIndex] == null then
  • Custom script: set udg_SP_BurnGroup[udg_SP_MaxIndex] = CreateGroup()
  • Custom script: endif
Next, when you are finished with your spell, add this line of code immediately after you destroy the burn group:
  • Custom script: call DestroyGroup (udg_SP_BurnGroup[udg_SP_CurrentIndex])
  • Custom script: set udg_SP_BurnGroup[udg_SP_CurrentIndex] = null
What does this do? The first custom script block checks if the group points to null. If it does, then it will assign the variable to an empty group. The second script assigns the variable to null after you destroy it. The next time the spell runs for that index, it'll create the group (since the previous one was destroyed). :)

Hopefully this solves your issue.

Yess! Thank you, Purge :) updade my post. I also never knew that's how unit groups worked. Another thank you for this valuable information.

I also realized this was causing a lot of issues. I originally had one universal burn group they were all added to, but I stepped away from that approach because I assumed it wouldn't be MUI. Do you have any suggestions of another way of doing this?

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (SP_Target is in SP_BurnGroup[SP_MaxIndex]) Equal to False
    • Then - Actions
      • ~add unit to unit group~
      • ~turn on loop~
 
Hmm. This is actually a tough problem. There are a lot of potential solutions, but it really depends on the exact behavior you desire. Since you are doing this for a request, I assume you want as few systems as possible, so that is another thing for me to consider.


I'm going to make two distinctions:
targeted - means that the unit is set as SP_Target
group-targeted - means that the unit is added to a burn group during the periodic part

Here are some questions:
  • If a single unit is targeted by the spell twice, should it show two special effects?
  • If a single unit is targeted by the spell twice, should it have two instances going on at the same time? Another way to phrase this question: let's say the target has been selected as SP_Target twice. Should he burn the units around him once or twice?
  • If a unit is group-targeted twice, should he have one effect or two?

If you intend to do damage per tick, should they be damaged once or twice?

There are a lot of ways to approach this. If you go with a bad solution, then units will lose their vertex coloring/sfx early. The main questions are: do you want the effects to stack? Do you want the damage to stack?
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
The main questions are: do you want the effects to stack? Do you want the damage to stack?

I'd rather have neither xP if I had to chose one, I would rather have the effects stack. The one who requested this spell asked for the damage to deal a % of their maximum HP after X amount of seconds from infecting the targeted unit, so there will be no "ticking." The damage only happens once, and that will be at the end of the spell.
 
I see. One more question. So let's say a unit has burn applied to him because he is next to a burn unit.

It applies at time = 0 seconds. Let's say that the burn lasts 10 seconds, and then it deals damage.

At time = 5 seconds, he gets burnt again.

Would you want the damage to be dealt at t = 10 seconds, t = 15 seconds, or both?

I assume if you only want the damage to be dealt once, then it should probably be only dealt at t = 10 seconds. In a sense, the units would be "immune" to burn reapplications until their first burn is over. I should probably be asking this directly to the requester, but idk what the request is for, heh.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
I see. One more question. So let's say a unit has burn applied to him because he is next to a burn unit.

It applies at time = 0 seconds. Let's say that the burn lasts 10 seconds, and then it deals damage.

At time = 5 seconds, he gets burnt again.

Would you want the damage to be dealt at t = 10 seconds, t = 15 seconds, or both?

I assume if you only want the damage to be dealt once, then it should probably be only dealt at t = 10 seconds. In a sense, the units would be "immune" to burn reapplications until their first burn is over.

At 10 seconds, yes :) the unit is already "infected", so it wouldn't make sense to infect them again.

I should probably be asking this directly to the requester, but idk what the request is for, heh.

Lol the request seemed so simple when I read it. However, when I started to pseudo code it, it just turned into this Unit Group hot mess. I'm considering uploading this as maybe my first resource on hive, what do you think?
 
Ah cool. I don't have much time to write out full code, but I'll give you the gist of what you need to do. You'll mostly have the same code with a few additions:

(1) Make a group variable (don't make it an array). I'll name it SP_Infected for this explanation.
(2) Each time you add a unit to SP_BurnGroup, add that unit to SP_Infected
(3) Before you add a unit to SP_BurnGroup/apply the special fx, check if the unit is in SP_Infected. If he is in SP_Infected, do nothing. If he isn't in SP_Infected, do all the usual stuff, and then add him to SP_Infected.
(4) At the end of the spell (when you deal damage to the units), remove those damaged units from SP_Infected
(5) Also, at the start of the spell (before you do SP_MaxIndex = SP_MaxIndex + 1), you should check if the unit is in SP_Infected. If he is, then you should give an error message or something saying that the unit is already burned/infected. (you can use SimError to simulate the error message)
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
At 10 seconds, yes :) the unit is already "infected", so it wouldn't make sense to infect them again.

This is actually makes a different in Spell Crafting;
Indexing
Hashtable

Indexing
You can stack multiple "handle" or count to a single unit reference.

Example:
Infects a target unit causing it to take 20 damage per second for 10 seconds.

On t = 1, you infect this unit
On t = 2, you infect this unit
On t = 3, you infect this unit

On t = 4, this unit should take 60 damage per second, as the spell can be stacked, but the remaining duration of the spell is 7/8/9 seconds respectively.


Hashtable
You can refresh the duration of the "handle" or count to a single unit reference.

On t = 1, you infect this unit
On t = 2, you infect this unit
On t = 3, you infect this unit

On t = 4, this unit should take 20 damage per second, but the remaining duration of the spell is 10 seconds.


I don't know if you already know this - just sharing, coder-to-coder experience :)
This is very important aspect of Spell Crafting as it will determine the "nature" of your spells.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
This is actually makes a different in Spell Crafting;
Indexing
Hashtable

Indexing
You can stack multiple "handle" or count to a single unit reference.

Example:
Infects a target unit causing it to take 20 damage per second for 10 seconds.

On t = 1, you infect this unit
On t = 2, you infect this unit
On t = 3, you infect this unit

On t = 4, this unit should take 60 damage per second, as the spell can be stacked, but the remaining duration of the spell is 7/8/9 seconds respectively.


Hashtable
You can refresh the duration of the "handle" or count to a single unit reference.

On t = 1, you infect this unit
On t = 2, you infect this unit
On t = 3, you infect this unit

On t = 4, this unit should take 20 damage per second, but the remaining duration of the spell is 10 seconds.


I don't know if you already know this - just sharing, coder-to-coder experience :)
This is very important aspect of Spell Crafting as it will determine the "nature" of your spells.

Hi defskull :) thanks for the insight. However, this was just for a spell request and I've already finished that "portion". I went ahead and added more features to the base spell though :) Check out the triggers there!
 
Status
Not open for further replies.
Top