Nestharus
o-o
- Reaction score
- 83
JASS:
library Tt /* v1.1.0.1
*************************************************************************************
*
* Timer Tools, or Tt for short, provides two types of timers.
*
* Timer tools timers use a special merging algorithm. When two timers merge, they
* both expiring on the same timer. If two timers expire close to the same time, they
* merge. The first tick accuracy is based on the size of the timer's timeout. The larger
* the timeout, the more inaccurate the first tick can be. The tick only becomes inaccurate
* if it merges with another timer.
*
* Timer timeouts are changed to a very slightly degree so that timers have a higher chance
* of merging. For example, 1.001 and 1 would have absolutely no chance of merging because
* they both have different timeouts. Timer Tools timers attempt to remedy this situation
* by making the timeout a little inaccurate (still highly accurate). This means that if
* 2 timers have timeouts that are very close to each other, like 1.001 and 1, they'd both
* have a timeout of 1 and have a chance to merge. 1.001 would also be able to merge with
* other future 1 second timeout timers if any are added. The timeout inaccuracy is based
* on the size of the timeout. For example, 1.1's actual timeout would be
* 1.09999 (just about 1.1) while 3600's timeout would be 3555.56, or about 44.44 seconds off.
* Keep in mind that 3600 seconds is 1 full hour, so 44.44 seconds relative to that entire hour
* really isn't that much. It's accuracy is 98.7654% at that point. The accuracy setting
* can be modified. Increasing it to a value like 10000000 will pretty much remove all inaccuracy.
* 3600 with 10000000 accuracy turns into 3599.712.
*
************************************************************************************
*
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
* SETTINGS
*/
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: Log(RELATIVE_MERGE/timeout)/100*timeout
* Log(64000/3600)/100*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. It just adds
* this value to the relative merge.
*
* The max 3600's first tick can be off is 44.995589035797596625536391805959+.03
* or 45.295589035797596625536391805959 seconds.
*
*************************************************************************************/
private constant real CONSTANT_MERGE=.3
/*************************************************************************************
*
* RELATIVE_ACCURACY
*
* Effects the timeout of a timer. A larger number will give a more accurate timeout.
* This allows two timers of relatively close timeouts to have a chance to merge and
* lowers the total number of timeouts within the system. More timeouts means less
* performance.
*
* Formula: RELATIVE_ACCURACY/Round(RELATIVE_ACCURACY/timeout)
* 64000/Round(64000/3600)=3555.5555555555555555555555555556
*
* 3600's timeout will be 3555.5555555555555555555555555556 instead of 3600
*
*************************************************************************************/
private constant real RELATIVE_ACCURACY=64000
/*************************************************************************************
*
* CONSTANT_ACCURACY
*
* This essentially just removes small decimals. An accuracy of 800 would allow for
* decimals in units of .00125. Removing small decimals helps increase the performance
* of very small timeouts by a drastic amount.
*
* Formula: Round(timeout*CONSTANT_ACCURACY)/CONSTANT_ACCURACY
* Round(3555.5555555555555555555555555556*800)/800=3555.555
*
* If a timeout is less than the minimum possible timeout, then it is changed to the
* minimum possible timeout.
*
*************************************************************************************/
private constant real CONSTANT_ACCURACY=800
endglobals
/*
************************************************************************************
*
* Constant Timer Queue Module
* - Constant timeouts
* Constant Merged Timer Module
* - Dynamic timeouts
* Constant Light Timer Queue 1
* - Specializes in 1 shot timers with constant 2+ timeouts. Useful for expirations on spell effects.
*
* private static constant real TIMEOUT Timer Queue
* - Timer Queue expects this value declare at the top of the struct
* - This value represents the constant timeout of the queue
*
* real timeout Merged Timers
* - How often the timer expires
* readonly real elapsed Merged Timers and Timer Queue
* - How long the timer has run since its last expiration
* readonly real remaining Merged Timers and Timer Queue
* - How long the timer has left before it next expires
*
* static method create takes real to returns thistype Merged Timers
* - Creates a new timer given a timeout
* static method create takes nothing returns thistype Timer Queue, Light Timer Queue 1
* - Creates a new timer of a constant timeout
*
* method destroy takes nothing returns nothing Timer Queue, Light Timer Queue 1, Merged Timers
* Note: Light Timer Queue auto destroys in expiration method
* - Destroys created timer
*
* Module
* - Implement these three modules into the struct in order to do a Constant Merged Timer. Code
* - written in between the modules is run whenever a timer expires.
* -
* - The first section allows local declarations and ini. It runs before a set of expiring timers
* - are about to run, meaning that there is no access to a timer in this section.
* -
* - The second section runs for each timer. Access the expiring timer via the this keyword.
*
* module CTM Merged Timers
* module CTQ Timer Queue
* module CLTQ1 Light Timer Queue
* - Declare locals in here
* - Run ini code
* module CTMExpire Merged Timers
* module CTQExpire Timer Queue
* - Run timer code
* -
* - thistype this refers to current expiring timer\
* module CTMNull Merged Timers
* module CTQNull Timer Queue
* - Null locals here
* module CTMEnd Merged Timers
* module CTQEnd Timer Queue
* module CLTQ1End Light Timer Queue
*
* Example of Merged Timers
* struct MyTimer extends array
* integer myValue
* implement CTM
* local string s="My value is "
* implement CTMExpire
* call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(myValue))
* call destroy()
* implement CTMNull
* set s=null //pointless, but shows how to use null block
* implement CTMEnd
* endstruct
*
* set MyTimer.create(5).myValue=16 //will display "My value is 16" in 5 seconds
*
************************************************************************************/
globals
private timer array st //segment timers
private integer ic=0 //instance count
private integer array sn //segment next
private integer array sp //segment previous
private boolean array sr //segment running
private integer array sf //segment first
private integer array tn //timer next, list recycler
private integer array tp //timer previous
private integer array tf //timer first
private integer array nh //node head
private real array nt //node timeout
private integer array qt //queue->timeout id, segment->handle id
private integer array an //after next
private integer array ap //after prev
private integer array as //after segment
private Table array tl
private hashtable tt=InitHashtable()
endglobals
//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
//! textmacro TIMER_TOOLS_ALLOCATE_TIMER takes TIMER_REF, TIMEOUT_REF, TABLE_REF
if (0==tn[0]) then
set $TIMER_REF$=ic+1
set ic=$TIMER_REF$
else
set $TIMER_REF$=tn[0]
set tn[0]=tn[$TIMER_REF$]
set nh[$TIMER_REF$]=0
endif
set nt[$TIMER_REF$]=$TIMEOUT_REF$
set tl[$TIMER_REF$]=$TABLE_REF$
//! endtextmacro
//! textmacro TIMER_TOOLS_GET_TIMER_QUEUE takes THIS_REF, QUEUE_REF, ID_REF
set $QUEUE_REF$=$THIS_REF$[$ID_REF$]
if (0==$QUEUE_REF$) then
if (0==tn[0]) then
set $QUEUE_REF$=ic+1
set ic=$QUEUE_REF$
else
set $QUEUE_REF$=tn[0]
set tn[0]=tn[$QUEUE_REF$]
endif
set sf[$QUEUE_REF$]=0
set nt[$QUEUE_REF$]=to
set $THIS_REF$[$ID_REF$]=$QUEUE_REF$
set qt[$QUEUE_REF$]=$ID_REF$
set tl[$QUEUE_REF$]=$THIS_REF$
endif
//! endtextmacro
//! textmacro TIMER_TOOLS_PREPARE_TIMEOUT takes REF_TIMEOUT, ID_REF
set $REF_TIMEOUT$=RELATIVE_ACCURACY/R2I(RELATIVE_ACCURACY/$REF_TIMEOUT$+.5)
set $ID_REF$=R2I($REF_TIMEOUT$*CONSTANT_ACCURACY+.5)
if (0==$ID_REF$) then
set $ID_REF$=1
endif
set $REF_TIMEOUT$=$ID_REF$/CONSTANT_ACCURACY
//! endtextmacro
//! textmacro TIMER_TOOLS_PREPARE_TICK takes TIMEOUT_REF, TICK_LENIANCY_REF
set $TICK_LENIANCY_REF$=Ln(RELATIVE_MERGE/$TIMEOUT_REF$)/230.258509*$TIMEOUT_REF$+CONSTANT_MERGE
//! endtextmacro
//! textmacro TIMER_TOOLS_CANT_MERGE_RIGHT takes QUEUE_REF, SEGMENT_REF, TICK_LENIANCY_REF, REMAINING_TIME_REF, TIMEOUT_REF
set $SEGMENT_REF$=sp[sf[$QUEUE_REF$]]
set $REMAINING_TIME_REF$=TimerGetRemaining(st[$SEGMENT_REF$])
if (not sr[$SEGMENT_REF$] or 0<$TIMEOUT_REF$-$REMAINING_TIME_REF$-$TICK_LENIANCY_REF$) then
//! endtextmacro
//! textmacro TIMER_TOOLS_CAN_MERGE_LEFT takes QUEUE_REF, SEGMENT_REF, TICK_LENIANCY_REF, REMAINING_TIME_REF, TIMEOUT_REF
set $SEGMENT_REF$=sf[$QUEUE_REF$]
set $REMAINING_TIME_REF$=TimerGetRemaining(st[$SEGMENT_REF$])
if (0!=$SEGMENT_REF$ and 0>=$REMAINING_TIME_REF$-$TICK_LENIANCY_REF$) then
//! endtextmacro
//! textmacro TIMER_TOOLS_ALLOCATE_SEGMENT takes QUEUE_REF, SEGMENT_REF, TIMEOUT_REF, CODE_REF, SEGMENT_FIRST
if (0==tn[0]) then
set $SEGMENT_REF$=ic+1
set ic=$SEGMENT_REF$
else
set $SEGMENT_REF$=tn[0]
set tn[0]=tn[$SEGMENT_REF$]
endif
set $SEGMENT_FIRST$=sf[$QUEUE_REF$]
if (0==$SEGMENT_FIRST$) then
set sn[$SEGMENT_REF$]=$SEGMENT_REF$
set sp[$SEGMENT_REF$]=$SEGMENT_REF$
set sf[$QUEUE_REF$]=$SEGMENT_REF$
else
set sp[$SEGMENT_REF$]=sp[$SEGMENT_FIRST$]
set sn[$SEGMENT_REF$]=$SEGMENT_FIRST$
set sn[sp[$SEGMENT_FIRST$]]=$SEGMENT_REF$
set sp[$SEGMENT_FIRST$]=$SEGMENT_REF$
endif
set tf[$SEGMENT_REF$]=0
set st[$SEGMENT_REF$]=CreateTimer()
set nt[$SEGMENT_REF$]=$TIMEOUT_REF$
call TimerStart(st[$SEGMENT_REF$],$TIMEOUT_REF$,true,$CODE_REF$)
set sr[$SEGMENT_REF$]=true
set an[$SEGMENT_REF$]=$SEGMENT_REF$
set ap[$SEGMENT_REF$]=$SEGMENT_REF$
//! endtextmacro
//! textmacro TIMER_TOOLS_ATTACH_SEGMENT takes QUEUE_REF, SEGMENT_REF
set nh[$SEGMENT_REF$]=$QUEUE_REF$
set qt[$SEGMENT_REF$]=-GetHandleId(st[$SEGMENT_REF$])
call SaveInteger(tt,qt[$SEGMENT_REF$],0,$QUEUE_REF$)
//! endtextmacro
//! textmacro TIMER_TOOLS_PUSH_AFTER takes SEGMENT_REF, SEGMENT_REF_TARGET, TIMER_REF
if ($SEGMENT_REF_TARGET$!=nh[$TIMER_REF$]) then
set an[$TIMER_REF$]=an[$SEGMENT_REF$]
set ap[$TIMER_REF$]=$SEGMENT_REF$
set ap[an[$SEGMENT_REF$]]=$TIMER_REF$
set an[$SEGMENT_REF$]=$TIMER_REF$
set as[$TIMER_REF$]=$SEGMENT_REF_TARGET$
endif
//! endtextmacro
//! textmacro TIMER_TOOLS_POP_AFTER takes SEGMENT_REF, TIMER_REF
set an[ap[$TIMER_REF$]]=an[$TIMER_REF$]
set ap[an[$TIMER_REF$]]=ap[$TIMER_REF$]
set $SEGMENT_REF$=as[$TIMER_REF$]
set as[$TIMER_REF$]=0
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_AFTER takes TIMER_REF
if (0!=as[$TIMER_REF$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT takes SEGMENT_REF, TIMER_REF
set tn[$TIMER_REF$]=tf[$SEGMENT_REF$]
set tp[tf[$SEGMENT_REF$]]=$TIMER_REF$
set tp[$TIMER_REF$]=0
set tf[$SEGMENT_REF$]=$TIMER_REF$
set nh[$TIMER_REF$]=$SEGMENT_REF$
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_ALLOCATED takes TIMER_REF, ERROR_MESSAGE
if (0==nt[$TIMER_REF$]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$ERROR_MESSAGE$")
else
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_NOT_PAUSED takes SEGMENT_REF
if (0!=$SEGMENT_REF$) then
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_PAUSED takes SEGMENT_REF
if (0==$SEGMENT_REF$) then
//! endtextmacro
//! textmacro TIMER_TOOLS_DEL takes THIS
set tn[$THIS$]=tn[0]
set tn[0]=$THIS$
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_SEGMENT_RUNNING takes SEGMENT_REF, TIMER_REF
if (sr[$SEGMENT_REF$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT takes SEGMENT_REF, TIMER_REF
if (0==tp[$TIMER_REF$]) then
set tf[$SEGMENT_REF$]=tn[$TIMER_REF$]
set tp[tn[$TIMER_REF$]]=0
else
set tp[tn[$TIMER_REF$]]=tp[$TIMER_REF$]
set tn[tp[$TIMER_REF$]]=tn[$TIMER_REF$]
endif
set nh[$TIMER_REF$]=0
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_SEGMENT_EMPTY takes SEGMENT_REF
if (sr[$SEGMENT_REF$] and 0==tf[$SEGMENT_REF$] and $SEGMENT_REF$==an[$SEGMENT_REF$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_DEL_SEGMENT takes QUEUE_REF, SEGMENT_REF
if ($SEGMENT_REF$==sf[$QUEUE_REF$]) then
if ($SEGMENT_REF$==sn[$SEGMENT_REF$]) then
set sf[$QUEUE_REF$]=0
else
set sf[$QUEUE_REF$]=sn[$SEGMENT_REF$]
set sn[sp[$SEGMENT_REF$]]=sn[$SEGMENT_REF$]
set sp[sn[$SEGMENT_REF$]]=sp[$SEGMENT_REF$]
endif
else
set sn[sp[$SEGMENT_REF$]]=sn[$SEGMENT_REF$]
set sp[sn[$SEGMENT_REF$]]=sp[$SEGMENT_REF$]
endif
set tn[$SEGMENT_REF$]=tn[0]
set tn[0]=$SEGMENT_REF$
call PauseTimer(st[$SEGMENT_REF$])
call DestroyTimer(st[$SEGMENT_REF$])
set st[$SEGMENT_REF$]=null
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_QUEUE_EMPTY takes QUEUE_REF
if (0==sf[$QUEUE_REF$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_UNATTACH takes THIS, REF
call $THIS$.remove(qt[$REF$])
//! endtextmacro
//! textmacro TIMER_TOOLS_UNATTACH2 takes REF
call RemoveSavedInteger(tt,qt[$REF$],0)
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_NEW_TIMEOUT takes TIMEOUT_REF, NEW_TIMEOUT
if ($NEW_TIMEOUT$!=$TIMEOUT_REF$ and $NEW_TIMEOUT$>0) then
//! endtextmacro
//! textmacro TIMER_TOOLS_GET_NODE_HEAD takes HEAD_REF, NODE_REF
set $HEAD_REF$=nh[$NODE_REF$]
//! endtextmacro
//! textmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG takes SEGMENT, FLAG
set sr[$SEGMENT$]=$FLAG$
//! endtextmacro
//! textmacro TIMER_TOOLS_PREPARE_TIMER takes QUEUE, SEGMENT
set sf[$QUEUE$]=sn[$SEGMENT$]
//! endtextmacro
//! textmacro TIMER_TOOLS_RUN_AFTER_QUEUE takes SEGMENT2, TIMER, TIMER2
set $TIMER$=an[$SEGMENT2$]
set an[$SEGMENT2$]=$SEGMENT2$
loop
set $TIMER2$=an[$TIMER$]
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_DESTROYING takes TIMER
if (0==nt[$TIMER$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_PAUSING takes TIMER
if (0==as[$TIMER$]) then
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_ADDING takes SEGMENT2, TIMER
elseif ($SEGMENT2$==as[$TIMER$]) then
set as[$TIMER$]=0
//! endtextmacro
//! textmacro TIMER_TOOLS_IS_MOVING takes TIMER
else
set as[$TIMER$]=0
//! endtextmacro
//! textmacro TIMER_TOOLS_GET_NEXT_AFTER takes SEGMENT, SEGMENT2, TIMER
endif
set $TIMER$=$TIMER$2
exitwhen $SEGMENT2$==$TIMER$
endloop
//! endtextmacro
private function CQ takes integer f, real to, code c returns integer
local integer t
local real l=Ln(RELATIVE_MERGE/to)/230.258509*to+CONSTANT_MERGE
local integer s=sp[f]
local real x
if (0==tn[0]) then
set t=ic+1
set ic=t
else
set t=tn[0]
set tn[0]=tn[t]
set nh[t]=0
endif
set nt[t]=to
set x=TimerGetRemaining(st<s>)
if (not sr<s> or 0<to-x-l) then
set x=TimerGetRemaining(st[f])
if (0!=f and 0>=x-l) then
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("f","f","t")
else
if (0==tn[0]) then
set s=ic+1
set ic=s
else
set s=tn[0]
set tn[0]=tn<s>
endif
if (0==f) then
set sn<s>=s
set sp<s>=s
else
set sp<s>=sp[f]
set sn<s>=f
set sn[sp[f]]=s
set sp[f]=s
endif
set tn[t]=0
set tp[t]=0
set tf<s>=t
set nh[t]=s
set st<s>=CreateTimer()
call TimerStart(st<s>,to,true,c)
set sr<s>=true
set an<s>=s
set ap<s>=s
endif
else
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
endif
return t
endfunction
private function DT takes integer t, integer f returns integer
local integer s
//! runtextmacro TIMER_TOOLS_IS_ALLOCATED("t","TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER")
set nt[t]=0
//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
if (s==f) then
if (s==sn<s>) then
set f=0
else
set f=sn<s>
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
else
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
set tn<s>=tn[0]
set tn[0]=s
call PauseTimer(st<s>)
call DestroyTimer(st<s>)
set st<s>=null
endif
endif
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("s","t")
//! runtextmacro TIMER_TOOLS_IS_PAUSED("s")
//! runtextmacro TIMER_TOOLS_DEL("t")
else
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_DEL("t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
if (s==f) then
if (s==sn<s>) then
set f=0
else
set f=sn<s>
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
else
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
set tn<s>=tn[0]
set tn[0]=s
call PauseTimer(st<s>)
call DestroyTimer(st<s>)
set st<s>=null
endif
else
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","0","t")
endif
endif
endif
return f
endfunction
private keyword f
private keyword c
private keyword e
private keyword s
module CTQ
static integer f=0
static code c
static method create takes nothing returns thistype
local integer t=CQ(f,TIMEOUT,c)
if (0==f) then
set f=nh[t]
endif
return t
endmethod
method destroy takes nothing returns nothing
set f=DT(this,f)
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(st[nh[this]])
endmethod
method operator remaining takes nothing returns real
return TimerGetRemaining(st[nh[this]])
endmethod
static method e takes nothing returns nothing
local integer s=f
local thistype this=tf<s>
endmodule
module CTQExpire
set sr<s>=false
set f=sn<s>
loop
exitwhen 0==this
endmodule
module CTQNull
set this=tn[this]
endloop
endmodule
module CTQEnd
set this=an<s>
set an<s>=s
loop
exitwhen s==this
//! runtextmacro TIMER_TOOLS_IS_PAUSING("this")
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","this")
//! runtextmacro TIMER_TOOLS_IS_DESTROYING("this")
//! runtextmacro TIMER_TOOLS_DEL("this")
endif
else
set as[this]=0
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","this")
endif
set this=an[this]
endloop
if (0==tf<s>) then
if (s==f) then
if (s==sn<s>) then
set f=0
else
set f=sn<s>
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
else
set sn[sp<s>]=sn<s>
set sp[sn<s>]=sp<s>
endif
set tn<s>=tn[0]
set tn[0]=s
call PauseTimer(st<s>)
call DestroyTimer(st<s>)
set st<s>=null
else
set sr<s>=true
endif
endmethod
private static method onInit takes nothing returns nothing
set c=function thistype.e
endmethod
endmodule
private function LCQ takes integer f, real to, code c returns integer
local integer t
if (0==tn[0]) then
set t=ic+1
set ic=t
else
set t=tn[0]
set tn[0]=tn[t]
endif
if (0==f) then
set f=t
set tn[t]=0
set tp[t]=t
else
set tn[t]=0
set tp[t]=tp[f]
set tn[tp[f]]=t
set tp[f]=t
endif
set st[t]=CreateTimer()
call TimerStart(st[t],to,false,c)
return t
endfunction
module CLTQ1
static integer f=0
static code c
static method create takes nothing returns thistype
local integer t=LCQ(f,TIMEOUT,c)
if (0==f) then
set f=t
endif
return t
endmethod
method destroy takes nothing returns nothing
set tn[tp[this]]=tn[this]
set tp[tn[this]]=tp[this]
if (this==f) then
set f=tn[this]
endif
set tn[this]=tn[0]
set tn[0]=this
call PauseTimer(st[this])
call DestroyTimer(st[this])
set st[this]=null
endmethod
static method e takes nothing returns nothing
local thistype this=f
endmodule
module CLTQ1End
set f=tn[this]
set tn[tp[this]]=tn[this]
set tp[tn[this]]=tp[this]
set tn[this]=tn[0]
set tn[0]=this
call DestroyTimer(st[this])
set st[this]=null
endmethod
private static method onInit takes nothing returns nothing
set c=function thistype.e
endmethod
endmodule
private struct Mt extends array
private method operator [] takes integer i returns thistype
return Table(this)<i>
endmethod
private method operator []= takes integer i, integer v returns nothing
set Table(this)<i>=v
endmethod
private method remove takes integer i returns nothing
call Table(this).remove(i)
endmethod
method operator timeout takes nothing returns real
return nt[this]
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(st[nh[this]])
endmethod
method operator remaining takes nothing returns real
return TimerGetRemaining(st[nh[this]])
endmethod
method setTimeout takes real to, code c returns nothing
local integer t=this
local integer s
local integer h
local integer i
local real x
local real l
local boolean b=true
set this=tl[t]
//! runtextmacro TIMER_TOOLS_IS_ALLOCATED("t","TIMER ERROR: ATTEMPTED TO MANIPULATE NULL TIMER")
//! runtextmacro TIMER_TOOLS_PREPARE_TIMEOUT("to","i")
//! runtextmacro TIMER_TOOLS_IS_NEW_TIMEOUT("nt[t]","to")
set nt[t]=to
//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
//! runtextmacro TIMER_TOOLS_DEL("h")
//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
endif
endif
endif
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("s","t")
//! runtextmacro TIMER_TOOLS_IS_NOT_PAUSED("s")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
//! runtextmacro TIMER_TOOLS_DEL("h")
//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
endif
endif
else
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","-1","t")
set b=false
endif
endif
if (b) then
//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
else
//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
endif
else
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
endif
endif
endif
endif
endmethod
method new takes real to, code c returns thistype
local integer t
local integer i
local integer h
local integer s
local real x
local real l
//! runtextmacro TIMER_TOOLS_PREPARE_TIMEOUT("to","i")
//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
//! runtextmacro TIMER_TOOLS_ALLOCATE_TIMER("t","to","this")
//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
else
//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
endif
else
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
endif
return t
endmethod
method destroy takes nothing returns nothing
local integer t=this
local integer s
local integer h
set this=tl[t]
//! runtextmacro TIMER_TOOLS_IS_ALLOCATED("t","TIMER ERROR: ATTEMPTED TO DESTROY NULL TIMER")
set nt[t]=0
//! runtextmacro TIMER_TOOLS_IS_AFTER("t")
//! runtextmacro TIMER_TOOLS_POP_AFTER("s","t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
//! runtextmacro TIMER_TOOLS_DEL("h")
//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
endif
endif
endif
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("s","t")
//! runtextmacro TIMER_TOOLS_IS_PAUSED("s")
//! runtextmacro TIMER_TOOLS_DEL("t")
else
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_RUNNING("s","t")
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_DEL("t")
//! runtextmacro TIMER_TOOLS_IS_SEGMENT_EMPTY("s")
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("h","s")
//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("h","s")
//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("h")
//! runtextmacro TIMER_TOOLS_DEL("h")
//! runtextmacro TIMER_TOOLS_UNATTACH("this","h")
endif
endif
else
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","0","t")
endif
endif
endif
endmethod
endstruct
private function MTM takes integer h, integer s, integer t, code c returns nothing
local integer s2=s
local integer t2
local Table this=tl[t]
local real l
local real x
local real to
local integer i
//! runtextmacro TIMER_TOOLS_RUN_AFTER_QUEUE("s2","t","t2")
//! runtextmacro TIMER_TOOLS_IS_PAUSING("t")
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s2","t")
//! runtextmacro TIMER_TOOLS_IS_DESTROYING("t")
//! runtextmacro TIMER_TOOLS_DEL("t")
endif
//! runtextmacro TIMER_TOOLS_IS_ADDING("s2","t")
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s2","t")
//! runtextmacro TIMER_TOOLS_IS_MOVING("t")
set to=nt[t]
set i=R2I(to/CONSTANT_ACCURACY+.5)
//! runtextmacro TIMER_TOOLS_REMOVE_FROM_SEGMENT("s2","t")
//! runtextmacro TIMER_TOOLS_PREPARE_TICK("to","l")
//! runtextmacro TIMER_TOOLS_GET_TIMER_QUEUE("this","h","i")
//! runtextmacro TIMER_TOOLS_CANT_MERGE_RIGHT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_CAN_MERGE_LEFT("h","s","l","x","to")
//! runtextmacro TIMER_TOOLS_PUSH_AFTER("s","s","t")
else
//! runtextmacro TIMER_TOOLS_ALLOCATE_SEGMENT("h","s","to","c","i")
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
//! runtextmacro TIMER_TOOLS_ATTACH_SEGMENT("h","s")
endif
else
//! runtextmacro TIMER_TOOLS_ADD_TIMER_TO_SEGMENT("s","t")
endif
//! runtextmacro TIMER_TOOLS_GET_NEXT_AFTER("s","s2","t")
endfunction
module CTM
static Mt f
static code c
static method create takes real to returns thistype
return f.new(to,c)
endmethod
method destroy takes nothing returns nothing
call Mt(this).destroy()
endmethod
method operator timeout takes nothing returns real
return Mt(this).timeout
endmethod
method operator timeout= takes real to returns nothing
call Mt(this).setTimeout(to,c)
endmethod
static method e takes nothing returns nothing
local integer s=LoadInteger(tt,-GetHandleId(GetExpiredTimer()),0)
local thistype this=sf<s>
endmodule
module CTMExpire
//! runtextmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG("this","false")
//! runtextmacro TIMER_TOOLS_PREPARE_TIMER("s","this")
set s=this
set this=tf[this]
loop
exitwhen 0==this
endmodule
module CTMNull
set this=tn[this]
endloop
endmodule
module CTMEnd
if (s!=an<s>) then
call MTM(nh<s>,s,tf<s>,function thistype.e)
endif
if (0==tf<s>) then
//! runtextmacro TIMER_TOOLS_GET_NODE_HEAD("this","s")
//! runtextmacro TIMER_TOOLS_DEL_SEGMENT("this","s")
//! runtextmacro TIMER_TOOLS_UNATTACH2("s")
//! runtextmacro TIMER_TOOLS_IS_QUEUE_EMPTY("this")
//! runtextmacro TIMER_TOOLS_DEL("this")
//! runtextmacro TIMER_TOOLS_UNATTACH("tl[this]","this")
endif
else
//! runtextmacro TIMER_TOOLS_SET_TIMER_RUNNING_FLAG("s","true")
endif
endmethod
private static method onInit takes nothing returns nothing
set f=Table.create()
set c=function thistype.e
endmethod
endmodule
endlibrary
</s></s></s></s></s></i></i></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>
Use all of the below for data attachment purposes only
Constant Light Timer Queue 1 Demo
Use when dealing with constant timeout 1 shot timers with constant methods of timeouts that are 2+. Usually used for expiring effects. Essentially use this when there is a low chance for merges between timers (perhaps highly infrequent timers).
JASS:
struct MyLightTimerQueue1 extends array
private static constant real TIMEOUT=2
readonly integer value
implement CLTQ1
local string s="My Value: "
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
implement CLTQ1End
private static method onInit takes nothing returns nothing
set create().value=1
endmethod
endstruct
Constant Timer Queue Demo
Use when dealing with constant repeating timeouts.
JASS:
struct MyTimerQueue extends array
private static constant real TIMEOUT=2
readonly integer value
implement CTQ
local string s="My Value: "
implement CTQExpire
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
implement CTQNull
set s=null //pointless, but shows how to use null block
implement CTQEnd
private static method onInit takes nothing returns nothing
set create().value=1
endmethod
endstruct
Constant Timer Method Demo
Use when dealing with repeating timers of an unknown timeout with a constant method
JASS:
struct MyTimerMethod extends array
readonly integer value
implement CTM
local string s="My Value: "
implement CTMExpire
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,s+I2S(value))
implement CTMNull
set s=null //pointless, but shows how to use null block
implement CTMEnd
private static method onInit takes nothing returns nothing
set create(2).value=1
endmethod
endstruct
In cases where the method is completely unknown, use regular timer data attachment. 1 Shot timers can run through Constant Timer Queues/Consant Timer Methods if there are many one shot timers of the same timeouts, otherwise the overhead on creation/destruction isn't worth it.
Light Timer Queue 1 will always be worth it for 1 shot timers when Constant Timer Queue isn't (perhaps infrequent timers, high period, essentially low chance of merge).