Timers are pretty awkward to use so I made a script which extends the timer functionality. It was much harder to implement than I thought it would be. There was all kind of weird problems, like if you destroy the timer in connector it will still call all remaining connectors but the timer object passed to connector event function is corrupted in some way (bad object error), that was pain in the ass to debug...but finally it works. But at least now I know everything about the timers

You can use it like normal timer but you have to spawn it by calling the timers:create(id) function
Example
Get the sources from the bottom of this post, create a script entity named myscript and follow the examples.
Code: Select all
function createTimer()
local mytimer = timers:create('mytimer_id')
mytimer:addConnector('activate','myscript','tick')
mytimer:setTimerInterval(1)
mytimer:activate()
end
function tick(timer)
print(timer.id..': tick')
end
Difference to native connectors is that the connector even function will have the extended timer object as an argument.
Callbacks (with arguments)
Timer:addCallback(callbackFunction,callbackArguments)
Arguments must be passed in table format
Code: Select all
mytimer:addCallback(
function(self,param1)
print(self.id..': Hello '..param1)
end,
{'You'}
)
Callbacks will have the extended timer object as a first argument.
tickLimit
Timer:setTickLimit(count,destroyFlag)
timer will be deactivated after (count) rounds, if destroyFlag is true it will be also destroyed.
Code: Select all
mytimer:setTickLimit(5)
Timer will stay in real time all the time. Technically it spawns a timer to all dungeon levels which calls same functions than the original timer. Only the timer on same level as the party is active.
This was the main purpose of this script but I added some other features too.
You have to tell the amount of the dungeon levels to timers-script. Just call the timers:setLevels(amount) before you call the setConstant-function. You need to call it only once, all timers will use the same value.
Code: Select all
timers:setLevels(4)
mytimer:setConstant()
find timer
you can't access the exteded timer by findEntity or by typing the id in script like normal timers, it will return the actual(native) timer entity which is wrapped inside the extended timer. So you have to use
timers:find(timer_id) function
Code: Select all
local mytimer = timers:find('mytimer_id')
Code: Select all
function createTimer()
timers:setLevels(5) -- modify this to the amount of levels in your dungeon.
local mytimer = timers:create('mytimer_id')
mytimer:addConnector('activate','myscript','tick')
mytimer:setTimerInterval(3)
mytimer:setTickLimit(5)
mytimer:addCallback(
function(self,param1)
print(self.id..': Hello '..param1)
end,
{'You'}
)
mytimer:setConstant()
mytimer:activate()
local mytimer2 = timers:find('mytimer_id')
print(mytimer2.id)
end
function tick(timer)
print(timer.id..': tick')
end
Source
Create a new script entity named timers and copy paste this script in it.
I have tested that it works in game too and is save game compatible.
Code: Select all
objects = {}
debug = false
settings = {levels = 0}
-- spawn a new timer
function create(self,id,plevel)
plevel = plevel or party.level
local timerEntity = spawn('timer',plevel,0,0,1,id)
self.objects[id] = wrap(timerEntity)
timerEntity:addConnector('activate','timers','callCallbacks')
return self.objects[id]
end
function find(self,id)
return self.objects[id]
end
function setLevels(self,levels)
self.settings.levels = levels
end
-- create a wrapper object to timer passed as argument
function wrap(timer)
local wrapper = {
id = timer.id,
level = timer.level,
interval = 0,
connectors = {},
active = false,
callbacks = {},
tick = 0,
addConnector = function(self,paction,ptarget,pevent)
self:addCallback(
function(self,scriptId,functionName)
findEntity(scriptId)[functionName](self)
end,
{ptarget,pevent}
)
end,
activate = function(self)
self.active = true
if self.isConstant then
timers.objects[self.id..'_'..party.level]:activate()
else
findEntity(self.id):activate()
end
if self.instant then
self:callCallbacks()
end
end,
deactivate = function(self)
self.active = false
findEntity(self.id):deactivate()
if (self.isConstant) then
for l=1, timers.settings.levels do
timers.objects[self.id..'_'..l]:deactivate()
end
end
end,
toggle = function(self)
if (self.active) then
self:deactivate()
else
self:activate()
end
end,
isActivated = function(self)
return self.active
end,
-- If set the first function calls will be instant after the activation.
setInstant = function(self,bool)
self.instant = bool
end,
setTimerInterval = function(self,interval)
self.interval = interval
findEntity(self.id):setTimerInterval(interval)
end,
setConstant = function(self)
if timers.settings.levels == 0 then
print('You must set the amount of dungeon levels. For example: timers:setlevels(5)')
return
end
self.isConstant = true
timers.copyTimerToAllLevels(self)
end,
destroy = function(self)
findEntity(self.id):destroy()
timers.objects[self.id] = nil
if (self.isConstant) then
for l=1,timers.settings.levels do
timers.objects[self.id..'_'..l]:destroy()
end
end
end,
addCallback = function(self,callback,callbackArgs)
callbackArgs = callbackArgs or {}
self.callbacks[#self.callbacks+1] = {callback,callbackArgs}
end,
callCallbacks = timers.callCallbacks,
setTickLimit = function(self,limit,destroy)
self:addCallback(
function(self,limit,destroy)
if timers.debug then
print('tick count:'..self.tick)
end
if self.tick >= limit then
self:deactivate()
if timers.debug then print('timer '..self.id..' deactivated: tick limit '..limit) end
if (destroy) then
-- mark as destroyed
self.destroyed = true
end
end
end,
{limit,destroy}
)
end
}
return wrapper
end
function callCallbacks(timerEntity)
--print(timerEntity.id..' callbacks called')
local extTimer = objects[timerEntity.id]
extTimer.tick = extTimer.tick + 1
for _,callback in ipairs(extTimer.callbacks) do
callback[1](extTimer,unpack(callback[2]))
end
if (extTimer.destroyed) then
if timers.debug then print('timer '..extTimer.id..' destroyed') end
extTimer:destroy()
end
end
function copyTimerToAllLevels(self)
for l=1,timers.settings.levels do
local t = timers:create(self.id..'_'..l,l)
-- if interval is larger tha 1 second
-- use 1 second interval and count to actual interval
-- this way the gap between level changes should stay minimal
-- Thanks to Betty for the idea
if self.interval > 1 then
t:setTimerInterval(1)
self.count = 0
t:addCallback(
function(self,timer_id,interval)
local timer = timers.objects[timer_id]
if (self.level == party.level) then
timer.count = timer.count + 1
else
self:deactivate()
timers:find(timer_id..'_'..party.level):activate()
end
if (timer.count == interval) then
timer:callCallbacks()
timer.count = 0
end
end,
{self.id,self.interval}
)
else
t:setTimerInterval(self.interval)
t:addCallback(
function(self,timer_id)
local timer = timers.objects[timer_id]
if (self.level == party.level) then
timer:callCallbacks()
else
self:deactivate()
timers:find(timer_id..'_'..party.level):activate()
end
end,
{self.id}
)
end
end
self:deactivate()
end