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

[System] [???] ElapsedGameTime

Don't be fooled by the title, this is more than just a simple Elapsed Game Time manager.
This system also allows you to register elapsed game-time events.
With the current configuration, the limit is 3.41 years in the future. I hope this isn't inconvenient.
I apologize for this limitation and I also hope it won't make this resource seem useless to you.
You can also get a formatted elapsed game-time string.

JASS:
/************************************
*
*   ElapsedGameTime
*   v2.0.0.0
*   By Magtheridon96
*   
*   - Fires a code given an elapsed game time.
*   - Retrieves:
*       - A Formatted Game-time String
*       - The Total Elapsed Game-time in Seconds
*       - The Game-time Seconds (0 <= x <= 59)
*       - The Game-time Minutes (0 <= x <= 59)
*       - The Game-time Hours   (0 <= x)
*
*   Optional Requirements:
*   ----------------------
*
*       - Table by Bribe
*           - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
*   API:
*   ----
*
*       - struct ElapsedGameTime extends array
*
*           - static method registerEvent takes real time, code c returns nothing
*               - Registers a code that will execute at the given time.
*
*           - static method start takes nothing returns nothing
*           - static method pause takes nothing returns nothing
*           - static method resume takes nothing returns nothing
*               - These are used to start/pause/resume the game timer.
*
*           - static method operator paused takes nothing returns boolean
*           - static method operator running takes nothing returns boolean
*               - These determine whether the system is running or not.
*
*           - static method getTime takes nothing returns real
*               - Gets the total elapsed game time in seconds.
*
*           - static method getSeconds takes nothing returns integer
*           - static method getMinutes takes nothing returns integer
*           - static method getHours takes nothing returns integer
*               - These static methods get the clock values.
*
*           - static method getTimeSeconds takes nothing returns integer
*               - Gets the elapsed game time string (Formatted)
*
*       - function RegisterElapsedGameTimeEvent takes real time, code c returns nothing
*           - Registers a code that will execute at the given time.
*
*       - function StartGameTimer takes nothing returns nothing
*       - function PauseGameTimer takes nothing returns nothing
*       - function ResumeGameTimer takes nothing returns nothing
*           - These are used to start/pause/resume the game timer.
*
*       - function IsGameTimerPaused takes nothing returns boolean
*       - function IsGameTimerRunning takes nothing returns boolean
*           - These determine whether the system is running or not.
*
*       - function GetElapsedGameTime takes nothing returns real
*           - Gets the total elapsed game time in seconds.
*
*       - function GetGameTimeSeconds takes nothing returns integer
*       - function GetGameTimeMinutes takes nothing returns integer
*       - function GetGameTimeHours takes nothing returns integer
*           - Gets the elapsed game time hours
*
*       - function GetGameTimeString takes nothing returns string
*           - Gets the elapsed game time string (Formatted)
*
************************************/
library ElapsedGameTime requires optional Table

    globals
        // If this is set to true, you need to call ElapsedGameTime.start() manually.
        private constant boolean CUSTOM_START_TIME = false
        // This timer interval. If accuracy means nothing to you, increase it.
        private constant real INTERVAL = 0.03125
    endglobals
    
    private module Init
        private static method onInit takes nothing returns nothing
            static if LIBRARY_Table then
                set cache = TableArray[0x200]
                set hash = Table.create()
                set data = Table.create()
            endif
            static if not CUSTOM_START_TIME then
                call start()
            endif
        endmethod
    endmodule
    
    struct ElapsedGameTime extends array
        private static constant real FPSR = 1 / INTERVAL
        private static constant integer FPSI = R2I(FPSR)
        
        private static integer seconds = 0
        private static integer minutes = 0
        private static integer hours = 0
        
        private static integer current = -1
        private static trigger t = CreateTrigger()
        private static timer gameTimer = CreateTimer()
        
        private static boolean runningX = false
        private static boolean done0 = false
        
        // This ugliness is required.
        static if LIBRARY_Table then
            private static TableArray cache
            private static Table hash
            private static Table data
            private static method loadData takes integer i returns boolean
                return data.boolean[i]
            endmethod
            private static method saveData takes integer i, boolean b returns nothing
                set data.boolean[i] = b
            endmethod
            private static method loadHash takes integer i returns integer
                return hash[i]
            endmethod
            private static method saveHash takes integer i, integer d returns nothing
                set hash[i] = d
            endmethod
            private static method loadCache takes integer i1, integer i2 returns boolexpr
                return cache[i1].boolexpr[i2]
            endmethod
            private static method saveCache takes integer i1, integer i2, boolexpr b returns nothing
                set cache[i1].boolexpr[i2] = b
            endmethod
            private static method haveData takes integer i1 returns boolean
                return data.boolean.has(i1)
            endmethod
        else
            private static hashtable cache = InitHashtable()
            private static constant integer HASH = -1
            private static constant integer DATA = -2
            private static method loadData takes integer i returns boolean
                return LoadBoolean(cache, DATA, i)
            endmethod
            private static method saveData takes integer i, boolean b returns nothing
                call SaveBoolean(cache, DATA, i, b)
            endmethod
            private static method loadHash takes integer i returns integer
                return LoadInteger(cache, HASH, i)
            endmethod
            private static method saveHash takes integer i, integer d returns nothing
                call SaveInteger(cache, HASH, i, d)
            endmethod
            private static method loadCache takes integer i1, integer i2 returns boolexpr
                return LoadBooleanExprHandle(cache, i1, i2)
            endmethod
            private static method saveCache takes integer i1, integer i2, boolexpr b returns nothing
                call SaveBooleanExprHandle(cache, i1, i2, b)
            endmethod
            private static method haveData takes integer i1 returns boolean
                return HaveSavedBoolean(cache, DATA, i1)
            endmethod
        endif
        
        private static method run takes nothing returns nothing
            local integer count
            
            if loadData(current) then
                // Increase current index
                set current = current + 1
                
                // Load number of codes registered
                set count = loadHash(current)
                
                // Clear all the conditions from the trigger.
                call TriggerClearConditions(t)
                
                loop
                    // Enqueue boolexprs
                    call TriggerAddCondition(t, loadCache(count, current))
                    exitwhen count == 0
                    set count = count - 1
                endloop
                
                // Fire the boolexprs
                call TriggerEvaluate(t)
            endif
            
            if done0 then
                // Game-time Data manager (For the user)
                if current == current / FPSI * FPSI then
                    set seconds = seconds + 1
                    if seconds == 60 then
                        set seconds = 0
                        set minutes = minutes + 1
                        if minutes == 60 then
                            set minutes = 0
                            set hours = hours + 1
                        endif
                    endif
                endif
            else
                set done0 = true
                call TimerStart(gameTimer, INTERVAL, true, function thistype.run)
            endif
        endmethod
        
        static method operator paused takes nothing returns boolean
            return not runningX
        endmethod
        
        static method operator running takes nothing returns boolean
            return runningX
        endmethod
        
        static method start takes nothing returns nothing
            if done0 then
                call TimerStart(gameTimer, INTERVAL, true, function thistype.run)
            else
                call TimerStart(gameTimer, 0, false, function thistype.run)
            endif
            set runningX = true
        endmethod
        
        static method pause takes nothing returns nothing
            call PauseTimer(gameTimer)
            set runningX = false
        endmethod
        
        static method resume takes nothing returns nothing
            call start()
        endmethod
        
        static method registerEvent takes real time, code c returns nothing
            local integer index = R2I(time * FPSI)
            local integer n
            
            // Check if the data for this time doesn't exist
            if not haveData(index) then
                // If it doesn't exist, we hash the first
                // boolexpr and set the count to 0
                call saveHash(index, 0)
                call saveData(index, true)
                call saveCache(0, index, Filter(c))
            else
                // If it exists, we hash the boolexpr and increase the count
                set n = loadHash(index) + 1
                call saveHash(index, n)
                call saveCache(n, index, Filter(c))
            endif
        endmethod
        
        static method getTime takes nothing returns real
            // You mad TimerGetElapsed?
            return current / FPSR
        endmethod
        
        static method getSeconds takes nothing returns integer
            return seconds
        endmethod
        
        static method getMinutes takes nothing returns integer
            return minutes
        endmethod
        
        static method getHours takes nothing returns integer
            return hours
        endmethod
        
        static method getTimeString takes nothing returns string
            local string s = I2S(seconds)
            if seconds < 10 then
                set s = "0" + s
            endif
            set s = I2S(minutes) + ":" + s
            if minutes < 10 then
                set s = "0" + s
            endif
            set s = I2S(hours) + ":" + s
            if hours < 10 then
                set s = "0" + s
            endif
            return s
        endmethod
        
        implement Init
    endstruct
    
    function RegisterElapsedGameTimeEvent takes real t, code c returns nothing
        call ElapsedGameTime.registerEvent(t, c)
    endfunction
    
    function StartGameTimer takes nothing returns nothing
        call ElapsedGameTime.start()
    endfunction
    
    function PauseGameTimer takes nothing returns nothing
        call ElapsedGameTime.pause()
    endfunction
    
    function ResumeGameTimer takes nothing returns nothing
        call ElapsedGameTime.resume()
    endfunction
    
    function IsGameTimerPaused takes nothing returns boolean
        return ElapsedGameTime.paused
    endfunction
    
    function IsGameTimerRunning takes nothing returns boolean
        return ElapsedGameTime.running
    endfunction
    
    function GetElapsedGameTime takes nothing returns real
        return ElapsedGameTime.getTime()
    endfunction
    
    function GetGameTimeHours takes nothing returns integer
        return ElapsedGameTime.getHours()
    endfunction
    
    function GetGameTimeMinutes takes nothing returns integer
        return ElapsedGameTime.getMinutes()
    endfunction
    
    function GetGameTimeSeconds takes nothing returns integer
        return ElapsedGameTime.getSeconds()
    endfunction
    
    function GetGameTimeString takes nothing returns string
        return ElapsedGameTime.getTimeString()
    endfunction

endlibrary

You're probably asking why using this is a plus. These are the reasons:
- One timer to handle all game-time events.
- No need for another Game-time library to get the game-time.
- Insanely fast Game-time 'Get' function.

Feel free to comment..
Constructive criticism will be accepted and is highly appreciated.
 
Last edited:
getTimeString forgets to concatenate the string "s" when you add on minutes/hours.

Destroying the boolexpr is not a good idea. What if the user is using the boolexpr handle ID for something else? You mess up their stuff.

Btw the last post was written on an iPod Touch which is why I didn't do numbers and math symbols, because it is too annoying to switch back and forth, I just wrote it out ;)
 
getTimeString forgets to concatenate the string "s" when you add on minutes/hours.

Oh my D:

Fixed. :D

Destroying the boolexpr is not a good idea. What if the user is using the boolexpr handle ID for something else? You mess up their stuff.

Oh my D:

Actually, I just realized that destroying those boolexprs is totally safe :p
The user is providing code variables and I'm creating the boolexprs through Filter
^_^

Btw the last post was written on an iPod Touch which is why I didn't do numbers and math symbols, because it is too annoying to switch back and forth, I just wrote it out ;)

I know what you mean :p
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Actually, I just realized that destroying those boolexprs is totally safe :p
The user is providing code variables and I'm creating the boolexprs through Filter
^_^

It isn't, nothing forbids the user to use the same function in an other boolexpr inside his own scripts.
Unless i'm really missing something actually :

code c = function MyF
Filter(c) == Filter(function MyF)
 
Filter(c) will create a new boolexpr. I may be wrong though.
I sure hope boolexprs aren't like players :x

edit

I guess it's time for a test.

edit

Well... that went well :x
It turns out, this lags the map like hell and goes into an infinite loop.
I fixed it though.
Also, you were right. Filter(c) works just like Player(0) (Returns the same handle every time it's used)

edit

Ok, I fixed all the bugs and optimized this baby, then I tested how accurate it is.
Every 10 seconds, accuracy will decrease by 16 milliseconds. This isn't really my fault, it's Jass's fault (Reals are totally
gay
like that)
That's a 0.1 second difference per minute.
Meaning a 6 second difference per hour ZOMG.
 
Last edited:
Actually, 0.03125 == 0.031284 in warcraft III :/ (3 decimal digits recognized)

Maybe I should change it so that it iterates 50x a second instead of 32x :eek:

edit

Updated.
Accuracy improved.
There will now be a constant 0-20ms difference.
The last version offered a 0-33ms difference as a base, and the accuracy got f*ed over time.

50 iterations per second isn't so bad. Right? :>
I could change it to 40 if you want.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
That's one "limitation" of the snippet, current integer variable will becomes negative ( > (2 ^ 32) - 1) and then break the code, if you run a map using this for more than 3.41 year without any pause (overflow).

It's just a joke, since you will hardly do this.
 
Level 10
Joined
May 27, 2009
Messages
494
hmm can you do something like a timer just like in DotA
wherein if the creeps aren't spawning yet, the timer decrements instead of increment.

then when it reaches 0, fire codes or anything like that then increment timer?
Well i have tried that unfortunately, if a global variable that is periodically changed reaches 0 in a timer that runs periodically, the timer will be destroyed? Blizzard bug? i think but nevermind
 
Updated to v2.0.0.0:

  • FireCode is no longer a requirement.
  • Table is now optional.
  • Code cleaned up a bit.
  • Fixed bug that caused zero-game-time events to execute on the first interval.
  • You can now call ElapsedGameTime.start() to start the timer. If you don't want
    to do this and take advantage of the ability to start game time events at any point
    in the game, just keep CUSTOM_START_TIME set to false.
  • 5 new functions:
    • function StartGameTimer or static method start
    • function PauseGameTimer or static method pause
    • function ResumeGameTimer or static method resume
    • function IsGameTimerPaused or static method operator paused
    • function IsGameTimerRunning or static method operator running
 
Top