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

Timed Function

Level 7
Joined
Apr 30, 2011
Messages
359
The System + Documentation:
JASS:
/*
    /\                                                                      /\
    ][======================================================================][
    ||  I.      TABLE OF CONTENTS                                           ||
    ][======================================================================][
    \/                                                                      \/
    
    // Talking about the system
    I.,,,,,,Table of Contents
    II.,,,,,Introduction
    III.,,,,Description
        A.,,,,,,Requirements
        B.,,,,,,Pros
        C.,,,,,,Cons
        D.,,,,,,Executables and Constants
        E.,,,,,,How the System Works
    IV.,,,,,Usages (Using TimedFunc)
        A.,,,,,,Simple Usages
        B.,,,,,,Complex Usage
    
    // The system itself
    V.,,,,,,The System
        A.,,,,,,Calibration Section
        B.,,,,,,Module TimedStruct
            a.,,,,,,Variables
            b.,,,,,,Contents
        C.,,,,,,Premade Struct: TimedFunc
            a.,,,,,,The Functions Format
            b.,,,,,,The Struct
    
    VI.,,,,,Conclusion and Final Notes
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     INTRODUCTION                                                ||
    ][======================================================================][
    \/                                                                      \/

//      Timed Object
//      -*- overcold_ice -*-
//      
//          This system enables users to bring their timed struct (either
//      delayed, or looping) in an accurate time. Normally they would use
//      timers for those things, but those will end up confusing or something
//      else. The worst case is using TriggerSleepActions, 100% inaccurate,
//      but in exchange, making the code easier to read and write.
//          The best case is using structs. Mostly, looping structs use only
//      one timer, which end up in a bad, inaccurate codes. Let's see this
//      example:
        
        struct example
            timer             t = CreateTimer()
            
            real              a = 0
            
            example array index [8190]
            integer       total = 0
            
            method onLoop takes nothing returns nothing
                local integer i = 1
                local integer m = .total
                
                loop
                    exitwhen i == m
                    
                    set .index [i].a = .index [i].a * 2
                    
                    set i = i + 1
                endloop
            endmethod
            
            method start takes real inita returns nothing
                local thistype that = thistype.create()
                
                set that.a = inita
                
                set .total = .total + 1
                set .index [.total] = that
                
                if .total == 1 and TimerGetRemaining(.t) == 0 then
                    call TimerStart(.t, 0.5, true, function thistype.onLoop)
                endif
                
                call that.destroy()
            endmethod
        endstruct
        
//          That struct above let us to increase a real every 0.5 sec, the
//      increments are equal to the real itself (in other way, the value
//      doubles for every 0.5 sec). If we look closer, the struct isn't
//      accurate, look at this example:
        
        struct multipled extends example
            method doubled takes real r1, r2 returns nothing
                call .start(r1)
                call TriggerSleepAction(0.25)   // no more complication
                                                // for the sake of example
                call .start(r2)
            endmethod
        endstruct
        
//          The struct runs the effect twice, once after another, but within
//      a 0.25 sec delay, but they will both have their values doubled at the
//      same time (no 'almost', just in an order), rather with a 0.25 sec
//      delay. This is an inaccurate example.
//          Because of those inaccurations, here comes the Timed Object
//      System. This system lets you to do delayed, or even looping functions
//      with so much ease. It even has some sort of index (thanks to TimerUtils),
//      allowing you to index structs with it.
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     DESCRIPTION                                                 ||
    ][======================================================================][
    \/                                                                      \/
    
    ]||[===------
    |A.| //Requirements
    ]||[===------
    
    This System requires the following systems:
        * TimerUtils (by Vexorian)
    
    ]||[===------
    |B.| //Pros
    ]||[===------
    
        // Both TimedStruct and TimedFunc
        * 100% Accurate
        * Useful for both Simple and Complex Usages
    
    ]||[===------
    |C.| //Cons
    ]||[===------
        
        // Both
        * Can use too much timers, with a maximum of 8190
        
        // TimedFunc
        * Can heavily increases your Map Size, because of the use of
          function interface
        * Slow, because the use of .evaluate()
        
    ]||[===------
    |D.| //Executables and Constants
    ]||[===------
        
        // globals
        * private boolean ENABLE_ONRUN     = false
        * private boolean ENABLE_TIMEDFUNC = true
            // the name already self-explanatory, ENABLE_ONRUN lets you enable/disable
            // the use of onRun method (it runs right when the timer started, seldomly
            // used, I think). ENABLE_TIMEDFUNC allows you to enable/disable the Timed
            // Function, since it has many cons
        
        // module TimedStruct
        
        * call YourStruct.Run(real time) (returns nothing)
            // used to start the timer, use onExpire(takes nothing returns boolean)
            // method to specify the action runt on expiration. If it returns false,
            // the timer will stop, otherwise it will restart
        
        * call YourStruct.IndexStruct (takes nothing) (returns nothing)
            // used to assign current running struct (.Struct) into the index, saving it
        
        // needed in YourStruct (else it'll pop ups an error)
        * private static method onExpire (takes nothing) (returns boolean)
            // this method runs when the system's timer expire, it returns false
            // by default
        
        * private static method onRun (takes nothing) (returns nothing)
            // this method runs when the system's timer started (only at the beginning,
            // not during restarts), this method can be disabled (see above)
        
        * readonly static timer      YourStruct.Expired = GetExpiredTimer()
        * readonly static integer    YourStruct.Value   = GetTimerData(timer)
        * readonly static YourStruct YourStruct.Struct  = Running Struct
            // those are the event responses, .Expired = GetExpiredTimer(), self-
            // explanatory. .Value is the unique number of current running struct,
            // .Struct is the current struct running. .Value and .Struct can be gotten
            // right after YourStruct.Run method. All of them can be gotten during
            // onExpire methods
        
        // premade struct TimedFunc
        
        * public function interface TimedFunction takes nothing returns boolean defaults false
            // this is the functions format used by the system
        
        * call TimedFunc.Start(real time, TimedFunction func) (returns nothing)
            // real time          = real timeout in timers
            // TimedFunction func = your function that will be runt
            //                      when the timer expire, you can
            //                      set this to null, to make the
            //                      system do nothing when the timer
            //                      expire
            //  doing this will set TimedFunc.Value to whatever
            //  the timer value in this new one. use that to specify
            //  struct indexes
        
        * TimedStruct constants
            // explained above
    
    ]||[===------
    |E.| //How the System Works
    ]||[===------
    
        // Basically, it works like this:
        * First, it checks all available spaces,
            - if there are an empty space (the timer are not running), then do
                - use that timer for the next actions
            - if there are no empty spaces (whether the timers are all running, or not created at all), then do
                - create a new timer for the next actions
        
        * Second, it starts the timer and set TimedFunc.Value to whatever the current timer's value is
        
        * Finally, when the timer expires, do
            - static method onExpire(),
                - if, it returns true, then restart the timer, otherwise, destroy it
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  IV.     USAGES (USING TIMEDFUNC)                                    ||
    ][======================================================================][
    \/                                                                      \/
    
    ]||[===------
    |A.| //Simple Usages
    ]||[===------
        
        * Example 1:
        
        library Initialization initializer Init requires TimedObject
            function FOWDisable takes nothing returns boolean
                call FogEnable(false)
                
                return false
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(5, FOWDisable)
            endfunction
        endlibrary
        
        // that library disables fog of war 5 seconds after initialization
        
        library FreeGold initializer Init requires TimedObject
            function Looping takes nothing returns boolean
                local integer i = 0
                local integer m = 11
                
                loop
                    exitwhen i == m
                    
                    call SetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD) + 1)
                    
                    set i = i +1
                endloop
                
                return true
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(1, Looping)
            endfunction
        endlibrary
        
        // this one adds one gold to player 1 - player 12 for every 1 second
    
    ]||[===------
    |B.| //Complex Usage
    ]||[===------
        
        * Example:
        
        library MoveUnit requires TimedObject
            
            globals
                private Variables array V [8190]
            endglobals
            
            struct Variables
                unit Moved
                real x
                real y
            endstruct
            
            private function Move takes nothing returns boolean
                local integer i = TimedFunc.Value
                
                call SetUnitX(V [i].Moved, V [i].x)
                call SetUnitY(V [i].Moved, V [i].y)
                
                return false
            endfunction
            
            function MoveUnit takes unit u, real x, real y, real delay returns nothing
                local integer i
                
                call TimedFunc.Start(delay, Move)
                set i = TimedFunc.Value
                
                set V [i].Moved = u
                set V [i].x     = x
                set V [i].y     = y
            endfunction
        endlibrary
        
        // that one move the unit 'instantly' to X, Y after a delay
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  V.      THE SYSTEM                                                  ||
    ][======================================================================][
    \/                                                                      \/
*/

// Do Not Edit
// If you know what you are doing, do it

library TimedObject requires TimerUtils
    
    /*=\
    ]||[===------
    |A.| //Calibration Section
    ]||[===------
    \=*/
    globals
        public constant boolean ENABLE_ONRUN     = false
        public constant boolean ENABLE_TIMEDFUNC = false
    endglobals
    
    /*=\
    ]||[===------
    |B.| //Module TimedStruct
    ]||[===------
    \=*/
    module TimedStruct
        
        /*=///===---
        |a.| Variables
        \=*///===---
        // the timer
        private  static timer            TS_t
        
        // struct indexing system
        private  static thistype  array Index [8190]
        private  static integer         Total = 0
        
        // event responses variables
        readonly static timer         Expired
        readonly static integer         Value
        readonly static thistype       Struct
        
        /*=///===---
        |b.| Contents
        \=*///===---
        private static method TS_onExpire takes nothing returns nothing
            set      .Expired = GetExpiredTimer()
            set        .Value = GetTimerData(.Expired)
            set       .Struct = .Index [.Value]
            
            if not .Struct.onExpire() then
                call ReleaseTimer(.Expired)
                
                call .Index [.Value].destroy()
            else
                set .Index [.Value] = .Struct
            endif
            
            set       .Struct = 0
            set      .Expired = null
            set        .Value = 0
        endmethod
        
        static method Run takes real time returns nothing
            local integer a = 1
            local integer b = .Total + 1
            
            local integer i = 0
            
            loop
                exitwhen a == b
                
                if .Index [a] == 0 then
                    set i = a
                    
                    set a = b
                endif
                
                set a = a + 1
            endloop
            
            if i == 0 then
                set     .Total  = .Total + 1
                set i = .Total
            endif
            
            set             .Index [i]   = .create()
            set             .Index [i].TS_t = NewTimerEx(i)
            call TimerStart(.Index [i].TS_t, time, true, function thistype.TS_onExpire)
            
            set                   .Value = i
            set                  .Struct = .Index [i]
            
            static if ENABLE_ONRUN then
                call .Struct.onRun()
            endif
        endmethod
        
        static method IndexStruct takes nothing returns nothing
            set .Index [.Value] = .Struct
        endmethod
    endmodule
    
    /*=\
    ]||[===------
    |C.| //Premade Struct: TimedFunc
    ]||[===------
    \=*/
    static if ENABLE_TIMEDFUNC then
        
        /*=///===---
        |a.| The Functions Format
        \=*///===---
        public function interface TimedFunction takes nothing returns boolean defaults false
        
        /*=///===---
        |b.| The Struct
        \=*///===---
        struct TimedFunc
            implement TimedStruct
            
            private static integer f
            
            private static method onExpire takes nothing returns boolean
                if TimedFunction(.Struct.f).evaluate() then
                    return true
                endif
                
                return false
            endmethod
            
            static method Start takes real time, TimedFunction func returns nothing
                call .Run(time)
                
                set .Struct.f = func
                
                call .IndexStruct()
            endmethod
        endstruct
    endif
endlibrary

/*
    /\                                                                      /\
    ][======================================================================][
    ||  VI.     CONCLUSION AND FINAL NOTES                                  ||
    ][======================================================================][
    \/                                                                      \/
    
//      Timed Object
//      -*- overcold_ice -*-
//      
//          Please apologize me if there are mistakes, or something else that make
//      you hurt. We have reached the end of the page, I hope you understand my
//      really long documentation. Don't forget to put me in your Credits list
//      if you use this system. If you want to edit this system, feel free to do
//      that. But don't publish it without my permission, okay?
//          You can remove these whole documentation. Enjoy and happy coding ^.^!
*/

v1:
JASS:
/*
    /\                                                                      /\
    ][======================================================================][
    ||  I.      TABLE OF CONTENTS                                           ||
    ][======================================================================][
    \/                                                                      \/
    
    // Talking about the system
    I.,,,,,,Table of Contents
    II.,,,,,General Description
    III.,,,,Specific Description
        A.,,,,,,Requirements
        B.,,,,,,Pros
        C.,,,,,,Cons
        D.,,,,,,Executables and Constants
        E.,,,,,,How the System Works
    IV.,,,,,Usage
        A.,,,,,,Simple Usages
        B.,,,,,,Complex Usages
    
    // The system itself
    V.,,,,,,The System
        A.,,,,,,The Function Format (function interface)
        B.,,,,,,The Struct          (struct TimedFunc)
            a.,,,,,,Variables
            b.,,,,,,UC - Storage    (UC: User-Customizeable)
            c.,,,,,,Contents
    
    VI.,,,,,Conclusion and Final Notes
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     GENERAL DESCRIPTION                                         ||
    ][======================================================================][
    \/                                                                      \/

//      Timed Function
//      -*- overcold_ice -*-
//      
//          This system enables users to bring their timed function (either
//      delayed, or looping) in an accurate time. Normally they would use
//      timers for those things, but those will end up confusing or something
//      else. The worst case is using TriggerSleepActions, 100% inaccurate,
//      but in exchange, making the code easier to read and write.
//          The best case is using structs. Mostly, looping structs use only
//      one timer, which end up in a bad, inaccurate codes. Let's see this
//      example:
        
        struct example
            timer             t = CreateTimer()
            
            real              a = 0
            
            example array index [8190]
            integer       total = 0
            
            method onLoop takes nothing returns nothing
                local integer i = 1
                local integer m = .total
                
                loop
                    exitwhen i == m
                    
                    set .index [i].a = .index [i].a * 2
                    
                    set i = i + 1
                endloop
            endmethod
            
            method start takes real inita returns nothing
                local thistype that = thistype.create()
                
                set that.a = inita
                
                set .total = .total + 1
                set .index [.total] = that
                
                if .total == 1 and TimerGetRemaining(.t) == 0 then
                    call TimerStart(.t, 0.5, true, function thistype.onLoop)
                endif
                
                call that.destroy()
            endmethod
        endstruct
        
//          That struct above let us to increase a real every 0.5 sec, the
//      increments are equal to the real itself (in other way, the value
//      doubles for every 0.5 sec). If we look closer, the struct isn't
//      accurate, look at this example:
        
        struct multipled extends example
            method doubled takes real r1, r2 returns nothing
                call .start(r1)
                call TriggerSleepAction(0.25)   // no more complication
                                                // for the sake of example
                call .start(r2)
            endmethod
        endstruct
        
//          The struct runs the effect twice, once after another, but within
//      a 0.25 sec delay, but they will both have their values doubled at the
//      same time (no 'almost', just in an order), rather with a 0.25 sec
//      delay. This is an inaccurate example.
//          Because of those inaccurations, here comes the Timed Function
//      System. This system lets you to do delayed, or even looping functions
//      with so much ease. It even let you to store things within it, and
//      it has some sort of index, allowing you to index structs with it.
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     SPECIFIC DESCRIPTION                                        ||
    ][======================================================================][
    \/                                                                      \/
    
    ]||[===------
    |A.| //Requirements
    ]||[===------
    
    This System requires the following systems:
        * TimerUtils (by Vexorian)
    
    ]||[===------
    |B.| //Pros
    ]||[===------
    
        * Easy to use
        * Useful for both Simple and Complex Usages
    
    ]||[===------
    |C.| //Cons
    ]||[===------
        
        * Missing some flexibility
        
    ]||[===------
    |D.| //Executables and Constants
    ]||[===------
        
        * function interface TimedFunc_TimedFunction takes nothing returns boolean defaults false
            // this is the function format used by the system
            
        * call TimedFunc.Start(real time, TimedFunction func) (returns nothing)
            // real time          = real timeout in timers
            // TimedFunction func = your function that will be runt
            //                      when the timer expire, you can
            //                      set this to null, to make the
            //                      system do nothing when the timer
            //                      expire
            //      doing this will set TimedFunc.Value to whatever
            //  the timer value in this new one. use that to specify
            //  struct indexes
        
        // Variable Storage system
        * call TimedFunc.S$VariableType$(integer index, $Type$ data) (returns nothing)
            // integer index      = the index you want the data stored to
            // $Type$ data        = the value of a $Type$
            //      replace the $VariableType$ with a variable type, but with
            //  its first letter Capitalized, $Type$ is a variable type, not with
            //  any changes. Example:
            //  
            //  TimedFunc.SBoolean(integer index, boolean data) (returns nothing)
            //      it would be self explanatory
            
        * call TimedFunc.L$VariableType$(integer index) (returns $Type$)
            // integer index      = the index you want the data loaded from
            //      a reversed version of above method, this one load it
        
        * TimedFunc.Expired
            //      this constant is equal to GetExpiredTimer()
        
        * TimedFunc.Value
            //      this constant is the unique number of the timer/system
            //  that currently runs. use this variable to index your structs
    
    ]||[===------
    |E.| //How the System Works
    ]||[===------
        
        // Basically, it works like this:
        * First, it checks all available spaces,
            - if there are an empty space (the timer are not running), then do
                - use that timer for the next actions
            - if there are no empty spaces (whether the timers are all running, or not created at all), then do
                - create a new timer for the next actions
        
        * Second, it starts the timer and set TimedFunc.Value to whatever the current timer's value is
        
        * Finally, when the timer expires, do
            - the function specified by the user,
                - if, it returns true, then restart the timer, otherwise, destroy it
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  IV.     USAGE                                                       ||
    ][======================================================================][
    \/                                                                      \/

    ]||[===------
    |A.| //Simple Usages
    ]||[===------
        
        * Example 1:
        
        library Initialization initializer Init requires TimedFunc
            function FOWDisable takes nothing returns boolean
                call FogEnable(false)
                
                return false
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(5, FOWDisable)
            endfunction
        endlibrary
        
        // that library disables fog of war 5 seconds after initialization
        
        library FreeGold initializer Init requires TimedFunc
            function Looping takes nothing returns boolean
                local integer i = 0
                local integer m = 11
                
                loop
                    exitwhen i == m
                    
                    call SetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD) + 1)
                    
                    set i = i +1
                endloop
                
                return true
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(1, Looping)
            endfunction
        endlibrary
        
        // this one adds one gold to player 1 - player 12 for every 1 second
    
    ]||[===------
    |B.| //Complex Usages
    ]||[===------
        
        * Example 1:
        
        library MoveUnit requires TimedFunc
            
            globals
                private Variables array V [8190]
            endglobals
            
            struct Variables
                unit Moved
                real x
                real y
            endstruct
            
            private function Move takes nothing returns boolean
                local integer i = TimedFunc.Value
                
                call SetUnitX(V [i].Moved, V [i].x)
                call SetUnitY(V [i].Moved, V [i].y)
                
                return false
            endfunction
            
            function MoveUnit takes unit u, real x, real y, real delay returns nothing
                local integer i
                
                call TimedFunc.Start(delay, Move)
                set i = TimedFunc.Value
                
                set V [i].Moved = u
                set V [i].x     = x
                set V [i].y     = y
            endfunction
        endlibrary
        
        // that one move the unit 'instantly' to X, Y after a delay
        
        * Example 2:
        
        library Messaging requires TimedFunc
            
            private function Display takes nothing returns boolean
                call DisplayTimedTextToPlayer(LPlayer [1], 0, 0, 10, LString [1])
                
                return false
            endfunction
            
            function DistantM takes unit s, unit t, string message, real speedfactor returns nothing
                local real delay = DistanceBetweenPoints(GetUnitLoc(s), GetUnitLoc(t)) / 1000 * speedfactor
                local string msg = "You Got a Message from " + GetPlayerName(GetOwningPlayer(s)) + ":\n" + message
                
                call TimedFunc.Start(delay, Display)
                
                call TimedFunc.SPlayer(1, GetOwningPlayer(t))
                call TimedFunc.SString(1, msg)
            endfunction
        endlibrary
        
        // a more complex library, it displays a game message to owner of unit t
        // after a delay, the delay is based on how far the distance from unit s
        // and unit t
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  V.      THE SYSTEM                                                  ||
    ][======================================================================][
    \/                                                                      \/
*/
library TimedFunc requires TimerUtils
    
    /*=\
    ]||[===------
    |A.| //The Function Format
    ]||[===------
    \=*/
    public function interface TimedFunction takes nothing returns boolean defaults false
    
    /*=\
    ]||[===------
    |B.| //The Struct
    ]||[===------
    \=*/
    struct TimedFunc
        
        /*=///===---
        |a.| Variables
        \=*///===---
        // timer + what it runs
        private  static timer               t
        private  static integer             f
        
        // indexer system
        private  static TimedFunc array Index [8190]
        private  static integer         Total = 0
        
        // the constants
        readonly static timer         Expired
        readonly static integer         Value
        
        /*=///===---
        |b.| User Customizeable - Storage
        \=*///===---
        
        // the textmacro to make a storage
        //! textmacro MAKE_STORAGE takes NAME, TYPE, VAR
        private static $TYPE$ array $VAR$ [8190]
        
        static method S$NAME$ takes integer index, $TYPE$ data returns nothing
            set Index [.Value].$VAR$ [index] = data
        endmethod
        
        static method L$NAME$ takes integer index returns $TYPE$
            return Index [.Value].$VAR$ [index]
        endmethod
        //! endtextmacro
        
        // non-handle storages, shouldn't be removed
        //! runtextmacro MAKE_STORAGE ("Boolean", "boolean", "b")
        //! runtextmacro MAKE_STORAGE ("Integer", "integer", "i")
        //! runtextmacro MAKE_STORAGE ("Real", "real", "r")
        //! runtextmacro MAKE_STORAGE ("String", "string", "s")
        
        // handle storages, can be removed/added
        //! runtextmacro MAKE_STORAGE ("Effect", "effect", "haEFF")
        //! runtextmacro MAKE_STORAGE ("Force", "force", "haFOR")
        //! runtextmacro MAKE_STORAGE ("Group", "group", "haGRO")
        //! runtextmacro MAKE_STORAGE ("Location", "location", "haLOC")
        //! runtextmacro MAKE_STORAGE ("Player", "player", "haPLA")
        //! runtextmacro MAKE_STORAGE ("Destructable", "destructable", "hawDES")
        //! runtextmacro MAKE_STORAGE ("Unit", "unit", "hawUNI")
        //! runtextmacro MAKE_STORAGE ("Item", "item", "hawITE")
        //! runtextmacro MAKE_STORAGE ("Lightning", "lightning", "hLIG")
        
        /*=///===---
        |c.| Contents
        \=*///===---
        
        // Do Not Edit
        // If you know what you are doing, do it
        
        private static method onExpire takes nothing returns nothing
            set      .Expired = GetExpiredTimer()
            set        .Value = GetTimerData(.Expired)
            
            if not TimedFunction(.Index [.Value].f).evaluate() then
                call DestroyTimer(.Expired)
                
                call .Index[.Value].destroy()
            endif
            
            set      .Expired = null
            set        .Value = 0
        endmethod
        
        static method Start takes real time, TimedFunction func returns nothing
            local integer a = 1
            local integer b = .Total + 1
            
            local integer i = 0
            
            loop
                exitwhen a == b
                
                if .Index [a] == 0 then
                    set i = a
                    
                    set a = b
                endif
                
                set a = a + 1
            endloop
            
            if i == 0 then
                set     .Total  = .Total + 1
                set i = .Total
            endif
            
            set             .Index [i]   = .create()
            set             .Index [i].f = func
            set             .Index [i].t = NewTimerEx(i)
            call TimerStart(.Index [i].t, time, true, function thistype.onExpire)
            
            set                   .Value = i
        endmethod
    endstruct
endlibrary

/*
    /\                                                                      /\
    ][======================================================================][
    ||  VI.     CONCLUSION AND FINAL NOTES                                  ||
    ][======================================================================][
    \/                                                                      \/
    
//      Timed Function
//      -*- overcold_ice -*-
//      
//          Please apologize me if there are mistakes, or something else that make
//      you hurt. We have reached the end of the page, I hope you understand my
//      really long documentation. Don't forget to put me in your Credits list
//      if you use this system. If you want to edit this system, feel free to do
//      that. But don't publish it without my permission, okay?
//          You can remove these whole documentation. Enjoy and happy coding ^.^!
*/

v2:
JASS:
/*
    /\                                                                      /\
    ][======================================================================][
    ||  I.      TABLE OF CONTENTS                                           ||
    ][======================================================================][
    \/                                                                      \/
    
    // Talking about the system
    I.,,,,,,Table of Contents
    II.,,,,,General Description
    III.,,,,Specific Description
        A.,,,,,,Requirements
        B.,,,,,,Pros
        C.,,,,,,Cons
        D.,,,,,,Executables and Constants
        E.,,,,,,How the System Works
    IV.,,,,,Usage
        A.,,,,,,Simple Usages
        B.,,,,,,Complex Usages
    
    // The system itself
    V.,,,,,,The System
        A.,,,,,,The Function Format (function interface)
        B.,,,,,,The Struct          (struct TimedFunc)
            a.,,,,,,Variables
            b.,,,,,,Contents
    
    VI.,,,,,Conclusion and Final Notes
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     GENERAL DESCRIPTION                                         ||
    ][======================================================================][
    \/                                                                      \/

//      Timed Function
//      -*- overcold_ice -*-
//      
//          This system enables users to bring their timed function (either
//      delayed, or looping) in an accurate time. Normally they would use
//      timers for those things, but those will end up confusing or something
//      else. The worst case is using TriggerSleepActions, 100% inaccurate,
//      but in exchange, making the code easier to read and write.
//          The best case is using structs. Mostly, looping structs use only
//      one timer, which end up in a bad, inaccurate codes. Let's see this
//      example:
        
        struct example
            timer             t = CreateTimer()
            
            real              a = 0
            
            example array index [8190]
            integer       total = 0
            
            method onLoop takes nothing returns nothing
                local integer i = 1
                local integer m = .total
                
                loop
                    exitwhen i == m
                    
                    set .index [i].a = .index [i].a * 2
                    
                    set i = i + 1
                endloop
            endmethod
            
            method start takes real inita returns nothing
                local thistype that = thistype.create()
                
                set that.a = inita
                
                set .total = .total + 1
                set .index [.total] = that
                
                if .total == 1 and TimerGetRemaining(.t) == 0 then
                    call TimerStart(.t, 0.5, true, function thistype.onLoop)
                endif
                
                call that.destroy()
            endmethod
        endstruct
        
//          That struct above let us to increase a real every 0.5 sec, the
//      increments are equal to the real itself (in other way, the value
//      doubles for every 0.5 sec). If we look closer, the struct isn't
//      accurate, look at this example:
        
        struct multipled extends example
            method doubled takes real r1, r2 returns nothing
                call .start(r1)
                call TriggerSleepAction(0.25)   // no more complication
                                                // for the sake of example
                call .start(r2)
            endmethod
        endstruct
        
//          The struct runs the effect twice, once after another, but within
//      a 0.25 sec delay, but they will both have their values doubled at the
//      same time (no 'almost', just in an order), rather with a 0.25 sec
//      delay. This is an inaccurate example.
//          Because of those inaccurations, here comes the Timed Function
//      System. This system lets you to do delayed, or even looping functions
//      with so much ease. It even let you to store things within it, and
//      it has some sort of index, allowing you to index structs with it.
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  II.     SPECIFIC DESCRIPTION                                        ||
    ][======================================================================][
    \/                                                                      \/
    
    ]||[===------
    |A.| //Requirements
    ]||[===------
    
    This System requires the following systems:
        * TimerUtils (by Vexorian)
    
    ]||[===------
    |B.| //Pros
    ]||[===------
    
        * Easy to use
        * Useful for both Simple and Complex Usages
    
    ]||[===------
    |C.| //Cons
    ]||[===------
        
        * Missing some flexibility
        
    ]||[===------
    |D.| //Executables and Constants
    ]||[===------
        
        * function interface TimedFunc_TimedFunction takes nothing returns boolean defaults false
            // this is the function format used by the system
            
        * call TimedFunc.Start(real time, TimedFunction func) (returns nothing)
            // real time          = real timeout in timers
            // TimedFunction func = your function that will be runt
            //                      when the timer expire, you can
            //                      set this to null, to make the
            //                      system do nothing when the timer
            //                      expire
            //      doing this will set TimedFunc.Value to whatever
            //  the timer value in this new one. use that to specify
            //  struct indexes
        
        * TimedFunc.Expired
            //      this constant is equal to GetExpiredTimer()
        
        * TimedFunc.Value
            //      this constant is the unique number of the timer/system
            //  that currently runs. use this variable to index your structs
    
    ]||[===------
    |E.| //How the System Works
    ]||[===------
        
        // Basically, it works like this:
        * First, it checks all available spaces,
            - if there are an empty space (the timer are not running), then do
                - use that timer for the next actions
            - if there are no empty spaces (whether the timers are all running, or not created at all), then do
                - create a new timer for the next actions
        
        * Second, it starts the timer and set TimedFunc.Value to whatever the current timer's value is
        
        * Finally, when the timer expires, do
            - the function specified by the user,
                - if, it returns true, then restart the timer, otherwise, destroy it
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  IV.     USAGE                                                       ||
    ][======================================================================][
    \/                                                                      \/

    ]||[===------
    |A.| //Simple Usages
    ]||[===------
        
        * Example 1:
        
        library Initialization initializer Init requires TimedFunc
            function FOWDisable takes nothing returns boolean
                call FogEnable(false)
                
                return false
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(5, FOWDisable)
            endfunction
        endlibrary
        
        // that library disables fog of war 5 seconds after initialization
        
        library FreeGold initializer Init requires TimedFunc
            function Looping takes nothing returns boolean
                local integer i = 0
                local integer m = 11
                
                loop
                    exitwhen i == m
                    
                    call SetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(Player(i), PLAYER_STATE_RESOURCE_GOLD) + 1)
                    
                    set i = i +1
                endloop
                
                return true
            endfunction
            
            function Init takes nothing returns nothing
                call TimedFunc.Start(1, Looping)
            endfunction
        endlibrary
        
        // this one adds one gold to player 1 - player 12 for every 1 second
    
    ]||[===------
    |B.| //Complex Usages
    ]||[===------
        
        * Example:
        
        library MoveUnit requires TimedFunc
            
            globals
                private Variables array V [8190]
            endglobals
            
            struct Variables
                unit Moved
                real x
                real y
            endstruct
            
            private function Move takes nothing returns boolean
                local integer i = TimedFunc.Value
                
                call SetUnitX(V [i].Moved, V [i].x)
                call SetUnitY(V [i].Moved, V [i].y)
                
                return false
            endfunction
            
            function MoveUnit takes unit u, real x, real y, real delay returns nothing
                local integer i
                
                call TimedFunc.Start(delay, Move)
                set i = TimedFunc.Value
                
                set V [i].Moved = u
                set V [i].x     = x
                set V [i].y     = y
            endfunction
        endlibrary
        
        // that one move the unit 'instantly' to X, Y after a delay
*/
/*
    /\                                                                      /\
    ][======================================================================][
    ||  V.      THE SYSTEM                                                  ||
    ][======================================================================][
    \/                                                                      \/
*/
library TimedFunc requires TimerUtils
    
    /*=\
    ]||[===------
    |A.| //The Function Format
    ]||[===------
    \=*/
    public function interface TimedFunction takes nothing returns boolean defaults false
    
    /*=\
    ]||[===------
    |B.| //The Struct
    ]||[===------
    \=*/
    struct TimedFunc
        
        /*=///===---
        |a.| Variables
        \=*///===---
        // timer + what it runs
        private  static timer               t
        private  static integer             f
        
        // indexer system
        private  static TimedFunc array Index [8190]
        private  static integer         Total = 0
        
        // the constants
        readonly static timer         Expired
        readonly static integer         Value
        
        /*=///===---
        |b.| Contents
        \=*///===---
        
        // Do Not Edit
        // If you know what you are doing, do it
        
        private static method onExpire takes nothing returns nothing
            set      .Expired = GetExpiredTimer()
            set        .Value = GetTimerData(.Expired)
            
            if not TimedFunction(.Index [.Value].f).evaluate() then
                call ReleaseTimer(.Expired)
                
                call .Index[.Value].destroy()
            endif
            
            set      .Expired = null
            set        .Value = 0
        endmethod
        
        static method Start takes real time, TimedFunction func returns nothing
            local integer a = 1
            local integer b = .Total + 1
            
            local integer i = 0
            
            loop
                exitwhen a == b
                
                if .Index [a] == 0 then
                    set i = a
                    
                    set a = b
                endif
                
                set a = a + 1
            endloop
            
            if i == 0 then
                set     .Total  = .Total + 1
                set i = .Total
            endif
            
            set             .Index [i]   = .create()
            set             .Index [i].f = func
            set             .Index [i].t = NewTimerEx(i)
            call TimerStart(.Index [i].t, time, true, function thistype.onExpire)
            
            set                   .Value = i
        endmethod
    endstruct
endlibrary

/*
    /\                                                                      /\
    ][======================================================================][
    ||  VI.     CONCLUSION AND FINAL NOTES                                  ||
    ][======================================================================][
    \/                                                                      \/
    
//      Timed Function
//      -*- overcold_ice -*-
//      
//          Please apologize me if there are mistakes, or something else that make
//      you hurt. We have reached the end of the page, I hope you understand my
//      really long documentation. Don't forget to put me in your Credits list
//      if you use this system. If you want to edit this system, feel free to do
//      that. But don't publish it without my permission, okay?
//          You can remove these whole documentation. Enjoy and happy coding ^.^!
*/
 
Last edited:
You're using TimerUtils, I don't know why you are using DestroyTimer when you used NewTimer to get that timer.

We generally advise: avoid function interfaces. They create duplicates of the pointed function, making your map file size much larger, and also evaluate triggers which means extra handles and extra slow performance.

You say looping structs are bad and innaccurate, well <= 0.3 timeouts really don't matter if you just throw it all into a loop, because the visual difference of those kind of timeouts is negligible. It's also really important that timeouts that low are as lightweight as possible, so using a timer + an evaluation per instance compared to a timer per struct is like comparing Jupiter's mass to Mercury's mass in terms of performance gain.

Nestharus and Dr Super Good also ran some tests that showed that low-timeout timers that expire at the same time can get unsafe (map crashes, low FPS) in larger quantities.

Rather than use your library that has tons of extra overhead because of all the textmacros you run, why not simply use any of the other 10,000,000 timer systems out there like we have been doing for the last 10 years?
 
Level 7
Joined
Apr 30, 2011
Messages
359
and then . . . .
any suggestions ?

maybe i'll just remove those storage things . . .

EDIT: removed those things on v2

maybe i should use old "struct YourStruct extends TimedFunc"
+ some stub method of onExpire . . . . (or make it extends an interface)
 
TimerUtils does data attachment for periods > 0.3 just fine, and for the other stuff you can use Constant Timer Loop 32 by Nestharus or Timer Tools by Nestharus.

Timer systems are a "favorite" for people to code, and 90% of them have to be thrown away regardless of how much work was put into them, because they simply don't offer anything which wasn't already offered.
 
Top