mirror of
https://github.com/Reuh/ubiquitousse.git
synced 2025-10-27 09:09:30 +00:00
Restructure timer
This commit is contained in:
parent
5d2c9c5f31
commit
fdd69fd7f8
1 changed files with 199 additions and 187 deletions
386
timer/timer.lua
386
timer/timer.lua
|
|
@ -4,7 +4,6 @@ local loaded, signal = pcall(require, (...):match("^(.-)timer").."signal")
|
|||
if not loaded then signal = nil end
|
||||
|
||||
local ease = require((...):match("^.-timer")..".easing")
|
||||
local timer
|
||||
|
||||
--- Returns true if all the values in the list are true ; functions in the list will be called and the test will be performed on their return value.
|
||||
-- Returns default if the list is empty.
|
||||
|
|
@ -24,222 +23,234 @@ local function all(list, default)
|
|||
end
|
||||
end
|
||||
|
||||
-- Timer methods.
|
||||
local timer_mt = {
|
||||
--- timer data table
|
||||
t = nil,
|
||||
|
||||
--- Wait time milliseconds before running the function.
|
||||
-- Specify no time to remove condition.
|
||||
after = function(self, time)
|
||||
self.t.after = time
|
||||
return self
|
||||
end,
|
||||
--- Run the function every time millisecond.
|
||||
-- Specify no time to remove condition.
|
||||
every = function(self, time)
|
||||
self.t.every = time
|
||||
return self
|
||||
end,
|
||||
--- The function will not execute more than count times.
|
||||
-- Specify no time to remove condition.
|
||||
times = function(self, count)
|
||||
self.t.times = count or -1
|
||||
return self
|
||||
end,
|
||||
--- The TimedFunction will be active for a time duration.
|
||||
-- Specify no time to remove condition.
|
||||
during = function(self, time)
|
||||
self.t.during = time
|
||||
return self
|
||||
end,
|
||||
|
||||
--- Function conditions ---
|
||||
--- Starts the function execution when func() returns true. Checked before the "after" condition,
|
||||
-- meaning the "after" countdown starts when func() returns true.
|
||||
-- If multiple init functions are added, init will trigger only when all of them returns true.
|
||||
initWhen = function(self, func)
|
||||
table.insert(self.t.initWhen, func)
|
||||
return self
|
||||
end,
|
||||
--- Starts the function execution when func() returns true. Checked after the "after" condition.
|
||||
-- If multiple start functions are added, start will trigger only when all of them returns true.
|
||||
startWhen = function(self, func)
|
||||
table.insert(self.t.startWhen, func)
|
||||
return self
|
||||
end,
|
||||
--- When the functions ends, the execution won't stop and will repeat as long as func() returns true.
|
||||
-- Will cancel timed repeat conditions if false but needs other timed repeat conditions to be true to create a new repeat.
|
||||
-- If multiple repeat functions are added, a repeat will trigger only when all of them returns true.
|
||||
repeatWhile = function(self, func)
|
||||
table.insert(self.t.repeatWhile, func)
|
||||
return self
|
||||
end,
|
||||
--- Stops the function execution when func() returns true. Checked before all timed conditions.
|
||||
-- If multiple stop functions are added, stop will trigger only when all of them returns true.
|
||||
stopWhen = function(self, func)
|
||||
table.insert(self.t.stopWhen, func)
|
||||
return self
|
||||
end,
|
||||
|
||||
--- Conditions override ---
|
||||
--- Force the function to start its execution.
|
||||
start = function(self)
|
||||
self.t.forceStart = true
|
||||
return self
|
||||
end,
|
||||
--- Force the function to stop its execution.
|
||||
stop = function(self)
|
||||
self.t.forceStop = true
|
||||
return self
|
||||
end,
|
||||
--- Force the function to stop immediately. Won't trigger onEnd or other callbacks.
|
||||
abort = function(self)
|
||||
self.t.abort = true
|
||||
return self
|
||||
end,
|
||||
--- Skip some amount of time.
|
||||
skip = function(self, time)
|
||||
self.t.skip = (self.t.skip or 0) + time
|
||||
end,
|
||||
|
||||
--- Callbacks functions ---
|
||||
--- Will execute func(self, lag) when the function execution start.
|
||||
onStart = function(self, func)
|
||||
table.insert(self.t.onStart, func)
|
||||
return self
|
||||
end,
|
||||
--- Will execute func(self, lag) each frame the main function is run.
|
||||
onUpdate = function(self, func)
|
||||
table.insert(self.t.onUpdate, func)
|
||||
return self
|
||||
end,
|
||||
--- Will execute func(self, lag) when the function execution end.
|
||||
onEnd = function(self, func)
|
||||
table.insert(self.t.onEnd, func)
|
||||
return self
|
||||
end,
|
||||
|
||||
--- Chaining ---
|
||||
--- Creates another TimedFunction which will be initialized immediately when the current one ends.
|
||||
-- Returns the new TimedFunction.
|
||||
chain = function(self, func)
|
||||
local done = false
|
||||
local fn = self:run(func)
|
||||
:initWhen(function() return done end)
|
||||
self:onEnd(function(self, lag)
|
||||
done = true
|
||||
fn:skip(lag)
|
||||
end)
|
||||
return fn
|
||||
end
|
||||
}
|
||||
timer_mt.__index = timer_mt
|
||||
|
||||
--- Registry methods.
|
||||
local registry_mt = {
|
||||
--- Update all the TimedFunctions calls.
|
||||
--- Update all the timers in the registry.
|
||||
-- Should be called at every game update; called by ubiquitousse.update.
|
||||
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
|
||||
-- @impl ubiquitousse
|
||||
update = function(self, dt)
|
||||
self.dt = dt
|
||||
|
||||
local done = {} -- functions done running
|
||||
|
||||
local d = self.delayed
|
||||
for func, t in pairs(d) do
|
||||
if t and all(t.initWhen, true) then
|
||||
t.initWhen = {}
|
||||
local co = t.coroutine
|
||||
-- skip
|
||||
dt = self.dt
|
||||
if t.skip then dt = dt + t.skip end
|
||||
-- start
|
||||
if t.after then t.after = t.after - dt end
|
||||
if t.forceStart or ((not t.after or t.after <= 0) and all(t.startWhen, true)) then
|
||||
local startLag = 0
|
||||
if t.after then
|
||||
startLag = -t.after
|
||||
elseif t.skip then
|
||||
startLag = t.skip
|
||||
end
|
||||
t.after, t.skip = nil, nil
|
||||
t.startWhen = {}
|
||||
d[func] = false -- niling here cause the next pair iteration to error
|
||||
table.insert(done, func)
|
||||
if not co then
|
||||
co = coroutine.create(func)
|
||||
t.coroutine = co
|
||||
if t.times > 0 then t.times = t.times - 1 end
|
||||
for _, f in ipairs(t.onStart) do f(t.object, startLag) end
|
||||
end
|
||||
-- update
|
||||
assert(coroutine.resume(co, function(delay)
|
||||
t.after = delay - startLag
|
||||
d[func] = t
|
||||
coroutine.yield()
|
||||
end, dt))
|
||||
for _, f in ipairs(t.onUpdate) do f(t.object, startLag) end
|
||||
if t.during then t.during = t.during - startLag - dt end
|
||||
-- stopping / repeat
|
||||
if all(t.stopWhen, false) then t.forceStop = true end
|
||||
if t.forceStop or coroutine.status(co) == "dead" then
|
||||
if t.forceStop
|
||||
or (t.during and t.during <= 0)
|
||||
or (t.times == 0)
|
||||
or (not all(t.repeatWhile, true))
|
||||
or (t.every == nil and t.times == -1 and t.during == nil and #t.repeatWhile == 0) -- no repeat
|
||||
then
|
||||
local endLag = t.during and -t.during or 0
|
||||
for _, f in ipairs(t.onEnd) do f(t.object, endLag) end
|
||||
else
|
||||
-- process timers
|
||||
for _, timer in ipairs(self.delayed) do
|
||||
local t = timer.t
|
||||
if not t.dead then
|
||||
if t.abort then
|
||||
t.dead = true
|
||||
elseif all(t.initWhen, true) then
|
||||
t.initWhen = {}
|
||||
local co = t.coroutine
|
||||
-- skip
|
||||
dt = self.dt
|
||||
if t.skip then dt = dt + t.skip end
|
||||
-- start
|
||||
if t.after then t.after = t.after - dt end
|
||||
if t.forceStart or ((not t.after or t.after <= 0) and all(t.startWhen, true)) then
|
||||
local startLag = 0
|
||||
if t.after then
|
||||
startLag = -t.after
|
||||
elseif t.skip then
|
||||
startLag = t.skip
|
||||
end
|
||||
t.after, t.skip = nil, nil
|
||||
t.startWhen = {}
|
||||
t.dead = true -- niling here cause the next pair iteration to error
|
||||
if not co then
|
||||
co = coroutine.create(t.func)
|
||||
t.coroutine = co
|
||||
if t.times > 0 then t.times = t.times - 1 end
|
||||
if t.every then t.after = t.every - startLag end
|
||||
t.coroutine = coroutine.create(func)
|
||||
d[func] = t
|
||||
for _, f in ipairs(t.onStart) do f(timer, startLag) end
|
||||
end
|
||||
-- update
|
||||
assert(coroutine.resume(co, timer, dt, 0, function(delay)
|
||||
t.after = delay - startLag
|
||||
t.dead = false
|
||||
local _, _, dt, lag = coroutine.yield()
|
||||
return dt, lag
|
||||
end))
|
||||
for _, f in ipairs(t.onUpdate) do f(t.object, startLag) end
|
||||
if t.during then t.during = t.during - startLag - dt end
|
||||
-- stopping / repeat
|
||||
if all(t.stopWhen, false) then t.forceStop = true end
|
||||
if t.forceStop or coroutine.status(co) == "dead" then
|
||||
if t.forceStop
|
||||
or (t.during and t.during <= 0)
|
||||
or (t.times == 0)
|
||||
or (not all(t.repeatWhile, true))
|
||||
or (t.every == nil and t.times == -1 and t.during == nil and #t.repeatWhile == 0) -- no repeat
|
||||
then
|
||||
local endLag = t.during and -t.during or 0
|
||||
for _, f in ipairs(t.onEnd) do f(timer, endLag) end
|
||||
else
|
||||
if t.times > 0 then t.times = t.times - 1 end
|
||||
if t.every then t.after = t.every - startLag end
|
||||
t.coroutine = coroutine.create(t.func)
|
||||
t.dead = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, func in ipairs(done) do
|
||||
if not d[func] then
|
||||
d[func] = nil
|
||||
-- remove dead timers
|
||||
for i=#self.delayed, 1, -1 do
|
||||
if self.delayed[i].t.dead then
|
||||
table.remove(self.delayed, i)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
--- Schedule a function to run.
|
||||
-- The function will receive as first parameter the wait(time) function, which will pause the function execution for time miliseconds.
|
||||
-- The function will receive as first parameter the TimedFunction object.
|
||||
-- As a second parameter, the function will receive the delta time (dt).
|
||||
-- As a third parameter, the function will receive the lag time (difference between the time when the function was run and when it should have been run).
|
||||
-- As a fourth parameter, the function will receive as first parameter the wait(time) function, which will pause the function execution for time miliseconds.
|
||||
-- @tparam[opt] function func the function to schedule
|
||||
-- @treturn TimedFunction the object
|
||||
-- @treturn timer the object
|
||||
-- @impl ubiquitousse
|
||||
run = function(self, func)
|
||||
-- Creates empty function (the TimedFunction may be used for time measure or stuff like that which doesn't need a specific function)
|
||||
func = func or function() end
|
||||
local r = setmetatable({
|
||||
t = {
|
||||
dead = false,
|
||||
func = func or function() end,
|
||||
coroutine = nil,
|
||||
|
||||
-- Since delayed functions can end in any order, it doesn't really make sense to use a integer-keyed list.
|
||||
-- Using the function as the key works and it's unique.
|
||||
self.delayed[func] = {
|
||||
object = nil,
|
||||
coroutine = nil,
|
||||
after = nil,
|
||||
every = nil,
|
||||
times = -1,
|
||||
during = nil,
|
||||
|
||||
after = nil,
|
||||
every = nil,
|
||||
times = -1,
|
||||
during = nil,
|
||||
initWhen = {},
|
||||
startWhen = {},
|
||||
repeatWhile = {},
|
||||
stopWhen = {},
|
||||
|
||||
initWhen = {},
|
||||
startWhen = {},
|
||||
repeatWhile = {},
|
||||
stopWhen = {},
|
||||
forceStart = false,
|
||||
forceStop = false,
|
||||
skip = nil,
|
||||
|
||||
forceStart = false,
|
||||
forceStop = false,
|
||||
skip = nil,
|
||||
onStart = {},
|
||||
onUpdate = {},
|
||||
onEnd = {}
|
||||
}
|
||||
}, timer_mt)
|
||||
|
||||
onStart = {},
|
||||
onUpdate = {},
|
||||
onEnd = {}
|
||||
}
|
||||
table.insert(self.delayed, r)
|
||||
|
||||
local t = self.delayed[func] -- internal data
|
||||
local r -- external interface
|
||||
r = {
|
||||
--- Timed conditions ---
|
||||
--- Wait time milliseconds before running the function.
|
||||
-- Specify no time to remove condition.
|
||||
after = function(_, time)
|
||||
t.after = time
|
||||
return r
|
||||
end,
|
||||
--- Run the function every time millisecond.
|
||||
-- Specify no time to remove condition.
|
||||
every = function(_, time)
|
||||
t.every = time
|
||||
return r
|
||||
end,
|
||||
--- The function will not execute more than count times.
|
||||
-- Specify no time to remove condition.
|
||||
times = function(_, count)
|
||||
t.times = count or -1
|
||||
return r
|
||||
end,
|
||||
--- The TimedFunction will be active for a time duration.
|
||||
-- Specify no time to remove condition.
|
||||
during = function(_, time)
|
||||
t.during = time
|
||||
return r
|
||||
end,
|
||||
|
||||
--- Function conditions ---
|
||||
--- Starts the function execution when func() returns true. Checked before the "after" condition,
|
||||
-- meaning the "after" countdown starts when func() returns true.
|
||||
-- If multiple init functions are added, init will trigger only when all of them returns true.
|
||||
initWhen = function(_, func)
|
||||
table.insert(t.initWhen, func)
|
||||
return r
|
||||
end,
|
||||
--- Starts the function execution when func() returns true. Checked after the "after" condition.
|
||||
-- If multiple start functions are added, start will trigger only when all of them returns true.
|
||||
startWhen = function(_, func)
|
||||
table.insert(t.startWhen, func)
|
||||
return r
|
||||
end,
|
||||
--- When the functions ends, the execution won't stop and will repeat as long as func() returns true.
|
||||
-- Will cancel timed repeat conditions if false but needs other timed repeat conditions to be true to create a new repeat.
|
||||
-- If multiple repeat functions are added, a repeat will trigger only when all of them returns true.
|
||||
repeatWhile = function(_, func)
|
||||
table.insert(t.repeatWhile, func)
|
||||
return r
|
||||
end,
|
||||
--- Stops the function execution when func() returns true. Checked before all timed conditions.
|
||||
-- If multiple stop functions are added, stop will trigger only when all of them returns true.
|
||||
stopWhen = function(_, func)
|
||||
table.insert(t.stopWhen, func)
|
||||
return r
|
||||
end,
|
||||
|
||||
--- Conditions override ---
|
||||
--- Force the function to start its execution.
|
||||
start = function(_)
|
||||
t.forceStart = true
|
||||
return r
|
||||
end,
|
||||
--- Force the function to stop its execution.
|
||||
stop = function(_)
|
||||
t.forceStop = true
|
||||
return r
|
||||
end,
|
||||
--- Skip some amount of time.
|
||||
skip = function(_, time)
|
||||
t.skip = (t.skip or 0) + time
|
||||
end,
|
||||
|
||||
--- Callbacks functions ---
|
||||
--- Will execute func(self, lag) when the function execution start.
|
||||
onStart = function(_, func)
|
||||
table.insert(t.onStart, func)
|
||||
return r
|
||||
end,
|
||||
--- Will execute func(self, lag) each frame the main function is run.
|
||||
onUpdate = function(_, func)
|
||||
table.insert(t.onUpdate, func)
|
||||
return r
|
||||
end,
|
||||
--- Will execute func(self, lag) when the function execution end.
|
||||
onEnd = function(_, func)
|
||||
table.insert(t.onEnd, func)
|
||||
return r
|
||||
end,
|
||||
|
||||
--- Chaining ---
|
||||
--- Creates another TimedFunction which will be initialized immediately when the current one ends.
|
||||
-- Returns the new TimedFunction.
|
||||
chain = function(_, func)
|
||||
local done = false
|
||||
local fn = self:run(func)
|
||||
:initWhen(function() return done end)
|
||||
r:onEnd(function(self, lag)
|
||||
done = true
|
||||
fn:skip(lag)
|
||||
end)
|
||||
return fn
|
||||
end
|
||||
}
|
||||
t.object = r
|
||||
return r
|
||||
end,
|
||||
|
||||
|
|
@ -271,7 +282,7 @@ local registry_mt = {
|
|||
end
|
||||
end
|
||||
|
||||
local r = self:run(function(wait, dt)
|
||||
local r = self:run(function(self, dt)
|
||||
time = time + dt
|
||||
update(tbl, from, to)
|
||||
end):during(duration)
|
||||
|
|
@ -325,6 +336,7 @@ local registry_mt = {
|
|||
registry_mt.__index = registry_mt
|
||||
|
||||
--- Time related functions
|
||||
local timer
|
||||
timer = {
|
||||
--- Creates and return a new TimerRegistry.
|
||||
-- A TimerRegistry is a separate ubiquitousse.time instance: its TimedFunctions will be independant
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue