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

[vJass] SleepAction - The new "Wait"

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
When I was making cinematic scripts last year, I was disappointed by the inaccuracies of TriggerSleepAction(), so I moved to fix that problem with Waldbaer's Exact-Timing Cinematic tutorial. Unfortunately, that looked like a mess and was getting tiresome to create the trigger queues for every wait period that I wanted to be exact.

I moved to JASS.

What happened then was the precedence-behavior of programming messed everything up in a whole new way. The TimerStart() call only compiles functions above it, which made my cinematic script look out-of-order and I was finding it hard to expand on it and to locate a specific section (because everything was in reverse-chronological order).

I tried learning Anitarf's Cinematic System but I found myself drifting further and further away from the approach I wanted to take with creating the cinematic. I wanted to have a script that looked like it was mine and would be easy for someone to pick up on.

The SleepAction library is the realization of those dreams I had. Now, thanks to a textmacro, it is an easy thing to implement a wait period. The only parts that require thinking are in the parameters passed to the macro.

Example 1:

JASS:
scope foo initializer bar // requires SleepAction
  
    private function bar takes nothing returns nothing
        //! runtextmacro SleepAction("5.5", "1")
        call BJDebugMsg("Hello,") // Executed exactly 5 and a half seconds into the game.
        //! runtextmacro SleepAction("0.1", "A")
        call BJDebugMsg("World!") // Executed exactly 1/10th of a second after the last message.
    endfunction
  
endscope

The first of the parameters is the period you want to wait, which must be a real value or variable. Everything below the line of the textmacro will not execute until the *exact moment* the wait period has ended. Not slightly before or slightly after which you get from TriggerSleepAction() and without disorganizing and/or complicating your script to accomplish something which should not have ever been a complicated process.

The textmacro must be within a scope or library (pretty much every vJass user does this anyway) and within a vJass function (not a method or a Zinc function [Zinc users have anonymous functions to make up for all the features Zinc didn't get]). Note that local variables are not remembered after the textmacro line, though it is possible to declare new local variables after the line. How it works:

Invisible to you while scripting, the textmacro breaks the function in two. A timer gets started with the duration you specified and then, using a vJass function interface combined with TimerData from Vexorian's TimerUtils, the "second part" of the function is executed when the timer expires. For this reason, the second parameter must be a scope-unique sequence of letters and/or numbers and/or underscores as that parameter more or less gives the hidden function a name.

I find it user-friendly to just name each part a sequencial number (1,2,3...) as you scroll down. The hidden function is a private function which is prefixed with SleepAction_ and, being private, prefixed with the scope's name as well, with the second parameter's name tacked on at the end of it all.

Hopefully, this script allows you to create cinematics in an environment which is suitable for you!

Example 2:

JASS:
    scope Scene1 initalizer Init // requires SleepAction
        
        private function Message takes real duration, string msg returns nothing
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.0, 0.0, duration, "|cff888888" + msg + "|r")
        endfunction
        
        private function Init takes nothing returns nothing
        
            //! runtextmacro SleepAction("0.0", "1")
            // Actions below this line happen exactly 0 seconds into the game.
            call Message(5.0, "This is an easy method to create cinematics.")
            
            //! runtextmacro SleepAction("5.0", "2")
            // Actions below this line happen exactly 5 seconds after the last message.
            call Message(7.0, "Using this tool, you can create perfect wait-periods without " + /*
                            */"distorting the look and feel of your cinematic scripts.")
                        
            //! runtextmacro SleepAction("7.0", "3")
            // Actions below this line happen exactly 7 seconds after the last message.
            call Message(10.0, "All you have to do is fill in the blanks:\n" + /*
                             */"//! runtextmacro SleepAction(\"<Real Duration>\", \"<Scope-Unique Name>\")")
            
            call TriggerSleepAction(10.0)
            // Yes, you can safely mix regular TriggerSleepActions in, if you want.
        endfunction
        
    endscope

Cons


I can just butter this whole thing up, but there are some real things to consider and to review from what I mentioned already.

A) The textmacro may only be enclosed in a function which takes nothing returns nothing.
B) The function may not be a method and may not be in the Zinc language.
C) The function must be contained in a scope or a library.
D) Local variables are forgotten after the textmacro line.
E) The textmacro line may not be embedded within a block such as if/then/else/endif or loop/endloop.
F) You must input a scope-unique name as a second textmacro parameter.

SleepAction script:

JASS:
library SleepAction requires TimerUtils
    
    function interface SleepActionFunc takes nothing returns nothing
    
    private function Sleep takes nothing returns nothing
        local timer t = GetExpiredTimer()
        call SleepActionFunc(GetTimerData(t)).execute()
        call ReleaseTimer(t)
    endfunction
    
    function DoSleepAction takes SleepActionFunc func, real duration returns nothing
        local timer t = NewTimer()
        call SetTimerData(t, integer(func))
        call TimerStart(t, duration, false, function Sleep)
    endfunction
    
    //! textmacro SleepAction takes RealSleepDuration, FuncName
            globals
                private keyword SleepAction_$FuncName$X
            endglobals
            call DoSleepAction(SleepAction_$FuncName$X, $RealSleepDuration$)
        endfunction
        
        private function SleepAction_$FuncName$X takes nothing returns nothing
    //! endtextmacro

endlibrary
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Do you have any benchmark to prove that globals are faster than locals? I vaguely remember doing a test a few years ago that proved the inverse of that.

Actually it depends.. if you have very few globals, the globals will be faster than the locals. If you have very many globals, the globals will be much slower than the locals. Essentially, the more globals you have, the slower reading a global value gets.

This means that you want to minimize global variables, not maximize them.


These results were from many benchmarks conducted by myself and other members at thehelper.net. Some of the benchmarks were under the direction of jesus4lyf.
 
Level 8
Joined
Oct 3, 2008
Messages
367
A method of making this decent to use has not yet been developed. It would take a lot of work to get anything good out of the concept. This just happens to hit some of Jasshelper's limitations like a bird does a window. Graveyarded.
 
Top