- Joined
- Jul 10, 2007
- Messages
- 6,306
ln=Ln(10)*100
ln=230.2585092994
//! textmacro a
//! endtextmacro
library Tt /* v2.0.0.0
*************************************************************************************
*
* Timer tools timers use a special merging algorithm. When two timers merge, they
* both expire on the same timer. 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.
*
************************************************************************************
*
* 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=.03//.3
endglobals
/*
************************************************************************************
*
* Functions
*
* function GetExpired takes nothing returns integer
* Gets the expiring timer. This is not the expiring timer method instance! This is read
* inside of GetTimerFirstInstance, which is why timer instances can only be retrieved inside of
* an expiring timer method.
*
* Boolean expressions on the same timer will expire for that same timer. Use timer methods instead.
*
* function TimerMethodAddInstance takes real timeout, integer timerMethod returns integer
* Adds a new instance to a timer method. These must be looped through inside of the boolean expression method.
* Returns a timer instance.
* function TimerMethodRemoveInstance takes integer timerInstance returns nothing
* Removes an instance from a timer method. Timer method is destroyed after it is done expiring when no instances
* are left.
* function CreateTimerMethod takes boolexpr method returns integer
* Creates a new timer method from a boolean expression. This boolean expression is run whenever the timer
* expires.
*
* function TimerGetElapsedEx takes integer timerInstance returns real
* function TimerGetRemainingEx takes integer timerInstance returns real
* function TimerGetTimeoutEx takes integer timerInstance returns real
*
* function GetTimerFirstInstance takes integer timerMethod returns integer
* Gets first timer instance in a timer method. Can only be called within an expiring timer method.
* function GetTimerNext takes integer timerInstance returns integer
* Gets next timer instance given a timer instance.
* Sentinel is 0.
*
************************************************************************************
*
* struct TimerList extends array
*
* method register takes boolexpr b returns integer
* Registers a boolean expression to timer
* method unregister takes integer i returns nothing
* Unregisters boolean expression from timer
* Timer is automatically destroyed if no boolean expressions are left
*
************************************************************************************
*
* struct Timer extends array
* static method operator [] takes real timeout returns thistype
* Converts a timeout to a timer group
* method operator list takes nothing returns TimerList
* Creates a new timer in the timer group if there are no viable currently
* existing timers. Will return an existing timer if that timer either expires soon enough
* or has enough time left to be close enough to the timeout.
*
************************************************************************************
*
* Modules
*
* module CTM (optional)
* locals
* module CTMExpire (not optional)
* expiration code
* module CTMNull (optional)
* null locals
* module CTMEnd (not optional)
* static method create takes real timeout returns thistype
* method destroy takes nothing returns nothing
* method operator elapsed takes nothing returns real
* method operator remaining takes nothing returns real
* method operator timeout takes nothing returns real
*
* module CLTQ
* expiration code
* module CLTQEnd
* static method create takes nothing returns thistype
* method destroy takes nothing returns nothing
* method operator elapsed takes nothing returns real
* method operator remaining takes nothing returns real
*
* Interface:
* private static constant real timeout
*
************************************************************************************/
globals
//list
private integer array lf
private integer array ln
private integer array lp
private integer array lr
private integer lc = 0
private timer array tm
//nodes
private trigger array nt
private integer array nc
private integer array nh
private boolean array aa
private boolean array nr
//trigger conditions
private integer ntcc = 0
private integer array ntcr
private triggercondition array ntc
//trigger condition add after
private integer array paf
private integer array pan
private integer array pap
private boolexpr array pab
//trigger condition destroy after
private integer array ds
private integer dsc = 0
//expiring timer
private integer exp = 0
//module
private integer mtfr = 0
private boolexpr array mb
private integer mnf = 0
private integer mnft = 0
private integer mnp = 0
//list
private hashtable mlf = InitHashtable()
private hashtable mlf2 = InitHashtable()
private hashtable mnt = InitHashtable()
private hashtable mnc = InitHashtable()
private integer array mln
private integer array mlp
//true list
private integer array mlnd
private integer array mlpd
//add after
private integer array mlnp
private integer array mlpp
private boolean array mlap
private integer array mlfp
private integer array mlr
private integer mlc = 0
//nodes
private integer array mnh
private integer array mno
//destroy after
private integer array mds
private integer mdsc = 0
private boolean array mdb
private boolean array al
private boolean array alm
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
private function AL takes nothing returns integer
local integer i = lr[0]
if (0 == i) then
set lc = lc + 1
set al[lc] = true
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Allocated Node: "+I2S(lc))
return lc
endif
set lr[0] = lr[i]
set al[i] = true
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Allocated Node: "+I2S(i))
return i
endfunction
function GetExpired takes nothing returns integer
return exp
endfunction
globals
private boolean en = true
endglobals
private function DIS takes nothing returns nothing
set en = false
loop
exitwhen 0 == lc
call PauseTimer(tm[lc])
set lc = lc - 1
endloop
endfunction
private function DELM takes integer i returns nothing
if (en) then
if (alm[i]) then
set mlr[i] = mlr[0]
set mlr[0] = i
set alm[i] = false
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DOUBLE FREE: "+I2S(i))
call DIS()
endif
endif
endfunction
//! textmacro TIMER_TOOLS_ADD_NODE takes D
if (en) then
if (alm[i]) then
set mln$D$[i] = 0
if (0 == mlp$D$[t]) then
set mlp$D$[i] = t
set mln$D$[t] = i
else
set mlp$D$[i] = mlp$D$[t]
set mln$D$[mlp$D$[i]] = i
endif
set mlp$D$[t] = i
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ADD: "+I2S(mlp$D$[t])+"->"+I2S(t)+"->"+I2S(mln$D$[t])+" on "+I2S(mnh[i]))
set i = mlp$D$[t]
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ADD 1: "+I2S(mlp$D$[i])+"->"+I2S(i)+"->"+I2S(mln$D$[i])+" on "+I2S(mnh[i]))
set i = mln$D$[t]
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ADD 2: "+I2S(mlp$D$[i])+"->"+I2S(i)+"->"+I2S(mln$D$[i])+" on "+I2S(mnh[i]))
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ATTEMPTED TO ADD NULL NODE: "+I2S(i)+" on "+I2S(mnh[i]))
call DIS()
endif
endif
//! endtextmacro
private function ADDM takes integer i, integer t returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"EXP LIST")
//! runtextmacro TIMER_TOOLS_ADD_NODE("")
endfunction
private function ADDMD takes integer i, integer t returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"TRUE LIST")
//! runtextmacro TIMER_TOOLS_ADD_NODE("d")
endfunction
private function ADDMP takes integer i, integer t returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ADD AFTER LIST")
//! runtextmacro TIMER_TOOLS_ADD_NODE("p")
endfunction
//! textmacro TIMER_TOOLS_REMOVE_NODE takes D
if (en) then
if (alm[i]) then
if (0 != f) then
if (0 == mln$D$[i]) then
set mlp$D$[f] = mlp$D$[i]
else
set mlp$D$[mln$D$[i]] = mlp$D$[i]
endif
set mln$D$[mlp$D$[i]] = mln$D$[i]
if (f == mln$D$[f]) then
set mln$D$[f] = 0
set mlp$D$[f] = 0
else
set mln$D$[mlp$D$[f]] = 0
endif
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"REM: "+I2S(mlp$D$[f])+"->"+I2S(f)+"->"+I2S(mln$D$[f])+" on "+I2S(mnh[i]))
set i = mlp$D$[f]
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"REM 1: "+I2S(mlp$D$[i])+"->"+I2S(i)+"->"+I2S(mln$D$[i])+" on "+I2S(mnh[i]))
set i = mln$D$[f]
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"REM 2: "+I2S(mlp$D$[i])+"->"+I2S(i)+"->"+I2S(mln$D$[i])+" on "+I2S(mnh[i]))
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"ATTEMPTED TO REMOVE NULL NODE: "+I2S(i)+" on "+I2S(mnh[i]))
call DIS()
endif
endif
//! endtextmacro
private function REMM takes integer i, integer f returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"EXP LIST")
//! runtextmacro TIMER_TOOLS_REMOVE_NODE("")
endfunction
private function REMMD takes integer i, integer f returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"TRUE LIST")
//! runtextmacro TIMER_TOOLS_REMOVE_NODE("d")
endfunction
private function REMMP takes integer i, integer f returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"REMOVE AFTER LIST")
//! runtextmacro TIMER_TOOLS_REMOVE_NODE("p")
endfunction
private function DAF takes integer i returns nothing
if (en) then
if (not mdb[i]) then
set mdb[i] = true
set mds[mdsc] = i
set mdsc = mdsc + 1
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Marked to Deallocate After: "+I2S(i))
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DOUBLE FREE ATTEMPT: "+I2S(i))
call DIS()
endif
endif
endfunction
private function EXP takes nothing returns nothing
local integer i = R2I(TimerGetTimeout(GetExpiredTimer())*100)
local integer n = lf[i]
local integer t
local integer f
set lf[i] = ln[n]
if (not al[n]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG NODE: "+I2S(n))
call DIS()
return
endif
set nr[n] = true
set exp = n
call TriggerEvaluate(nt[n])
set exp = 0
set nr[n] = false
set i = paf[n]
set paf[n] = 0
loop
exitwhen 0 == i
set ntc[i] = TriggerAddCondition(nt[n], pab[i])
set pab[i] = null
set ntcr[i] = ntcr[0]
set ntcr[0] = i
set i = pan[i]
endloop
set i = mlfp[n]
set mlfp[n] = 0
loop
exitwhen 0 == i
set t = LoadInteger(mlf, mno[i], mnh[i])
call ADDM(i, t)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Added After: "+I2S(i))
set i = mlnp[i]
endloop
set i = dsc
loop
exitwhen 0 == i
set i = i - 1
call TriggerRemoveCondition(nt[n], ntc[ds[i]])
set ntc[ds[i]] = null
set nc[n] = nc[n] - 1
endloop
set dsc = 0
set t = mdsc
loop
exitwhen 0 == t
set t = t - 1
set i = mds[t]
set mdb[i] = false
set f = LoadInteger(mlf, mno[i], mnh[i])
if (f == i) then
set f = LoadInteger(mlf2, mno[i], mnh[i])
if (f == i) then
set f = 0
endif
call SaveInteger(mlf, mno[i], mnh[i], f)
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"First: "+I2S(i)+" -> "+I2S(f))
call REMM(i, f)
call DELM(i)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated After: "+I2S(i)+" on "+I2S(n))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated After Counter: "+I2S(t))
set mlap[i] = false
endloop
set mdsc = 0
if (0 == nc[n]) then
call PauseTimer(tm[n])
call DestroyTimer(tm[n])
set tm[n] = null
if (lf[nh[n]] == n) then
set lf[nh[n]] = ln[n]
endif
set ln[lp[n]] = ln[n]
set lp[ln[n]] = lp[n]
if (ln[n] == n) then
set lf[nh[n]] = 0
endif
call DestroyTrigger(nt[n])
set nt[n] = null
set lr[n] = lr[0]
set lr[0] = n
set al[n] = false
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated Node After: "+I2S(n))
endif
endfunction
struct TimerList extends array
method register takes boolexpr b returns integer
local integer i = ntcr[0]
if (0 == i) then
set i = ntcc + 1
set ntcc = i
else
set ntcr[0] = ntcr[i]
endif
set nc[this] = nc[this] + 1
if (aa[this]) then
if (0 == paf[this]) then
set paf[this] = i
set pan[i] = 0
set pap[i] = i
else
set pan[i] = 0
set pap[i] = pap[paf[this]]
set pan[pap[i]] = i
set pap[paf[this]] = i
endif
set pab[i] = b
else
set ntc[i] = TriggerAddCondition(nt[this], b)
endif
return i
endmethod
method unregister takes integer i returns nothing
if (not nr[this]) then
set nc[this] = nc[this] - 1
if (0 == nc[this]) then
call PauseTimer(tm[this])
call DestroyTimer(tm[this])
set tm[this] = null
if (lf[nh[this]] == this) then
set lf[nh[this]] = ln[this]
endif
set ln[lp[this]] = ln[this]
set lp[ln[this]] = lp[this]
if (ln[this] == this) then
set lf[nh[this]] = 0
endif
call DestroyTrigger(nt[this])
set nt[this] = null
set lr[this] = lr[0]
set lr[0] = this
set al[this] = false
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated Node: "+I2S(this))
endif
endif
if (null == pab[i]) then
if (nr[this]) then
set ds[dsc] = i
set dsc = dsc + 1
else
call TriggerRemoveCondition(nt[this], ntc[i])
set ntc[i] = null
endif
else
if (paf[this] == i) then
set paf[this] = pan[i]
endif
if (0 == pan[i]) then
set pap[paf[this]] = pap[i]
else
set pap[pan[i]] = pap[i]
endif
if (0 == pap[i]) then
set pan[paf[this]] = pan[i]
else
set pan[pap[i]] = pan[i]
endif
set pab[i] = null
set ntcr[i] = ntcr[0]
set ntcr[0] = i
endif
endmethod
endstruct
struct Timer extends array
static method operator [] takes real timeout returns thistype
local integer i = R2I(timeout*100)
if (0 == i) then
return 1
elseif (8191 < i) then
return 8191
endif
return i
endmethod
method operator list takes nothing returns TimerList
local integer t = lf[this]
local integer l
local real tl
local real a
local integer n
local real rem
if (0 == t) then
set t = AL()
set tm[t] = CreateTimer()
call TimerStart(tm[t], this/100., true, function EXP)
set lf[this] = t
set lp[t] = t
set ln[t] = t
set nt[t] = CreateTrigger()
set nh[t] = this
set aa[t] = false
return t
endif
set l = lp[t]
set a = this/100.
set tl = Ln(RELATIVE_MERGE/a)/230.258509*a+CONSTANT_MERGE
set rem = TimerGetRemaining(tm[l])
if (TimerGetElapsed(tm[t]) < tl) then
set aa[t] = false
return t
elseif (rem < tl) then
set aa[l] = true
return l
elseif (TimerGetTimeout(tm[t]) - rem < tl) then
set aa[l] = false
return l
endif
set n = AL()
set tm[n] = CreateTimer()
if (en) then
call TimerStart(tm[n], a, true, function EXP)
endif
set ln[l] = n
set lp[t] = n
set ln[n] = t
set lp[n] = l
set nt[n] = CreateTrigger()
set nh[n] = this
set aa[n] = false
return n
endmethod
endstruct
private function ALM takes nothing returns integer
local integer i = mlr[0]
if (alm[i]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DOUBLE ALLOCATE: "+I2S(i))
call DIS()
endif
if (0 == i) then
set mlc = mlc + 1
set alm[mlc] = true
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Allocated: "+I2S(mlc))
return mlc
endif
set mlr[0] = mlr[i]
set alm[i] = true
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Allocated: "+I2S(i))
return i
endfunction
private function RegisterMethod takes integer this, integer t, integer i, boolean after returns nothing
call ADDMD(i, t)
if (not after) then
call ADDM(i, t)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Adding: "+I2S(i))
else
set t = mlfp[this]
if (0 == t) then
set mlfp[this] = i
set mlnp[i] = 0
set mlpp[i] = 0
else
call ADDMP(i, t)
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Marked To Add After: "+I2S(i))
endif
endfunction
function TimerMethodAddInstance takes real a, integer o returns integer
local Timer i = Timer[a]
local TimerList this = i.list
local integer t = LoadInteger(mlf2, o, this)
local integer n
call SaveInteger(mnc, o, this, LoadInteger(mnc, o, this) + 1)
set mnp = this
if (0 == t) then
set t = ALM()
call SaveInteger(mlf, o, this, t)
call SaveInteger(mlf2, o, this, t)
call SaveInteger(mnt, o, this, this.register(mb[o]))
set mlp[t] = 0
set mln[t] = 0
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,I2S(mlp[t])+"->"+I2S(t)+"->"+I2S(mln[t]))
set mlpd[t] = 0
set mlnd[t] = 0
set mnh[t] = this
set mno[t] = o
set mnf = t
set mnft = t
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"First: 0 -> "+I2S(t)+" on "+I2S(this))
return t
endif
set mnf = t
set mnft = t
set n = ALM()
set mnh[n] = this
set mno[n] = o
call RegisterMethod(this, t, n, aa[this])
return n
endfunction
function TimerMethodRemoveInstance takes integer i returns nothing
local TimerList this = mnh[i]
local integer o = mno[i]
local integer f = LoadInteger(mlf2, o, this)
local integer c = LoadInteger(mnc, o, this) - 1
if (not alm[i]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Attempted To Deallocate Null: "+I2S(i))
call DIS()
return
endif
call SaveInteger(mnc, o, this, c)
if (0 == c) then
call this.unregister(LoadInteger(mnt, o, this))
endif
if (f == i) then
set f = mlnd[i]
call SaveInteger(mlf2, o, this, f)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"First Temp: "+I2S(i)+" -> "+I2S(f)+" on "+I2S(this))
endif
set mnft = f
call REMMD(i, f)
if (mlap[i]) then
set mnf = LoadInteger(mlf, o, this)
set f = mlfp[this]
call REMMP(i, f)
set mlap[i] = false
call DELM(i)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated From Marked After: "+I2S(i))
else
if (nr[this]) then
call DAF(i)
set mnf = LoadInteger(mlf, o, this)
else
set f = LoadInteger(mlf, o, this)
if (f == i) then
set f = mln[i]
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"First: "+I2S(i)+" -> "+I2S(f)+" on "+I2S(this))
endif
call REMM(i, f)
call DELM(i)
call SaveInteger(mlf, o, this, f)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Deallocated: "+I2S(i))
set mnf = f
endif
endif
endfunction
function CreateTimerMethod takes boolexpr b returns integer
set mtfr = mtfr + 1
set mb[mtfr] = b
return mtfr
endfunction
function TimerGetElapsedEx takes integer i returns real
return TimerGetElapsed(tm[mnh[i]])
endfunction
function TimerGetRemainingEx takes integer i returns real
return TimerGetRemaining(tm[mnh[i]])
endfunction
function TimerGetTimeoutEx takes integer i returns real
return TimerGetTimeout(tm[mnh[i]])
endfunction
function GetTimerFirstInstance takes integer timerMethod returns integer
return LoadInteger(mlf, timerMethod, exp)
endfunction
function GetTimerNext takes integer timerInstance returns integer
return mln[timerInstance]
endfunction
module CTM
static integer CTMf
static integer array CTMq
static integer array CTMqt
static method create takes real t returns thistype
local integer i
local integer n
if (en) then
set i = TimerMethodAddInstance(t, CTMf)
set CTMq[mnp] = mnf
set CTMqt[mnp] = mnft
if (mnf != LoadInteger(mlf, CTMf, mnh[i])) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"CREATE FIRST DESYNC: "+I2S(mnf)+"!="+I2S(LoadInteger(mlf, CTMf, mnh[i])))
call DIS()
endif
if (mnft != LoadInteger(mlf2, CTMf, mnh[i])) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"CREATE FIRST DESYNC TEMP: "+I2S(mnft)+"!="+I2S(LoadInteger(mlf2, CTMf, mnh[i])))
call DIS()
endif
return i
endif
return 0
endmethod
method destroy takes nothing returns nothing
local integer n
if (en) then
call TimerMethodRemoveInstance(this)
set CTMq[mnh[this]] = mnf
set CTMqt[mnh[this]] = mnft
if (mnf != LoadInteger(mlf, CTMf, mnh[this])) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DESTROY FIRST DESYNC: "+I2S(mnf)+"!="+I2S(LoadInteger(mlf, CTMf, mnh[this])))
call DIS()
endif
if (mnft != LoadInteger(mlf2, CTMf, mnh[this])) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DESTROY FIRST DESYNC TEMP: "+I2S(mnft)+"!="+I2S(LoadInteger(mlf2, CTMf, mnh[this])))
call DIS()
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Destroying: "+I2S(this)+" -> "+I2S(mnf)+","+I2S(mnft))
endif
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsedEx(this)
endmethod
method operator remaining takes nothing returns real
return TimerGetRemainingEx(this)
endmethod
method operator timeout takes nothing returns real
return TimerGetTimeoutEx(this)
endmethod
static method CTMe takes nothing returns boolean
local thistype this = CTMq[exp]
local boolean array hit
endmodule
module CTMExpire
implement CTM
if (not en) then
return false
endif
if (CTMq[exp] != LoadInteger(mlf, CTMf, exp)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG FIRST DESYNC: "+I2S(CTMq[exp])+"!="+I2S(LoadInteger(mlf, CTMf, exp))+" from "+I2S(exp))
call DIS()
return false
endif
if (CTMqt[exp] != LoadInteger(mlf2, CTMf, exp)) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG FIRST DESYNC TEMP: "+I2S(CTMqt[exp])+"!="+I2S(LoadInteger(mlf2, CTMf, exp))+" from "+I2S(exp))
call DIS()
return false
endif
if (0 == this) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"NULL TIMER EXPIRED ON "+I2S(exp))
call DIS()
endif
loop
exitwhen 0 == this
if (hit[this]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG: HIT "+I2S(this)+" MORE THAN ONCE FROM "+I2S(exp))
call DIS()
return false
endif
set hit[this] = true
endmodule
module CTMNull
if (mnh[this] != exp) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG: "+I2S(this)+" "+I2S(mnh[this])+"!="+I2S(exp)+" from "+I2S(exp))
call DIS()
return false
endif
if (not alm[this]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"BUG NOT ALLOCATED: "+I2S(this)+" from "+I2S(exp))
call DIS()
return false
endif
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,I2S(this)+" expired")
set this = mln[this]
endloop
if (CTMq[exp] != CTMqt[exp]) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Temp First Expiration: "+I2S(CTMq[exp])+" -> "+I2S(CTMqt[exp])+" on "+I2S(exp))
endif
set CTMq[exp] = CTMqt[exp]
endmodule
module CTMEnd
implement CTMNull
return false
endmethod
private static method onInit takes nothing returns nothing
set CTMf = CreateTimerMethod(Condition(function thistype.CTMe))
endmethod
endmodule
module CLTQ
static code CTQc
private static integer array n
private static integer array p
static method create takes nothing returns thistype
local integer i = AL()
set tm[i] = CreateTimer()
call TimerStart(tm[i], TIMEOUT, false, CTQc)
set n[i] = 0
set p[i] = p[0]
set n[p[0]] = i
set p[0] = i
return i
endmethod
method destroy takes nothing returns nothing
set n[p[this]] = n[this]
set p[n[this]] = p[this]
set lr[this] = lr[0]
set lr[0] = this
call PauseTimer(tm[this])
call DestroyTimer(tm[this])
set tm[this] = null
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(tm[this])
endmethod
method operator remaining takes nothing returns real
return TimerGetRemaining(tm[this])
endmethod
static method CTMe takes nothing returns nothing
local thistype this = n[0]
set n[0] = n[this]
endmodule
module CLTQEnd
call destroy()
endmethod
private static method onInit takes nothing returns nothing
set CTQc = function thistype.CTMe
endmethod
endmodule
endlibrary
local thistype this = LoadInteger(table, GetHandleId(GetExpiredTimer()), 0)
? nolocal thistype this = LoadInteger(table, someId, 0)
? nolocal thistype this = R2I(TimerGetTimeout(GetExpiredTimer())*100)
no!local thistype this = array[integer]
!if you can reasonably avoid that.
4 hashtables really.
Can't you just play with "-" for the hashtable function argument (Save... / Load...) in order to use the same hashtable all the time, or at least less hashtables ?
Now, that wouldn't be a problem if the hashtable limit was higher than 256, sure by itself you're still far to the limit, but i consider as a real bad practice to use X hashtables for the same resource if you can reasonably avoid that.
As I said, I still need to optimize it. For now, I want it to be working.
I'm going to turn it all into 1 hashtable, don't you worry. There are 4 different combinations with +, - I can use, meaning I can turn 4 hashtables into 1.
I will also be happy to explain the design of Timer Tools... it's a bit wild, heh.
And by unique i mean for "low" periods (under 0.1 s or so), and with as many timers as needed periods (example : 0.1 s , 1/25 s, ...)myself said:"TimerUtils way" or a short unique periodic timer.
local thistype this = n[0]
, meaning that it of course beats TimerUtils for 1 shot timers. The code for CLTQ is extremely short as well static code expiringMethod
private static integer array next
private static integer array prev
static method create takes nothing returns thistype
//simple standard allocation
local integer this = Allocate()
//create timer
set timer[this] = CreateTimer()
call TimerStart(timer[this], TIMEOUT, false, expiringMethod)
//add to end of list
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
return this
endmethod
method destroy takes nothing returns nothing
//remove from list
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//simple standard deallocation
call Deallocate(this)
//destroy timer
call PauseTimer(timer[this])
call DestroyTimer(timer[this])
set timer[this] = null
endmethod
method operator elapsed takes nothing returns real
return TimerGetElapsed(timer[this])
endmethod
method operator remaining takes nothing returns real
return TimerGetRemaining(timer[this])
endmethod
static method expire takes nothing returns nothing
//retrieve this
local thistype this = next[0]
//update first
set next[0] = next[this]
//code here
call destroy()
endmethod
private static method onInit takes nothing returns nothing
set expiringMethod = function thistype.expire
endmethod
if anyone has a slow slow slow slow slow computer
Also, what kind of a name is Tt?
library Tt requires ....
// code
endlibrary
// This is what users will require:
library TimerTools requires Tt
endlibrary
Also, if I were you, I'd rewrite this thing so that it wouldn't look like an abomination
You should recycle the timers that are used in the CTLQ (or even throughout the whole system) module.
library GetUnitCost /* v1.0.1.0
*************************************************************************************
*
* */uses/*
* */ UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/system-unit-indexer-172090/
* */ RegisterPlayerUnitEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
************************************************************************************
*
* Functions
*
* function GetUnitTypeIdGoldCost takes integer unitTypeId returns integer
* function GetUnitTypeIdWoodCost takes integer unitTypeId returns integer
*
* NATIVES WORK WITH NON-HERO UNITS ONLY
*/
native GetUnitGoldCost takes integer unitid returns integer
native GetUnitWoodCost takes integer unitid returns integer
/*
* function GetUnitTotalGoldCost takes unit whichUnit returns integer
* - Gets total unit gold cost including prior upgrades
* function GetUnitTotalWoodCost takes unit whichUnit returns integer
* - Gets total unit wood cost including prior upgrades
*
************************************************************************************/
//! textmacro UNIT_COST
globals
private Table g = 0 //unit type id gold cost table
private Table l = 0 //unit type id lumber cost table
private integer array gu //unit gold cost array (indexed)
private integer array lu //unit lumber cost array (indexed)
private integer array ug //upgrade gold
private integer array ul //upgrade lumber
endglobals
//unit cost (sell)
private function O takes nothing returns boolean
call RemoveUnit(GetSoldUnit())
return false
endfunction
private function LogUnit takes integer id returns nothing
//store previous gold, lumber, and food
local integer k=GetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD)
local integer w=GetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER)
local integer h=GetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_USED)
local integer m=GetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_CAP)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD,1000000)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER,1000000)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_USED,0)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_CAP,100000)
//build unit
set UnitIndexer.enabled=false
call AddUnitToStock(u,id,1,1)
call IssueNeutralImmediateOrderById(p,u,id)
call RemoveUnitFromStock(u,id)
set UnitIndexer.enabled = true
//retrieve gold and lumber cost
set g[id]=1000000-GetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD)
set l[id]=1000000-GetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER)
//set player gold back to what it was
call SetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD,k)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER,w)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_USED,h)
call SetPlayerState(p,PLAYER_STATE_RESOURCE_FOOD_CAP,m)
endfunction
function GetUnitTypeIdGoldCost takes integer id returns integer
debug if (null!=UnitId2String(id)) then
if (not g.has(id)) then
if (IsHeroUnitId(id)) then
call LogUnit(id)
else
set g[id]=GetUnitGoldCost(id)
set l[id]=GetUnitWoodCost(id)
endif
endif
return g[id]
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"COSTS ERROR: INVALID UNIT TYPE ID")
debug endif
debug return 0
endfunction
function GetUnitTypeIdWoodCost takes integer id returns integer
debug if (null!=UnitId2String(id)) then
if (not g.has(id)) then
if (IsHeroUnitId(id)) then
call LogUnit(id)
else
set g[id]=GetUnitGoldCost(id)
set l[id]=GetUnitWoodCost(id)
endif
endif
return l[id]
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"COSTS ERROR: INVALID UNIT TYPE ID")
debug endif
debug return 0
endfunction
function GetUnitTotalGoldCost takes unit t returns integer
debug if (null!=t) then
if (0==GetUnitUserData(t)) then
return GetUnitTypeIdGoldCost(GetUnitTypeId(t))
endif
return gu[GetUnitUserData(t)]
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"COSTS ERROR: INVALID UNIT")
debug endif
debug return 0
endfunction
function GetUnitTotalWoodCost takes unit t returns integer
debug if (null!=t) then
if (0==GetUnitUserData(t)) then
return GetUnitTypeIdWoodCost(GetUnitTypeId(t))
endif
return lu[GetUnitUserData(t)]
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"COSTS ERROR: INVALID UNIT")
debug endif
debug return 0
endfunction
//on unit index
private function I takes nothing returns boolean
local integer i=GetUnitTypeId(GetIndexedUnit())
local integer k=GetIndexedUnitId()
//store gold/lumber cost for unit
set gu[k]=GetUnitTypeIdGoldCost(i)
set lu[k]=l[i]
return false
endfunction
//on unit deindex
private function D takes nothing returns boolean
local integer k=GetIndexedUnitId()
//reset gold/lumber cost for unit
set gu[k]=0
set lu[k]=0
return false
endfunction
//on unit upgrade start
private function US takes nothing returns boolean
local integer k=GetUnitUserData(GetTriggerUnit())
local integer i=GetUnitTypeId(GetTriggerUnit())
//if the unit is indexed, store upgrade gold/lumber cost
if (0!=k) then
set ug[k]=GetUnitTypeIdGoldCost(i)
set ul[k]=l[i]
set gu[k]=ug[k]+gu[k]
set lu[k]=ul[k]+lu[k]
endif
return false
endfunction
//on unit upgrade cancel
private function UC takes nothing returns boolean
local integer k=GetUnitUserData(GetTriggerUnit())
//if unit is indexed, add upgrade cost to cost
if (0!=k) then
set gu[k]=gu[k]-ug[k]
set lu[k]=lu[k]-ul[k]
endif
return false
endfunction
//! endtextmacro
//! textmacro UNIT_COST_2
local trigger t=CreateTrigger() //sell unit
//! endtextmacro
//! textmacro UNIT_COST_3
set g=Table.create() //gold table
set l=Table.create() //lumber table
//register unit indexing for setting/resetting gold/lumber costs for units
call RegisterUnitIndexEvent(Condition(function I),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function D),UnitIndexer.DEINDEX)
//this is done for removing units that are sold for getting their costs
call TriggerRegisterUnitEvent(t,u,EVENT_UNIT_SELL)
call TriggerAddCondition(t,Condition(function O))
//this is done for updating unit gold/lumber costs on upgrade
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_START, Condition(function US))
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_CANCEL, Condition(function UC))
//! endtextmacro
endlibrary
I don't trust paused timers to start with the timeouts they are supposed to start with.
The map I'm working on uses GetUnitCost
You can easily fix those natives by manually editing the war3map.j file, which would take less than a few minutes.
Any semi-decent MPQ editor.What program do you use with this?
You could easily do a workaround for those 2 by just giving them the same treatment as you did for ones that don't count with the natives. The speed loss of not using those natives is going to be heavily eclipsed by the speed gain and map file size compression gained by the optimizer.
Or I could say that vexorian's optimizer being broken is not my problem.
When there is a 100% working optimizer for vJASS (so not the froptimizer), I'll happily write readable scripts.
Challenge Accepted.
I was planning on writing one in the past, but I was too lazy.
I guess now would be a good time to get started on one
And with optimizer you mean a script that shortens all the names?
Imo "CreateTimerMethod" should take "code" argument, which reduces user verbosity and will make the compiled code a bit lessened.
"Nah, boolexpr"?
Pointless verbosity.
Well, if I use this, I would for sure change the argument to use code instead of boolexpr, in my own map
boolexpr array
Filter(myCode)
I think what Bribe is trying to point out is that you can convert the passed code to boolexpr in the create method, just like you'd pass them:Filter(myCode)