/*
3.0.0.0
struct Timer extends array
readonly static Timer expired //CALL EXACTLY ONCE!
readonly real remaining
readonly real elapsed
readonly real timeout
method createDialog takes nothing returns timerdialog
static method create takes real timeout, code onExpire returns Timer
method destroy takes nothing returns nothing
*/
library TimerTools
/*
* Checks if any CTM modules appear to be constant
*/
static if DEBUG_MODE then
private struct DEBI extends array
static constant boolean SUGGEST_MODULE_CHANGES = false
endstruct
endif
globals
/*************************************************************************************
*
* RELATIVE_MERGE
*
* Effects the accuracy of first tick. The smaller the merge value, the less chance two
* timers have of sharing the same first tick, which leads to worse performance.
* However, a larger merge value decreases the accuracy of the first tick.
*
* Formula: Ln(RELATIVE_MERGE/timeout)/230.258509*3600*timeout
* Ln(64000/3600)/230.258509*3600=44.995589035797596625536391805959 seconds max off
*
*************************************************************************************/
private constant real RELATIVE_MERGE = 64000
/*************************************************************************************
*
* CONSTANT_MERGE
*
* Effects the accuracy of the first tick. This is a constant merge. If this value +.01 is
* greater than the value calculated from RELATIVE_MERGE, the timer auto merges.
*
* Constant merge must be greater than 0.
*
*************************************************************************************/
private constant real CONSTANT_MERGE = .1
endglobals
private keyword TimerNode
private keyword TimerGroup
globals
private code expirationCode
endglobals
//! textmacro T_ALLOC
private static integer instanceCount = 0
private static integer array recycler
private static method allocate takes nothing returns thistype
local thistype this = recycler[0]
if (0 == this) then
set instanceCount = instanceCount + 1
debug set recycler[instanceCount] = -1
return instanceCount
endif
set recycler[0] = recycler[this]
debug set recycler[this] = -1
return this
endmethod
private method deallocate takes nothing returns boolean
debug if (recycler[this] == -1) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DOUBLE FREE ERROR: TIMER " + StringCase("", true))
debug return false
debug endif
set recycler[this] = recycler[0]
set recycler[0] = this
return true
endmethod
//! endtextmacro
//hiveworkshop.com/forums/jass-functions-413/snippet-natural-logarithm-108059/
//credits to BlinkBoy
private function Ln takes real a returns real
local real s = 0
loop
exitwhen a < 2.71828
set a = a/2.71828
set s = s + 1
endloop
return s + (a - 1)*(1 + 8/(1 + a) + 1/a)/6
endfunction
private module Queue
thistype first
thistype last
thistype next
thistype head
method add takes thistype node returns nothing
if (0 == first) then
set first = node
else
set last.next = node
endif
set last = node
set node.next = 0
set node.head = this
endmethod
method clear takes nothing returns nothing
set first = 0
set last = 0
endmethod
endmodule
/*
private module SingleQueue
static thistype last
thistype next
static method operator first takes nothing returns thistype
return thistype(0).next
endmethod
static method add takes thistype this returns nothing
set next = 0
set last.next = this
set last = this
endmethod
static method clear takes nothing returns nothing
set last = 0
set thistype(0).next = 0
endmethod
endmodule
*/
private module SingleStack
thistype next
static method operator first takes nothing returns thistype
return thistype(0).next
endmethod
static method push takes thistype this returns nothing
set next = thistype(0).next
set thistype(0).next = this
endmethod
static method clear takes nothing returns nothing
set thistype(0).next = 0
endmethod
endmodule
private module CircularList
thistype first
thistype next
thistype prev
thistype head
debug private boolean added
method operator last takes nothing returns thistype
return first.prev
endmethod
method add takes thistype node returns nothing
debug if (0 != node.head) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"T CIRCULAR LIST ERROR: ATTEMPT TO ADD ALREADY ADDED NODE TO LIST")
debug set node = 1/0
debug endif
if (0 == first) then
set first = node
set node.next = node
set node.prev = node
else
set node.next = first
set node.prev = first.prev
set node.prev.next = node
set node.next.prev = node
endif
set node.head = this
endmethod
method remove takes nothing returns boolean
local thistype node = this
set this = node.head
debug if (0 == this) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"T CIRCULAR LIST ERROR: ATTEMPT TO REMOVE ALREADY REMOVE NODE FROM LIST")
debug set node = 1/0
debug endif
set node.prev.next = node.next
set node.next.prev = node.prev
if (first == node) then
set first = node.next
if (first == node) then
set first = 0
endif
endif
set node.next = 0
set node.prev = 0
set node.head = 0
return 0 == first
endmethod
endmodule
/*
private module List
thistype first
thistype last
thistype next
thistype prev
thistype head
method add takes thistype node returns nothing
debug if (0 != node.head) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"T LIST ERROR: ATTEMPT TO ADD ALREADY ADDED NODE TO LIST")
debug set node = 1/0
debug endif
if (0 == first) then
set first = node
set last = node
else
set last.next = node
set node.prev = last
set last = node
endif
set node.head = this
endmethod
method remove takes nothing returns boolean
local thistype node = this
set this = node.head
debug if (0 == this) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"T LIST ERROR: ATTEMPT TO REMOVE ALREADY REMOVE NODE FROM LIST")
debug set node = 1/0
debug endif
if (first == node) then
set node.next.prev = 0
set first = node.next
else
set node.next.prev = node.prev
endif
if (last == node) then
set node.prev.next = 0
set last = node.prev
else
set node.prev.next = node.next
endif
set node.next = 0
set node.prev = 0
set node.head = 0
return 0 == first
endmethod
endmodule
*/
/*
* This stores timers that are to be added to a sublist of a timer group after
* that sublist expires.
*
* This is iterated over after a sublist expires
*/
private struct TimerAddQueue extends array
implement Queue
endstruct
/*
* This stores timers to be destroyed. This is iterated over after any sublist expires.
*/
private struct TimerDestructionStack extends array
implement SingleStack
endstruct
/*
* A sublist is a list of actual timer nodes that expire at the same time
*/
private struct TimerSubGroup extends array
implement CircularList
//! runtextmacro T_ALLOC()
timer timer
trigger trigger
boolean running
method fire takes nothing returns nothing
call TriggerEvaluate(trigger)
endmethod
method get takes nothing returns thistype
local thistype node = first
set first = first.next
return node
endmethod
method operator queue takes nothing returns TimerAddQueue
return this
endmethod
static method operator stack takes nothing returns TimerDestructionStack
return 0
endmethod
static method create takes real timeout returns thistype
local thistype this = allocate()
if (null == timer) then
set timer = CreateTimer()
set trigger = CreateTrigger()
endif
call TimerStart(timer, timeout, true, expirationCode)
return this
endmethod
method destroy takes nothing returns nothing
call deallocate()
call TriggerClearConditions(trigger)
call PauseTimer(timer)
endmethod
method register takes TimerNode node returns triggercondition
call add(node)
return TriggerAddCondition(trigger, node.code)
endmethod
method unregister takes TimerNode node returns boolean
call TriggerRemoveCondition(trigger, node.registered)
return thistype(node).remove() and 0 == TimerAddQueue(this).first
endmethod
endstruct
private struct TimerNode extends array
//! runtextmacro T_ALLOC()
boolexpr code
readonly triggercondition registered
readonly TimerSubGroup owner
private method register takes nothing returns nothing
set registered = owner.register(this)
endmethod
static method create takes boolexpr codeToRegister, TimerSubGroup owner, boolean addAfter returns thistype
local thistype this = allocate()
set code = codeToRegister
set this.owner = owner
if (addAfter) then
call owner.queue.add(this)
else
call register()
endif
return this
endmethod
method destroy takes nothing returns nothing
call deallocate()
if (null != registered) then
if (owner.unregister(this)) then
call owner.destroy()
endif
set registered = null
endif
set code = null
set owner = 0
endmethod
private static method registerNodes takes TimerAddQueue queue returns nothing
local thistype this = queue.first
loop
exitwhen 0 == this
if (null == code) then
call deallocate()
else
call register()
endif
set this = TimerAddQueue(this).next
endloop
call queue.clear()
endmethod
private static method destroyNodes takes nothing returns nothing
local thistype this = TimerDestructionStack.first
loop
exitwhen 0 == this
call destroy()
set this = TimerDestructionStack(this).next
endloop
call TimerDestructionStack.clear()
endmethod
static method cleanup takes TimerAddQueue queue returns nothing
call registerNodes(queue)
call destroyNodes()
endmethod
endstruct
/*
* This stores a list of sublists that have the same timeouts
*/
private struct TimerGroup extends array
implement CircularList
readonly boolean permanent
readonly real threshold
readonly real timeout
readonly static TimerSubGroup expired = 0
private method operator empty takes nothing returns boolean
return 0 == first
endmethod
private method operator addBeforeFirst takes nothing returns boolean
return permanent or TimerGetElapsed(TimerSubGroup(first).timer) <= threshold
endmethod
private method operator addBeforeLast takes nothing returns boolean
return TimerGetElapsed(TimerSubGroup(last).timer) <= threshold
endmethod
private method operator addAfterLast takes nothing returns boolean
return TimerGetRemaining(TimerSubGroup(last).timer) <= threshold
endmethod
static method getTimerGroup takes real timeout returns thistype
local integer i = R2I(timeout) * 320
if (0 == i) then
return 1
elseif (8191 < i) then
return 8191
endif
return i
endmethod
private method getExpired takes nothing returns TimerSubGroup
local TimerSubGroup expired = first
set first = first.next
return expired
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = getTimerGroup(TimerGetTimeout(GetExpiredTimer()))
local TimerSubGroup expired = getExpired()
set thistype.expired = expired
set expired.running = true
call expired.fire()
set expired.running = false
set thistype.expired = 0
call TimerNode.cleanup(expired)
if (0 == expired.first) then
call expired.remove()
endif
endmethod
static method operator [] takes real timeout returns thistype
local thistype timerGroup = getTimerGroup(timeout)
if (0 == timeout) then
set timeout = timerGroup/320.
set timerGroup.threshold = Ln(RELATIVE_MERGE/timerGroup.timeout)/230.258509*timerGroup.timeout
if (timerGroup.threshold < CONSTANT_MERGE) then
set timerGroup.threshold = CONSTANT_MERGE
endif
set timerGroup.permanent = CONSTANT_MERGE < (timerGroup.timeout - timerGroup.threshold)/2
endif
return timerGroup
endmethod
method register takes boolexpr onExpire returns thistype
local TimerSubGroup subgroup
local TimerNode node
if (empty) then
set subgroup = TimerSubGroup.create(timeout)
set node = TimerNode.create(onExpire, subgroup, false)
call add(subgroup)
elseif (addBeforeFirst) then
set subgroup = first
set node = TimerNode.create(onExpire, subgroup, false)
elseif (addBeforeLast) then
set subgroup = last
set node = TimerNode.create(onExpire, subgroup, false)
elseif (addAfterLast) then
set subgroup = last
set node = TimerNode.create(onExpire, subgroup, true)
else
set subgroup = TimerSubGroup.create(timeout)
set node = TimerNode.create(onExpire, subgroup, false)
call add(subgroup)
endif
return node
endmethod
method destroy takes nothing returns nothing
local TimerNode node = this
local TimerSubGroup owner = node.owner
set this = thistype(owner).head
if (owner.running) then
if (null == node.registered) then
set node.code = null
else
call TimerDestructionStack.push(node)
endif
else
call node.destroy()
if (0 == owner.first and 0 == owner.queue.first) then
call thistype(owner).remove()
endif
endif
endmethod
private static method onInit takes nothing returns nothing
set expirationCode = function thistype.onExpire
endmethod
endstruct
struct Timer extends array
/*
* CALL ONCE!
*/
static method operator expired takes nothing returns thistype
return TimerGroup.expired.get()
endmethod
method operator remaining takes nothing returns real
return TimerGetRemaining(TimerNode(this).owner.timer)
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(TimerNode(this).owner.timer)
endmethod
method operator timeout takes nothing returns real
return TimerGetTimeout(TimerNode(this).owner.timer)
endmethod
method createDialog takes nothing returns timerdialog
return CreateTimerDialog(TimerNode(this).owner.timer)
endmethod
static method create takes real timeout, code onExpire returns thistype
return TimerGroup[timeout].register(Condition(onExpire))
endmethod
method destroy takes nothing returns nothing
call TimerGroup(this).destroy()
endmethod
endstruct
endlibrary
private static method onExpire takes nothing returns nothing
local thistype this = getTimerGroup(TimerGetTimeout(GetExpiredTimer()))
local TimerSubGroup expired = getExpired()
set thistype.expired = expired
set running = true
call expired.fire()
set running = false
set thistype.expired = 0
call TimerNode.cleanup(expired)
if (0 == expired.first) then
call expired.remove()
endif
endmethod
private struct TimerAddQueue extends array
private struct TimerDestructionStack extends array
private struct TimerSubGroup extends array
private struct TimerNode extends array
private struct TimerGroup extends array
struct Timer extends array
struct Tester extends array
private static hashtable testTable = InitHashtable()
private static timer array testArray
private static boolexpr c
private static code m
private static integer funcId
private static integer count = 0
private static boolean enabled = true
implement TimerHead
private static method test takes nothing returns nothing
local timer t
local real timeout = GetRandomReal(.01, 1.5)
local Timer t2 = Timer.create(timeout, c, funcId)
if (testArray[t2] != null) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ALLOCATION ERROR")
set enabled = false
set timeout = 1/0
else
set t = CreateTimer()
endif
call TimerStart(t, timeout, true, null)
call SaveInteger(testTable, GetHandleId(t), 0, t2)
set testArray[t2] = t
set t = null
set count = count + 1
call add(t2)
endmethod
private static method validate takes nothing returns nothing
local Timer node = thistype(Timer.expired).first
local Timer node2 = Timer.getExpired(funcId)
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE HEAD CORRUPTION")
set enabled = false
set node = 1/0
endif
loop
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE CORRUPTION")
set enabled = false
set node = 1/0
endif
set node = node.next
set node2 = node2.next
exitwhen node == 0 and node2 == 0
endloop
endmethod
private static method expire takes nothing returns boolean
local Timer node = thistype(Timer.expired).first
if (not enabled) then
return false
endif
call validate()
if (count < 100*8) then
call test()
endif
loop
if (LoadInteger(testTable, GetHandleId(testArray[node]), 0) != node) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ERROR: "+I2S(LoadInteger(testTable, GetHandleId(testArray[node]), 0))+ " != "+I2S(node))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Handle Id: "+I2S(GetHandleId(testArray[node])))
set enabled = false
return false
endif
if (GetRandomInt(0, 100) > 75) then
call DestroyTimer(testArray[node])
set testArray[node] = null
call node.destroy()
set count = count - 1
call remove(node)
endif
if (count < 250*8 and GetRandomInt(0, 100) > 65) then
call test()
endif
set node = node.next
exitwhen node == 0
endloop
if (count < 100*8) then
call test()
endif
call updateFirst(Timer.expired)
return false
endmethod
private static method onInit takes nothing returns nothing
set c = Condition(function thistype.expire)
set m = function thistype.expire
set funcId = thistype.expire
call test()
endmethod
endstruct
struct Tester2 extends array
private static hashtable testTable = InitHashtable()
private static timer array testArray
private static boolexpr c
private static code m
private static integer funcId
private static integer count = 0
private static boolean enabled = true
implement TimerHead
private static method test takes nothing returns nothing
local timer t
local real timeout = GetRandomReal(.01, 1.5)
local Timer t2 = Timer.create(timeout, c, funcId)
if (testArray[t2] != null) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ALLOCATION ERROR")
set enabled = false
set timeout = 1/0
else
set t = CreateTimer()
endif
call TimerStart(t, timeout, true, null)
call SaveInteger(testTable, GetHandleId(t), 0, t2)
set testArray[t2] = t
set t = null
set count = count + 1
call add(t2)
endmethod
private static method validate takes nothing returns nothing
local Timer node = thistype(Timer.expired).first
local Timer node2 = Timer.getExpired(funcId)
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE HEAD CORRUPTION")
set enabled = false
set node = 1/0
endif
loop
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE CORRUPTION")
set enabled = false
set node = 1/0
endif
set node = node.next
set node2 = node2.next
exitwhen node == 0 and node2 == 0
endloop
endmethod
private static method expire takes nothing returns boolean
local Timer node = thistype(Timer.expired).first
if (not enabled) then
return false
endif
call validate()
if (count < 100*8) then
call test()
endif
loop
if (LoadInteger(testTable, GetHandleId(testArray[node]), 0) != node) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ERROR: "+I2S(LoadInteger(testTable, GetHandleId(testArray[node]), 0))+ " != "+I2S(node))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Handle Id: "+I2S(GetHandleId(testArray[node])))
set enabled = false
return false
endif
if (GetRandomInt(0, 100) > 75) then
call DestroyTimer(testArray[node])
set testArray[node] = null
call node.destroy()
set count = count - 1
call remove(node)
endif
if (count < 250*8 and GetRandomInt(0, 100) > 65) then
call test()
endif
set node = node.next
exitwhen node == 0
endloop
if (count < 100*8) then
call test()
endif
call updateFirst(Timer.expired)
return false
endmethod
private static method onInit takes nothing returns nothing
set c = Condition(function thistype.expire)
set m = function thistype.expire
set funcId = thistype.expire
call test()
endmethod
endstruct
TimerUtils is now useless... >.<
The only thing native timers are good for now are 1 shot timers with no data attachment or null functions
scope Testing
//! runtextmacro TEST_TIMER("1")
//! runtextmacro TEST_TIMER("2")
//! runtextmacro TEST_TIMER("3")
//! runtextmacro TEST_TIMER("4")
//! runtextmacro TEST_TIMER("5")
//! runtextmacro TEST_TIMER("6")
//! textmacro TEST_TIMER takes N
private struct Tester$N$ extends array
private static hashtable testTable = InitHashtable()
private static timer array testArray
private static boolexpr c
private static code m
private static integer funcId
private static integer count = 0
private static boolean enabled = true
private integer ran1
private integer ran2
implement TimerHead
private static method expire2 takes nothing returns nothing
local thistype this = LoadInteger(testTable, GetHandleId(GetExpiredTimer()), 0)
set this.ran2 = this.ran2 + 1
if (IAbsBJ(thistype(this).ran1 - thistype(this).ran2) > 1) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"TIMER RUN ERROR #2: "+I2S(thistype(this).ran1)+ " != "+I2S(thistype(this).ran2))
set enabled = false
endif
endmethod
private static method test takes nothing returns nothing
local timer t
local real timeout = GetRandomReal(.1, .4)
local Timer t2 = Timer.create(timeout, c, funcId)
if (testArray[t2] != null) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ALLOCATION ERROR")
set enabled = false
set timeout = 1/0
else
set t = CreateTimer()
endif
call TimerStart(t, R2I(timeout*320 + .5)/320., true, function thistype.expire2)
call SaveInteger(testTable, GetHandleId(t), 0, t2)
set testArray[t2] = t
set t = null
set count = count + 1
call add(t2)
set thistype(t2).ran1 = 0
set thistype(t2).ran2 = 0
endmethod
private static method validate takes nothing returns nothing
local Timer node = thistype(Timer.expired).first
local Timer node2 = Timer.getExpired(funcId)
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE HEAD CORRUPTION")
set enabled = false
set node = 1/0
endif
loop
if (node != node2) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,I2S(node) + " != "+I2S(node2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"FUTURE CORRUPTION")
set enabled = false
set node = 1/0
endif
set node = node.next
set node2 = node2.next
exitwhen node == 0 and node2 == 0
endloop
endmethod
private static method expire takes nothing returns boolean
local Timer node = thistype(Timer.expired).first
if (not enabled) then
return false
endif
call validate()
if (count < 100*2) then
call test()
endif
loop
if (LoadInteger(testTable, GetHandleId(testArray[node]), 0) != node) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"ERROR: "+I2S(LoadInteger(testTable, GetHandleId(testArray[node]), 0))+ " != "+I2S(node))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Handle Id: "+I2S(GetHandleId(testArray[node])))
set enabled = false
return false
endif
if (GetRandomInt(0, 100*2) > 75) then
call PauseTimer(testArray[node])
call DestroyTimer(testArray[node])
set testArray[node] = null
call node.destroy()
set count = count - 1
call remove(node)
endif
if (count < 100*2 and GetRandomInt(0, 100) > 65) then
call test()
endif
set thistype(node).ran1 = thistype(node).ran1 + 1
if (IAbsBJ(thistype(node).ran1 - thistype(node).ran2) > 1) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"TIMER RUN ERROR: "+I2S(thistype(node).ran1)+ " != "+I2S(thistype(node).ran2))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,R2S(TimerGetRemaining(testArray[node])))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,R2S(TimerGetTimeout(GetExpiredTimer()))+ " == "+R2S(TimerGetTimeout(testArray[node])))
set enabled = false
set node = 1/0
endif
set node = node.next
exitwhen node == 0
endloop
if (count < 100*2) then
call test()
endif
call updateFirst(Timer.expired)
return false
endmethod
private static method onInit takes nothing returns nothing
set c = Condition(function thistype.expire)
set m = function thistype.expire
set funcId = thistype.expire
call test()
endmethod
endstruct
//! endtextmacro
endscope
static method create takes real timeout, boolexpr onExpire, integer funcId returns Timer
* Example of Constant Timer Loop 32
* struct MyTimer extends array
* integer myValue
* implement CTL
* local string s="My value is "
* implement CTLExpire
* call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(myValue))
* call destroy()
* implement CTLNull
* set s=null //pointless, but shows how to use null block
* implement CTLEnd
* endstruct
*
* set MyTimer.create().myValue=16 //will display "My value is 16" 32x a second
function Hello takes nothing returns boolean
return false
endfunction
call Timer.create(5, Condition(function Hello), Hello)
* readonly Timer first
* - thistype(Timer.expired).first == Timer.getExpired(funcId)
struct Test extends array
private static method onExpr takes nothing returns boolean
call Timer(Timer.getExpired(thistype.onExpr)).destroy()
call BJDebugMsg("Hello")
return false
endmethod
private static method test takes nothing returns boolean
call Timer(Timer.getExpired(thistype.test)).destroy()
call Timer.create(5, Condition(function thistype.onExpr), thistype.onExpr)
return false
endmethod
private static method onInit takes nothing returns nothing
call Timer.create(5, Condition(function thistype.onExpr), thistype.onExpr)
call Timer.create(1, Condition(function thistype.test), thistype.test)
endmethod
endstruct
readonly Timer next
struct Foo extends array
private real bar
private static method expired takes nothing returns boolean
local Timer t = Timer.getExpired(thistype.expired)
local thistype this
loop
exitwhen t == 0
set this = t
call BJDebugMsg(R2S(this.bar))
// only one shot
call Timer[t].destroy()
set t = t.next
endloop
return false
endmethod
private static method onInit takes nothing returns nothing
local thistype this = Timer.create(1, Condition(function thistype.expired), thistype.expired)
set this.bar = 4
set this = Timer.create(1, Condition(function thistype.expired), thistype.expired)
set this.bar = 2
endmethod
endstruct
What does funcId do??
implement TimerHead
private Timer timer
private static boolexpr onExpireC
private static integer onExpireI
method destroy takes nothing returns nothing
//whenever you destroy a timer, remove it
call remove(timer)
call timer.destroy()
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set timer = Timer.create(1, onExpireC, onExpireI)
//whenever you create a timer, add it
call add(timer)
return this
endmethod
private static method onExpire takes nothing returns boolean
local thistype this = thistype(Timer.expired).first
loop
//code
call destroy() //destroy the timer?
//iteration
set this = Timer(this).next
exitwhen this == 0
endloop
return false
endmethod
//in a module
private static method onInit takes nothing returns nothing
set onExpireC = Condition(function thistype.onExpire)
set onExpireI = onExpire
endmethod
static method moveUnit takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if UnitAlive(GetUnitById(this)) then
set .tick = .tick - 1
if .tick <= 0 then
set .tick = 8
call IssuePointOrder(GetUnitById(this), "move", GetUnitX(GetUnitById(this)) + GetRandomReal(-2000, 2000), GetUnitY(GetUnitById(this)) + GetRandomReal(-2000, 2000))
endif
else
call UnitIndex(this).unlock()
call ReleaseTimer(GetExpiredTimer())
endif
endmethod
Is TimerTools at its current state ready to use? Or more precise superior to the 2.x versions with CTC, CTTC, CTM modules or CTL?
you could also perhaps update it with your ThrowError functionallity, I think the ThrowError(........) is nicer then DisplayTimedTextToPlayer(...)