local Timer = {} -- group management local function detach (group, item) local index = item.index group[index] = group[#group] group[index].index = index group[#group] = nil item.groupField = nil end local function attach (group, item) if item.groupField then detach (item.groupField, item) end local index = #group + 1 item.index = index group[index] = item item.groupField = group item.lastGroup = group end -- instance update methods local function updateContinuous (self, dt) local cutoff = self.cutoff local elapsed = self.elapsed + dt if self:callback(dt) == false or elapsed >= cutoff then if self.finishField then self:finishField(elapsed - cutoff) end self:remove() end self.elapsed = elapsed return end local function updateIntermittent (self, dt) local duration = self.delay or self.interval local elapsed = self.elapsed + dt while elapsed >= duration do elapsed = elapsed - duration if self.limitField then self.limitField = self.limitField - 1 end if self:callback(elapsed) == false or self.delay or self.limitField == 0 then if self.finishField then self:finishField(elapsed) end self:remove() return end end self.elapsed = elapsed end local function updateTween (self, dt) local elapsed = self.elapsed + dt local plan = self.plan local duration = self.duration self.elapsed = elapsed if elapsed >= duration then for index = 1, #plan do local task = plan[index] task.target[task.key] = task.final end if self.finishField then self:finishField(elapsed - duration) end self:remove() return end local ease = self.easeField for index = 1, #plan do local task = plan[index] local target, key = task.target, task.key local initial, change = task.initial, task.change target[key] = ease(elapsed, initial, change, duration) end end -- shared instance methods local defaultGroup = {} local function group (self, group) if not group then group = defaultGroup end attach(group, self) return self end local function remove (self) if self.groupField then detach(self.groupField, self) end return self end local function register (self) attach(self.lastGroup, self) return self end local function limit (self, limitField) self.limitField = limitField return self end local function finish (self, finishField) self.finishField = finishField return self end local function ease (self, easeField) self.easeField = easeField return self end -- tweening helper functions local function planTween (definition) local plan = {} for target, values in pairs(definition) do for key, final in pairs(values) do local initial = target[key] plan[#plan + 1] = { target = target, key = key, initial = initial, final = final, change = final - initial, } end end return plan end local function easeLinear (elapsed, initial, change, duration) return change * elapsed / duration + initial end -- instance initializer local function initialize (timer) timer.elapsed = 0 timer.group = group timer.remove = remove timer.register = register attach(defaultGroup, timer) return timer end -- static api function Timer.after (delay, callback) return initialize { delay = delay, callback = callback, update = updateIntermittent, } end function Timer.every (interval, callback) return initialize { interval = interval, callback = callback, update = updateIntermittent, limit = limit, finish = finish, } end function Timer.prior (cutoff, callback) return initialize { cutoff = cutoff, callback = callback, update = updateContinuous, finish = finish, } end function Timer.tween (duration, definition) return initialize { duration = duration, plan = planTween(definition), update = updateTween, easeField = easeLinear, ease = ease, finish = finish, } end function Timer.update (dt, group) if not group then group = defaultGroup end for index = #group, 1, -1 do group[index]:update(dt) end end function Timer.clear (group) if not group then group = defaultGroup end for i = 1, #group do group[i] = nil end end Timer.defaultGroup = defaultGroup return Timer