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

[GUI-friendly] Timer System

[GUI-Friendly] Timer System v1.0.0

This system facilitates the execution of triggers after a certain delay for GUI users, using JASS timers. The timer data is stored using Hashtables. The timers created are independent from each other, thus they can be used to create fully MUI spells and systems with the right techniques. This system can also be used to create simple "waits" that use in-game time instead of real time.

Here are a few other systems for executing delayed actions with GUI: Delayed Action 1.5a and [GUI] Effect Over Time System 2.3.1 . What sets GTS apart from these systems is that it is a more modular implementation, basically giving GUI users access to JASS timers in a very user-friendly manner. Thus, it offers less features, while allowing the user to tweak the usage to what they need. GTS also uses less variables (only 7). This system also uses actual JASS timers, so you can create timers that are more precise than 0.03 seconds (which is 30 frames per second used by most periodic systems). You can also create 0-second timers, which have their own uses.

The system you will want to use will depend on what exactly you want to achieve. EoT will make it easier for effects between units and Delayed Action already has support for special effects, source and target units (and uses arrays instead of Hashtables). You can always use multiple systems for different applications, too. For the best satisfaction and best results in your map, you should research to see which system will fit your use cases better.

Timer:

A timer in JASS is a variable type that counts down from a certain number in game-time (so when the game is paused, the timer is paused) . Normally, it executes JASS functions when it expires, however, GUI does not have functions, so timers in this system execute triggers instead.

All timers created with this system are, in fact, periodic timers, and you must specify in the executed trigger that you want the timer to be destroyed/stopped. To identify which timer has expired, the system allows you to define custom values for each timer. In addition, each timer automatically counts the amount of times it has executed.

You can use a timer's handle ID as a child hashtable key in GTS_Hashtable, and you can store any data you want in the adresses, except the hard-coded ones below.
Hashtable adresses:
-3: Trigger to execute
-2: Executions counter
-1: Custom value

How to Import:
  1. Open World Editor. File -> Preferences -> Tick box for "Automatically create unknown variables (...)"
  2. Copy the trigger category "GUI Timer System" into your map
  3. After you have copied the triggers, delete the "GTS Import" trigger & the trigger comment.
  4. Done!
Using the System:

Start a New Timer

Destroy Timer

Pause Timer

Resume Timer

Timer Trigger

JASS API


This is the main function of the system. Use it to create a timer that will execute the GTS_Trigger trigger after GTS_TimeOut seconds. There are two methods: the first one will set the Custom Value of the timer to its Handle ID. The second method will have the user specifying the Custom Value themselves. The second method should be used when you want to user arrays, as all timer Handle IDs are bigger than the maximum size of arrays.

Please notice the difference between checking conditions and ignoring conditions.

Handle ID custom value:
  • Actions
    • Set GTS_TimeOut = RealNumber
    • Set GTS_Trigger = YourTrigger
    • Trigger - Run GTS Main <gen> (checking conditions)

User-defined custom value:
  • Actions
    • Set GTS_TimeOut = NonNegativeRealNumber
    • Set GTS_Trigger = YourTrigger
    • Set GTS_CustomValue = IntegerNumber
    • Trigger - Run GTS Main <gen> (ignoring conditions)
After you create a timer, it will be stored in the GTS_Timer variable. In addition, it's handle ID will be stored in the GTS_CustomValue hastable if you use the first method.

This is a complementary function that allows you to prematurely stop a timer. Please not that you should ignore conditions.

  • Actions
    • Set GTS_TimeOut = NegativeRealNumber
    • Set GTS_Timer = YourTimer
    • Trigger - Run GTS Main <gen> (ignoring conditions)

Pausing a timer is simple and can be done with a single line of GUI code.

  • Actions
    • Countdown Timer - Pause YourTimer

Unfortunately, GUI does not have a way to use the JASS native ResumeTimer(). However, with this function you can resume a timer or change its current duration. While the specified number must be negative, it will automatically be converted to a positive duration. Please note that you must check for conditions when executing the "GTS Main" trigger.

  • Actions
    • Set GTS_TimeOut = NegativeRealNumber
    • Set GTS_Timer = YourTimer
    • Trigger - Run GTS Main <gen> (checking conditions)

When a timer expires, the appropriate values are set for GTS_CustomValue and GTS_ExecutionCounter. You can use those however you wish in your trigger. This is a guide on how you can use them in your triggers.

Suppose we want to create a special effect and destroy it after 1 second, repeating 3 times. This is one way we could do it, using Hashtables.

  • Actions
    • -------- Start the timer, which will automatically set the GTS_CustomValue variable --------
    • Set GTS_TimeOut = 5.00
    • Set GTS_Trigger = Destroy Effect <gen>
    • Trigger - Run GTS Main <gen> (checking conditions)
    • -------- ---------------------- --------
    • -------- Create Special Effect and Save it in Hashtable --------
    • Special Effect - Create a special effect at (Center of (Playable map area)) using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
    • Hashtable - Save Handle Of(Last created special effect) as 0 of GTS_CustomValue in GTS_Hashtable
    • -------- ---------------------- --------
    • -------- Clear Leaks --------
    • Custom script: call RemoveLocation(null)
For this trigger, we create a Timer that has its own Handle ID as its Custom Data, so we can easily save things in the GTS_Hashtable by using GTS_CustomData as the Child Hashtable. As you can see, we saved the special effect in address 0, but we could use any other address, except (-3, -2, -1), as those are reserved by the system.

  • Actions
    • -------- Use "if" statement so we can do different things depending on how many times the timer has executed --------
    • -------- ---------------------- --------
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • GTS_ExecutionCounter Equal to 3
      • Then - Actions
        • Special Effect - Destroy (Load 0 of GTS_CustomValue in GTS_Hashtable)
        • -------- ---------------------- --------
        • -------- We are done with the timer, as it has already executed 3 times. --------
        • Set GTS_DestroyTimer = True
      • Else - Actions
        • -------- Destroy last special effect, create new one and save it to Hashtable --------
        • Special Effect - Destroy (Load 0 of GTS_CustomValue in GTS_Hashtable)
        • Special Effect - Create a special effect at (Center of (Playable map area)) using Abilities\Spells\Human\ThunderClap\ThunderClapCaster.mdl
        • Hashtable - Save Handle Of(Last created special effect) as 0 of GTS_CustomValue in GTS_Hashtable
        • -------- ---------------------- --------
        • -------- We still want to execute the timer, so we set GTS_DestroyTimer to false --------
        • Set GTS_DestroyTimer = False
For the destruction trigger, we want to do different things depending on how many times the timer has executed. When the timer expires, the values for GTS_CustomValue and GTS_ExecutionCounter are automatically set to the Handle ID of the timer and to how many times it has executed, so we can use them in our trigger.

As all timers created by the system are periodic, the user must specify, at the end of a timer trigger, if the timer should be destroyed or kept. This can easily be done by setting the GTS_DestroyTimer boolean variable to True/False.

  • Actions
    • (...DO STUFF...)
    • Set GTS_DestroyTimer = True

The system is built so it can also be used by calling JASS functions.To use the JASS API, simply move the code between the following tags from the "GTS Main" trigger to your map header after importing the system. GUI users that want to use Custom Scripts can also follow this guide.

JASS:
////////////////////////////////////////////////////////
//Guhun's Timer System version 1.0
////////////////////////////////////////////////////////
CODE TO COPY TO MAP HEADER (I recommend including the tags above and below)
////////////////////////////////////////////////////////
//End of Guhun's Timer System
////////////////////////////////////////////////////////

Provided functions:
JASS:
//Starts the timer "t" using this system's function and stores relevant data in hashtable
//If useHandleId is true, then the timer's custom data will be equal to its handle ID
//If it is false, the custom data will be the specified "userData" parameter
function GTS_StartTimer takes timer t, real timeout, trigger whichTrigger, boolean useHandleId, integer userData returns nothing

//Destroys timer and clears hashtable data related to this system
function GTS_DestroyTimer takes timer t returns nothing

//Restarts a timer created with this system with the specified timeout
function GTS_RestartTimer takes timer t, real timeout returns nothing

//Creates a new timer and sets udg_GTS_Timer to it, then starts the timer with GTS_StartTimer
function GTS_CreateTimer takes real timeout, trigger whichTrigger, boolean useHandleId, integer userData returns nothing




Test Map:


Command

Action

kill

Kills your selected units after 5 seconds.

save

Saves units that are marked for death.

periodic <number>

Changes the period of the periodic timer to the number specified.

explode

Starts a countdown timer that counts down every second. When it reaches 0, an explosion occurs at the center of the map.

The test map has 3 examples: a periodic timer with a configurable period, a 1-time timer that can be stopped and 1 repeating timer that does something after 4 executions. Check them out to see how this system can be used, especially with Hashtables.

It also includes GMUI, GUI MUI Engine, as it can be very useful to generate unique numbers for timer custom values. You can learn more about the system here.

Updates and Version History:
v1:
1.0.0 > Initial Release
1.0.0a > Test Map now includes GMUI 2.0.0 and author name is now Guhun
1.0.0b > Fixed timer variable missing from import trigger
1.0.0c > Provided JASS API and fixed some comments in code
Upcoming:
Contents

Guhun's Timer System (Map)

Reviews
Tasyen
Compact System enabling dynamic non specialized timer usage in GUI. Uses a smart usage of +/- and checking/ignoring conditions to provide 4 different behaviours with 1 triggerAPI. In the same time this usage makes each small detail of the GTS Main...
Compact System enabling dynamic non specialized timer usage in GUI.
Uses a smart usage of +/- and checking/ignoring conditions to provide 4 different behaviours with 1 triggerAPI. In the same time this usage makes each small detail of the GTS Main calling significant, allowing a high error quote.
On this post you explain the usage well, but in the map one has to get that information from reading the jass code Action/Condition stuff (which requiers jass skills).

You could use the destroy function inside your Timerexpire function.

I like this lightweight timers for GUI.
 
Level 12
Joined
Jun 12, 2010
Messages
413
The reason i did not use the Destroy function isnide the Execute function is performance, since it would not only be calling the extra function, but also redundantly use GetHandleId() again. I know it's not good practice, but JASS really limits good practice =/

One of the downfalls of using one trigger for all functions in GUI really is the fact that it can lead to errors, but I think most people would prefer to have that extra care when writing the code than having the extra triggers. It's a tradeoff made in all of my systems :S

I'll see about adding more documentation in the test map, since mine usually don't have a lot of information.

Thanks for the review and feedback :wthumbsup:
 
Top