Tutorial Static Timer

Sooda

Diversity enchants
Reaction score
318
Static Timer Tutorial
by
Sooda
thanks to:
Vexorian​

This tutorial is for advanced GUI-ers. If interested read still.

1. What is Static Timer?
2. Damage over time ability (dot) example
3. Demo map with dot ability

1. What is Static Timer?
Static Timer is used to handle each triggered ability cast while keeping multi unit instance-ability.
It means casting multiple times same triggered ability will not malfunction. For that variable arrays
are used. Each array index refers to unique ability cast. New casts get added to loop which is updated
when Static Timer expires. Ability actions take place inside loop. From all abilities shortest wait
time is used as Static Timer expiration time. When loop is empty timer is paused until ability activates it again.

2. Damage over time ability (dot) example

As our example we take from famous Defending of the Ancients map Crystal Maiden ability
Frostbite. It is basic ability with a twist which makes good practice.

Frostbite description:
Freezes the target in a case of ice, prohibiting movement and attacking. Deals 70 damage per second.

Level 1 - Lasts 1.5 seconds.
Level 2 - Lasts 2 seconds.
Level 3 - Lasts 2.5 seconds.
Level 4 - Lasts 3 seconds.

With brief overview lets think what does our ability need to store? That would be caster, its target and ability duration. Damage is constant at all levels and doesn't need saving. Almost forgot, it does periodically damage. Elapsed ability duration needs to be tracked also. That went easily, time to create those variable arrays.

'frostbite_caster' unit variable array with default starting value
'frostbite_target' unit variable array with default starting value
'frostbite_dur' real variable array with default starting value
'frostbite_elapsedDur' real variable array with default starting value

It's impossible to know which array is currently free unless we don't keep index (count).
When ability is cast we raise index by one and vice versa, lower by one when ends.

'frostbite_index' integer variable with default starting value
Trigger:
  • Frostbite Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Animate Dead
    • Actions
      • Set frostbite_index = (frostbite_index + 1)
      • Set frostbite_caster[frostbite_index] = (Triggering unit)
      • Set frostbite_target[frostbite_index] = (Target unit of ability being cast)
      • Set frostbite_dur[frostbite_index] = (1.00 + ((Real((Level of Acid Bomb for frostbite_caster[frostbite_index]))) x 0.50))
      • Set frostbite_elapsedDur[frostbite_index] = 0.00

Our caster might die while ability lasts, that's why we order dummy unit to do damage instead. Next
part is for dummy unit and isn't explained in detail. Dummy must survive ability duration. Target animation is paused for frozen effect.
Trigger:
  • Unit - Create 1 Footman for (Owner of frostbite_caster[frostbite_index]) at (Position of frostbite_target[frostbite_index]) facing Default building facing degrees
    • Unit - Add Acid Bomb to (Last created unit)
    • Unit - Set level of Acid Bomb for (Last created unit) to (Level of Acid Bomb for frostbite_caster[frostbite_index])
    • Unit - Order (Last created unit) to Undead Necromancer - Cripple frostbite_target[frostbite_index]
    • Unit - Add a 4.00 second Generic expiration timer to (Last created unit)
    • Set frostbite_caster[frostbite_index] = (Last created unit)
    • Animation - Change frostbite_target[frostbite_index]'s animation speed to 0.00% of its original speed
    • Unit - Pause frostbite_target[frostbite_index]

Now we adjust timer if needed, for that create timer. By default timer is paused. First cast activates it and last cast end pauses timer. Our ability new casts never need lower wait. For practice we add new and current wait comparison. For Static Timer lowest wait is always chosen. Timer waits for first ability which needs its actions executed. Finished first trigger:
'frostbite_timer' timer variable with default starting value
Trigger:
  • Frostbite Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Animate Dead
    • Actions
      • Set frostbite_index = (frostbite_index + 1)
      • Set frostbite_caster[frostbite_index] = (Triggering unit)
      • Set frostbite_target[frostbite_index] = (Target unit of ability being cast)
      • Set frostbite_dur[frostbite_index] = (1.00 + ((Real((Level of Acid Bomb for frostbite_caster[frostbite_index]))) x 0.50))
      • Set frostbite_elapsedDur[frostbite_index] = 0.00
      • Unit - Create 1 Footman for (Owner of frostbite_caster[frostbite_index]) at (Position of frostbite_target[frostbite_index]) facing Default building facing degrees
      • Unit - Add Acid Bomb to (Last created unit)
      • Unit - Set level of Acid Bomb for (Last created unit) to (Level of Acid Bomb for frostbite_caster[frostbite_index])
      • Unit - Order (Last created unit) to Undead Necromancer - Cripple frostbite_target[frostbite_index]
      • Unit - Add a 4.00 second Generic expiration timer to (Last created unit)
      • Set frostbite_caster[frostbite_index] = (Last created unit)
      • Animation - Change frostbite_target[frostbite_index]'s animation speed to 0.00% of its original speed
      • Unit - Pause frostbite_target[frostbite_index]
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • frostbite_index Equal to 1
        • Then - Actions
          • Countdown Timer - Start frostbite_timer as a One-shot timer that will expire in 1.00 seconds
          • Skip remaining actions
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Remaining time for frostbite_timer) Greater than 1.00
        • Then - Actions
          • Countdown Timer - Pause frostbite_timer
          • Countdown Timer - Start frostbite_timer as a One-shot timer that will expire in 1.00 seconds
        • Else - Actions

Second trigger does all the work. For subtracting elapsed time from ability duration variable is used.
Variable loop is used to check every active cast. When ability ends its variables gets overwritten with
newest cast data. Loop iterations is reduced by one to check just moved cast data. Lowest wait is chosen as timer expiration time. When all checked timer gets started again. When loop becomes empty which means no casts to handle timer is paused:
'frostbite_timeout' real variable with default starting value
'frostbite_newTimeout' real variable with default starting value
'frostbite_abilTimeout' real variable with default starting value
'frostbite_loopIndex' integer variable with default starting value
Trigger:
  • Frostbite Timer
    • Events
      • Time - frostbite_timer expires
    • Conditions
    • Actions
      • Set frostbite_timeout = (Elapsed time for frostbite_timer)
      • Set frostbite_newTimeout = 1.00
      • For each (Integer frostbite_loopIndex) from 1 to frostbite_index, do (Actions)
        • Loop - Actions
          • Set frostbite_dur[frostbite_loopIndex] = (frostbite_dur[frostbite_index] - frostbite_timeout)
          • Set frostbite_elapsedDur[frostbite_loopIndex] = (frostbite_elapsedDur[frostbite_loopIndex] + frostbite_timeout)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • frostbite_dur[frostbite_loopIndex] Less than or equal to 0.00
            • Then - Actions
              • Set frostbite_caster[frostbite_loopIndex] = frostbite_caster[frostbite_index]
              • Set frostbite_target[frostbite_loopIndex] = frostbite_target[frostbite_index]
              • Set frostbite_dur[frostbite_loopIndex] = frostbite_dur[frostbite_index]
              • Set frostbite_elapsedDur[frostbite_loopIndex] = frostbite_elapsedDur[frostbite_index]
              • Set frostbite_loopIndex = (frostbite_loopIndex - 1)
              • Set frostbite_index = (frostbite_index - 1)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (frostbite_dur[frostbite_loopIndex] - 1.00) Greater than or equal to 1.00
            • Then - Actions
              • Set frostbite_abilTimeout = 1.00
            • Else - Actions
              • Set frostbite_abilTimeout = frostbite_dur[frostbite_loopIndex]
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • frostbite_abilTimeout Less than frostbite_newTimeout
              • frostbite_abilTimeout Greater than or equal to 0.00
            • Then - Actions
              • Set frostbite_newTimeout = frostbite_abilTimeout
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • frostbite_index Equal to 0
        • Then - Actions
          • -------- Timer gets "paused" not activated again. --------
        • Else - Actions
          • Countdown Timer - Start frostbite_timer as a One-shot timer that will expire in frostbite_newTimeout seconds

Core of Static Timer is done. We only need to add actions what happen after waits. I'm going
to add 'do damage after each second' and 'unfreeze target' parts into loop. Finished second trigger:
Trigger:
  • Frostbite Timer
    • Events
      • Time - frostbite_timer expires
    • Conditions
    • Actions
      • Set frostbite_timeout = (Elapsed time for frostbite_timer)
      • Set frostbite_newTimeout = 1.00
      • For each (Integer frostbite_loopIndex) from 1 to frostbite_index, do (Actions)
        • Loop - Actions
          • Set frostbite_dur[frostbite_loopIndex] = (frostbite_dur[frostbite_index] - frostbite_timeout)
          • Set frostbite_elapsedDur[frostbite_loopIndex] = (frostbite_elapsedDur[frostbite_loopIndex] + frostbite_timeout)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • frostbite_elapsedDur[frostbite_loopIndex] Greater than or equal to 1.00
              • (frostbite_target[frostbite_loopIndex] is dead) Equal to False
            • Then - Actions
              • Set frostbite_elapsedDur[frostbite_loopIndex] = (frostbite_elapsedDur[frostbite_loopIndex] - 1.00)
              • Unit - Cause frostbite_caster[frostbite_index] to damage frostbite_target[frostbite_index], dealing 70.00 damage of attack type Spells and damage type Cold
              • Floating Text - Create floating text that reads +70 at (Position of frostbite_target[frostbite_index]) with Z offset 0.00, using font size 10.00, color (50.00%, 50.00%, 100.00%), and 0.00% transparency
              • Floating Text - Change the lifespan of (Last created floating text) to 3.00 seconds
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
              • Floating Text - Change (Last created floating text): Disable permanence
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • frostbite_dur[frostbite_loopIndex] Less than or equal to 0.00
            • Then - Actions
              • Animation - Change frostbite_target[frostbite_loopIndex]'s animation speed to 100.00% of its original speed
              • Unit - Unpause frostbite_target[frostbite_loopIndex]
              • Set frostbite_caster[frostbite_loopIndex] = frostbite_caster[frostbite_index]
              • Set frostbite_target[frostbite_loopIndex] = frostbite_target[frostbite_index]
              • Set frostbite_dur[frostbite_loopIndex] = frostbite_dur[frostbite_index]
              • Set frostbite_elapsedDur[frostbite_loopIndex] = frostbite_elapsedDur[frostbite_index]
              • Set frostbite_loopIndex = (frostbite_loopIndex - 1)
              • Set frostbite_index = (frostbite_index - 1)
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (frostbite_dur[frostbite_loopIndex] - 1.00) Greater than or equal to 1.00
            • Then - Actions
              • Set frostbite_abilTimeout = 1.00
            • Else - Actions
              • Set frostbite_abilTimeout = frostbite_dur[frostbite_loopIndex]
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • frostbite_abilTimeout Less than frostbite_newTimeout
              • frostbite_abilTimeout Greater than or equal to 0.00
            • Then - Actions
              • Set frostbite_newTimeout = frostbite_abilTimeout
            • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • frostbite_index Equal to 0
        • Then - Actions
          • -------- Timer gets "paused" not activated again. --------
        • Else - Actions
          • Countdown Timer - Start frostbite_timer as a One-shot timer that will expire in frostbite_newTimeout seconds

With same method any triggered ability can be done multi unit instance-ability. I hope it becomes
reference for GUI-ers how to make triggered abilities. I hope better tutorials which explain same
thing easier come with time. Meanwhile bear with it. When I find strength I may extend tutorial
for triggered channeling and auras.

EDIT: Tested map and saw when rapidly cast damage isn't shown on all units, I don't know which
causes this, my bad math in tutorial? It's third time I try to get this tutorial example to work. Going
to release it anyway.
 

Attachments

  • Spell Test Map Template - Frostbite v1.w3x
    53.6 KB · Views: 217
Reaction score
91
> Oh my .
Same reaction here. Could you create the same tutorial but in vJASS? I'm really trying to work out different methods with a static timer to retrieve some data... GUI doesn't work for me since I've abandoned it a long time ago.
 

Kenny

Back for now.
Reaction score
202
I was thinking about making a template for a vJass static timer, seeing as so many new jassers don't like using attachment systems.

However, this is pretty mad. I didnt think it could really be done in GUI. However it looks to be for pretty damn advanced GUI'ers.

Props for the work. + rep.
 

AceHart

Your Friendly Neighborhood Admin
Reaction score
1,495
> With that same method, any triggered ability can be made multi-unit instanceable.

Maybe.

> I hope this becomes a reference for GUI-ers

Unlikely at the current state.

Random example:
> It's impossible to know which array is currently free unless we don't keep index

And that's the part that is actually explained.

Overall, this is a long and rather complicated GUI trigger,
with no explanations on what it is doing, or why you think it actually works.
Impossible to apply this to my own spell.


> For each (Integer frostbite_loopIndex) from 1 to frostbite_index
> Set frostbite_index = (frostbite_index - 1)

You know the end condition on "for each" isn't dynamic, right?
 

Sooda

Diversity enchants
Reaction score
318
First thanks for feedback. Of course I try to simplify this tutorial even more. I got stuck when thinking how to write it down because thinking ahead is important part of it. I see triggered ability tutorials in repository. There are well explained and I try to follow with next version their style.

> You know the end condition on "for each" isn't dynamic, right?

I converted variable loop to custom script and saw everything worked on variables. It seemed dynamic to me, does it mean we are doomed for ever?

EDIT:

I'm subtracting from both. One from iterations and another from loop end condition variable.
Trigger:
  • # Set frostbite_elapsedDur[frostbite_loopIndex] = frostbite_elapsedDur[frostbite_index]
    • # Set frostbite_loopIndex = (frostbite_loopIndex - 1)
    • # Set frostbite_index = (frostbite_index - 1)


EDIT2:

Some tips how to make it more understandable? Do I have to explain what is loop? Why I overwrite variables? Should I explain every action line? Concept is important, how do I make it clear for most readers?
 

SerraAvenger

Cuz I can
Reaction score
234
really good ; )


"Now we adjust timer if needed, for that create timer. By default timer is paused. First cast activates it and last cast end pauses timer. Our ability new casts never need lower wait. For practice we add new and current wait comparison. For Static Timer lowest wait is always chosen. Timer waits for first ability which needs its actions executed. Finished first trigger:"

Needs rework, though. It is a bit ungrammatical, it seems ; )
 

Andrewgosu

The Silent Pandaren Helper
Reaction score
716
Moved to the graveyard until updated.

Hint: it's more than a system with an explanation than a tutorial for the repository.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top