Help with over time spell damage in MUI

Hello and greetings if anyone would be able to read this thread. I need help with the spell I am working on. I've been trying to follow some basic in making MUI spells in the tutorial but mine seems not to fire correctly at all. So this spell that I am currently working, holds a target in place for some time and while the unit is paused unable to do anything it is also being damaged every 0.5 second.

So my trigger starts with the effect of an ability, then a dummy cast a spell on it for effect and for holding it in place. After which I set indexes for the target and the caster then set some booleans to true so that the spell can damage the target every 0.5 second and for checking when to turn the trigger off, however, when I tested the map there was no damage at all. I wonder why, wether if I turn the trigger on via If Integer = 1 turn on trigger or set the boolean to true, the other trigger that fires the damage to the tarvet every 0.5 second never work and I'm so bugged at it.

For the damage part I use the action: For each Integer variable from 1 to "index", I damage the target using damage target by recalling the casters index and the targets index using the lnteger variable. None of it worked so I am really asking why is this happening.
 
So this is my trigger

  • Frostbite
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Frostbite
    • Actions
      • Custom script: local location fbt = null
      • Custom script: set fbt = GetUnitLoc(GetSpellTargetUnit())
      • Custom script: call CreateNUnitsAtLoc( 1, 'aci2', GetOwningPlayer(GetTriggerUnit()), fbt, bj_UNIT_FACING )
      • Unit - Add Frostbite (Dummy Spell) to (Last created unit)
      • Unit - Set level of Frostbite (Dummy Spell) for (Last created unit) to (Level of Frostbite for (Triggering unit))
      • Unit - Order (Last created unit) to Orc Raider - Ensnare (Target unit of ability being cast)
      • Unit - Cause (Triggering unit) to damage (Target unit of ability being cast), dealing 50.00 damage of attack type Spells and damage type Cold
      • Unit - Add a 1.50 second Generic expiration timer to (Last created unit)
      • Custom script: call RemoveLocation ( fbt )
      • Custom script: set fbt = null
      • Set FB_Index = (FB_Index + 1)
      • Set FB_Caster[FB_Index] = (Triggering unit)
      • Set FB_Target[FB_Index] = (Target unit of ability being cast)
      • Set FB_Level = (Level of Frostbite for FB_Caster[FB_Index])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (FB_Target[FB_Index] is A Hero) Equal to True
        • Then - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 1) + 2)
        • Else - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 3) + 1)
      • Set FB_Boolean = True
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FB_Index Equal to 1
        • Then - Actions
          • Trigger - Turn on Frostbite Damage Copy <gen>
        • Else - Actions
  • Frostbite Damage
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • FB_Boolean Equal to True
          • FB_Index Greater than 0
    • Actions
      • For each (Integer FBLoopInteger) from 1 to FB_Index, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FB_Duration[FBLoopInteger] Not equal to 0
            • Then - Actions
              • Unit - Cause FB_Caster[FBLoopInteger] to damage FB_Target[FBLoopInteger], dealing 50.00 damage of attack type Spells and damage type Cold
              • Set FB_Duration[FBLoopInteger] = (FB_Duration[FBLoopInteger] - 1)
            • Else - Actions
              • Set FB_Caster[FBLoopInteger] = FB_Caster[FB_Index]
              • Set FB_Target[FBLoopInteger] = FB_Target[FB_Index]
              • Set FB_Duration[FBLoopInteger] = FB_Duration[FB_Index]
              • Set FB_Index = (FB_Index - 1)
              • Set FBLoopInteger = (FBLoopInteger - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FB_Index Equal to 0
                • Then - Actions
                  • Set FB_Boolean = False
                  • Trigger - Turn off (This trigger)
                • Else - Actions
I hope you will be able to check it.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I think you're actually just enabling the wrong trigger. In "Frostbite <gen>" you do this: Trigger - Turn on Frostbite Damage Copy <gen>... but the other trigger you posted here is just called "Frostbite Damage <gen>" without the "copy".
  • There's no reason to use a local location fbt like that. You're doing it exactly how GUI would do it and using neither the benefits of coordinates (which JASS has easy access to) or locals, so I just don't see why cause additional headache for no gain.

  • There's no reason to have a boolean and counter and also manually turn your periodic trigger on/off and also use a condition in the periodic trigger to check if it should run. That's extremely redundant and you can reduce it down to just keeping FB_Index as a counter and using that to turn the trigger on/off (no conditions and no boolean).

  • Set your variables like FB_Caster[] at the very beginning of the cast trigger trigger so that you can refer to those variables throughout the trigger rather than 'recomputing' them a few times. Like this line: Unit - Cause (Triggering unit) to damage (Target unit of ability being cast), dealing 50.00 damage of attack type Spells and damage type Cold

  • Usually recommended to compare using inequalities (>, <, >=, or <=) rather than equalities (== !=). If something happens and your duration skips to -1 through some bizarre error that instance will never be removed from the list. FB_Duration[FBLoopInteger] Not equal to 0 should instead be FB_Duration[FBLoopInteger] Less than or equal to 0
Consider that if this periodic trigger is already running, units casting a new instance of the spell might deal their second tick of damage (after the cast trigger tick) immediately or be delayed by up to the intended 0.5s, depending on how much of the current period was remaining upon cast. This issue can be minimized with higher frequency triggers (if this ran every 0.25s then the maximum delay would be 0.25s) where you only take action every N cycles rather than always taking action each cycle. Usually this effect matters little, but when the period is long it's very noticeable.
 
I actually enabled the Frostbite Damage Copy gen, I just deleted the copy word when I uploaded it.

And oh, don't worry about the local, I just made sure I declared the point so I can nullify it in the end rather than doing it like how it used to be in GUI. I've been testing the map somehow for quite some time and I don't know if it's just me, but the memory consumption, as I see it in task manager, accumulates faster with out doing this.

And I will do as you say. I'll see if it could run properly.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Then I suggest you put a bunch of debug messages throughout your triggers to determine what exactly runs and how often it runs. You can use the normal GUI text display or BJDebugMsg(). You can print static text, the value of variables, unit names/health/handle-ids, etc. to get a better picture since wc3 doesn’t have a step-by-step debugger.
 
Then I suggest you put a bunch of debug messages throughout your triggers to determine what exactly runs and how often it runs. You can use the normal GUI text display or BJDebugMsg(). You can print static text, the value of variables, unit names/health/handle-ids, etc. to get a better picture since wc3 doesn’t have a step-by-step debugger.
Hi, I did what you said to reference the unit only once. And the spell works percectly fine now. But I do have another question. I don't know if you would be able to answer this.

How do we stop the the trigger effects of a channeling spell in MUI, if the caster finishes 9r stops casting an ability? I have no idea how to turn the trigger off for the one caster who finishes or stops channeling it.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Hi, I did what you said to reference the unit only once. And the spell works percectly fine now. But I do have another question. I don't know if you would be able to answer this.

How do we stop the the trigger effects of a channeling spell in MUI, if the caster finishes 9r stops casting an ability? I have no idea how to turn the trigger off for the one caster who finishes or stops channeling it.
For channeling spells you may want to take advantage of Unit Indexing instead of Dynamic Indexing.

Dynamic Indexing, which you used for your DoT spell, is designed to allow for an infinite number of active instances of the spell. It doesn't care who those instances belong to. In other words, it allows you to create "stacking" effects.

Note: There's a chance that you may have not wanted to use this for your spell since it lets you stack multiple DoTs on the same target. Warcraft 3 normally relies on Buffs for this type of effect with a simple rule: Only 1 buff of a given kind can exist on a unit at a time, and it will get refreshed with new data when reapplied. Think about how casting Bloodlust on a unit simply refreshes the duration (buff) instead of stacking the effects and doubling the speed.

Unit Indexing (when used this way) is similar to Dynamic Indexing but only allows 1 active instance per caster - with some exceptions. This would work great for a channeling ability since a caster is stuck in the channeling process and cannot create a new instance until after it stops casting it's previous one. It also works well for anything that relies on the basic Buff mechanics that you're used to seeing in most abilities.

Here's an example:
  • Channel Start
    • Events
      • Unit - A unit Begins channeling an ability
    • Conditions
    • Actions
      • Set Channel_Caster = (Triggering unit)
      • Set Channel_CV = (Custom value of Channel_Caster)
      • Set Channel_Ticks[Channel_CV] = 0
      • Unit Group - Add Channel_Caster to Channel_Caster_Group
      • Trigger - Turn on Channel Loop
  • Channel Stop
    • Events
      • Unit - A unit Stops casting an ability
    • Conditions
    • Actions
      • Set Channel_Caster = (Triggering unit)
      • Unit Group - Remove Channel_Caster from Channel_Caster_Group
      • If (Channel_Caster_Group is empty) then Trigger - Turn off Channel Loop Else do nothing
  • Channel Loop
    • Events
      • Time - Every 0.02 seconds of game-time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Channel_Caster_Group and do (Actions)
        • Loop - Actions
          • -------- Do stuff --------
          • Set Channel_Caster = (Picked unit)
          • Set Channel_CV = (Custom value of Channel_Caster)
          • Set Channel_Ticks[Channel_CV] = Channel_Ticks[Channel_CV] + 1
^ Channel_Ticks will be tracked for each caster similar to how Dynamic Indexing has unique data for each Index in the Array (see your FB_Duration variable). You could then check when Channel_Ticks is equal to say 50 to know that 1.00 second of channeling has passed. Add more Variable arrays as needed.

Master these two methods and you'll be able to create fairly advanced stuff. Also, Unit Indexing is really just a tool for attaching data to units. That concept alone is very useful and can be taken advantage of in any of your triggers.
 
Last edited:
For channeling spells you may want to take advantage of Unit Indexing instead of Dynamic Indexing.

Dynamic Indexing, which you used for your DoT spell, is designed to allow for an infinite number of active instances of the spell. It doesn't care who those instances belong to. In other words, it allows you to create "stacking" effects.

Note: There's a chance that you may have not wanted to use this for your spell since it lets you stack multiple DoTs on the same target. Warcraft 3 normally relies on Buffs for this type of effect with a simple rule: Only 1 buff of a given kind can exist on a unit at a time, and it will get refreshed with new data when reapplied. Think about how casting Bloodlust on a unit simply refreshes the duration (buff) instead of stacking the effects and doubling the speed.

Unit Indexing (when used this way) is similar to Dynamic Indexing but only allows 1 active instance per caster - with some exceptions. This would work great for a channeling ability since a caster is stuck in the channeling process and cannot create a new instance until after it stops casting it's previous one. It also works well for anything that relies on the basic Buff mechanics that you're used to seeing in most abilities.

Here's an example:
  • Channel Start
    • Events
      • Unit - A unit Begins channeling an ability
    • Conditions
    • Actions
      • Set Channel_Caster = (Triggering unit)
      • Set Channel_CV = (Custom value of Channel_Caster)
      • Set Channel_Ticks[Channel_CV] = 0
      • Unit Group - Add Channel_Caster to Channel_Caster_Group
      • Trigger - Turn on Channel Loop
  • Channel Stop
    • Events
      • Unit - A unit Stops casting an ability
    • Conditions
    • Actions
      • Set Channel_Caster = (Triggering unit)
      • Unit Group - Remove Channel_Caster from Channel_Caster_Group
      • If (Channel_Caster_Group is empty) then Trigger - Turn off Channel Loop Else do nothing
  • Channel Loop
    • Events
      • Time - Every 0.02 seconds of game-time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Channel_Caster_Group and do (Actions)
        • Loop - Actions
          • -------- Do stuff --------
          • Set Channel_Caster = (Picked unit)
          • Set Channel_CV = (Custom value of Channel_Caster)
          • Set Channel_Ticks[Channel_CV] = Channel_Ticks[Channel_CV] + 1
^ Channel_Ticks will be tracked for each caster similar to how Dynamic Indexing has unique data for each Index in the Array (see your FB_Duration variable). You could then check when Channel_Ticks is equal to say 50 to know that 1.00 second of channeling has passed. Add more Variable arrays as needed.

Master these two methods and you'll be able to create fairly advanced stuff. Also, Unit Indexing is really just a tool for attaching data to units. That concept alone is very useful and can be taken advantage of in any of your triggers.
Thank you for posting an example, I am going to study it well.

And what you said about the DoT Spell. I never thought that this trigger could stack its DoE to one target. I only thought that it will allow the caster to the damage its previous target while targetting other units. Now I am confused what to do since I don't want the damagebto stack, only that the duration will be refreshed if recasted to the same target.

What could be done about it?
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Thank you for posting an example, I am going to study it well.

And what you said about the DoT Spell. I never thought that this trigger could stack its DoE to one target. I only thought that it will allow the caster to the damage its previous target while targetting other units. Now I am confused what to do since I don't want the damagebto stack, only that the duration will be refreshed if recasted to the same target.

What could be done about it?
Like I said before, you have two methods, Dynamic Indexing and Unit Indexing. If Dynamic Indexing is for stacking effects and Unit Indexing is for standard "Buff" effects then the answer is clear.

Use Unit Indexing instead.
  • Frostbite Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Frostbite
    • Actions
      • Set FB_Target = (Target unit of abiiity being cast)
      • Set FB_Index = (Custom value of FB_Target)
      • Set FB_Caster[FB_Index] = (Triggering unit)
      • Set FB_Level = (Level of Frostbite for FB_Caster[FB_Index])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (FB_Target is A Hero) Equal to True
        • Then - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 1) + 2)
        • Else - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 3) + 1)
      • -------- Create Dummy --------
      • Set FB_Point = (Position of FB_Target)
      • Unit - Create 1 Dummy for (Owner of FB_Caster[FB_Index]) at FB_Point
      • Custom script: call RemoveLocation( udg_FB_Point )
      • Unit - Add Frostbite (Dummy Spell) to (Last created unit)
      • Unit - Add a 1.50 second Generic expiration timer to (Last created unit)
      • Unit - Set level of Frostbite (Dummy Spell) for (Last created unit) to (Level of Frostbite for FB_Caster[FB_Index])
      • Unit - Order (Last created unit) to Orc Raider - Ensnare FB_Target
      • -------- Turn On Damage Loop --------
      • Unit Group - Add FB_Target to FB_Target_Group
      • Trigger - Turn on Frostbite Damage Loop <gen>
      • -------- Deal Damage (careful when you do this! it can kill the target and run your "A unit Dies" triggers) --------
      • Unit - Cause FB_Caster[FB_Index] to damage FB_Target, dealing 50.00 damage of attack type Spells and damage type Cold
  • Frostbite Damage Loop
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in FB_Target_Group and do (Actions)
        • Loop - Actions
          • Set FB_Target = (Picked unit)
          • Set FB_Index = (Custom value of FB_Target)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FB_Duration[FB_Index] Greater than 0
              • (FB_Target is Alive) Equal to True
            • Then - Actions
              • -------- Deal Damage --------
              • Unit - Cause FB_Caster[FB_Index] to damage FB_Target, dealing 50.00 damage of attack type Spells and damage type Cold
              • Set FB_Duration[FB_Index] = (FB_Duration[FB_Index] - 1)
            • Else - Actions
              • -------- Remove Target - Try To Turn Off --------
              • Unit Group - Remove FB_Target from FB_Target_Group
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (FB_Target_Group is empty) Equal to True
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
Make sure your Loop trigger is Initially OFF since it should only be on while at least one instance of the spell is active.

Also, you may want to think about how Buffs work. If a unit has it's Ensnare buff dispelled/removed (I don't think this Buff CAN be dispelled) then the effects of the ability should probably end since that's how it works for everything else. You can manage this by checking for the Buff in the Conditions of your Loop trigger similar to how I check if the unit is still Alive or not. Note that not every Buff is applied instantly so there can be a timing issue if you're not careful. For example, your Loop trigger can run before the Ensnare missile has even reached it's target, see that there is no Buff, and end the effects prematurely. In that case you need to take some extra measures by waiting for the Buff to be applied before even Adding the target to FB_Target_Group.
 
Last edited:
Like I said before, you have two methods, Dynamic Indexing and Unit Indexing. If Dynamic Indexing is for stacking effects and Unit Indexing is for standard "Buff" effects then the answer is clear.

Use Unit Indexing instead.
  • Frostbite Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Frostbite
    • Actions
      • Set FB_Target = (Target unit of abiiity being cast)
      • Set FB_Index = (Custom value of FB_Target)
      • Set FB_Caster[FB_Index] = (Triggering unit)
      • Set FB_Level = (Level of Frostbite for FB_Caster[FB_Index])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (FB_Target[FB_Index] is A Hero) Equal to True
        • Then - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 1) + 2)
        • Else - Actions
          • Set FB_Duration[FB_Index] = ((FB_Level x 3) + 1)
      • -------- Create Dummy --------
      • Set FB_Point = (Position of FB_Target)
      • Unit - Create 1 Dummy for (Owner of FB_Caster[FB_Index]) at FB_Point
      • Custom script: call RemoveLocation( udg_FB_Point )
      • Unit - Add Frostbite (Dummy Spell) to (Last created unit)
      • Unit - Add a 1.50 second Generic expiration timer to (Last created unit)
      • Unit - Set level of Frostbite (Dummy Spell) for (Last created unit) to (Level of Frostbite for FB_Caster[FB_Index])
      • Unit - Order (Last created unit) to Orc Raider - Ensnare FB_Target
      • -------- Turn On Damage Loop --------
      • Unit Group - Add FB_Target to FB_Target_Group
      • Trigger - Turn on Frostbite Damage Loop <gen>
      • -------- Deal Damage (careful when you do this! it can kill the target and run your "A unit Dies" triggers) --------
      • Unit - Cause FB_Caster[FB_Index] to damage FB_Target, dealing 50.00 damage of attack type Spells and damage type Cold
  • Frostbite Damage Loop
    • Events
      • Time - Every 0.50 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in FB_Target_Group and do (Actions)
        • Loop - Actions
          • Set FB_Target = (Picked unit)
          • Set FB_Index = (Custom value of FB_Target)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FB_Duration[FB_Index] Greater than 0
              • (FB_Target is Alive) Equal to True
            • Then - Actions
              • -------- Deal Damage --------
              • Unit - Cause FB_Caster[FB_Index] to damage FB_Target, dealing 50.00 damage of attack type Spells and damage type Cold
              • Set FB_Duration[FB_Index] = (FB_Duration[FB_Index] - 1)
            • Else - Actions
              • -------- Remove Target - Try To Turn Off --------
              • Unit Group - Remove FB_Target from FB_Target_Group
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (FB_Target_Group is empty) Equal to True
                • Then - Actions
                  • Trigger - Turn off (This trigger)
                • Else - Actions
Make sure your Loop trigger is Initially OFF since it should only be on while at least one instance of the spell is active.

Also, you may want to think about how Buffs work. If a unit has it's Ensnare buff dispelled/removed (I don't think this Buff CAN be dispelled) then the effects of the ability should probably end since that's how the game works for everything else. You can manage this by checking for the Buff in the Conditions of your Loop trigger similar to how I check if the unit is still Alive or not. Note that not every Buff is applied instantly so there can be a timing issue if you're not careful. For example, your Loop trigger can run before the Ensnare missile has even reached it's target, see that there is no Buff, and end the effects prematurely. In that case you need to take some extra measures by waiting for the Buff to be applied before even Adding the target to FB_Target_Group.
Thank you very much for your help. I can do so much with this example. It gives me clear idea of Dynamic and Unit Indexing. I hope with these examples I can manage to experiment a lot. This is already so much help for me.
 
Last edited:
Top