---@class ActionQueue ---@field queue table actionQueue = { queue = {}, } ---@alias Action fun(deltaSeconds: number) local close = coroutine.close --- Added actions will be called on every runWaiting() update. --- They will continue to be executed until they return Succeeded or Failed instead of NeedsMoreTime. --- --- Replaces any existing action with the given id. --- If the initial call of action() doesn't return NeedsMoreTime, this function will not bother adding it to the queue. ---@param id any ---@param maxTimeMs number ---@param action Action function actionQueue:upsert(id, maxTimeMs, action) if self.queue[id] then close(self.queue[id].coroutine) end self.queue[id] = { coroutine = coroutine.create(action), expireTimeMs = maxTimeMs + playdate.getCurrentTimeMilliseconds(), } end function actionQueue:newOnly(id, maxTimeMs, action) if self.queue[id] then return end self.queue[id] = { coroutine = coroutine.create(action), expireTimeMs = maxTimeMs + playdate.getCurrentTimeMilliseconds(), } end --- Must be called on every playdate.update() to check for (and run) any waiting tasks. --- Actions that return NeedsMoreTime will not be removed from the queue unless they have expired. function actionQueue:runWaiting(deltaSeconds) local currentTimeMs = playdate.getCurrentTimeMilliseconds() for id, actionObject in pairs(self.queue) do coroutine.resume(actionObject.coroutine, deltaSeconds) if currentTimeMs > actionObject.expireTimeMs then close(actionObject.coroutine) end if coroutine.status(actionObject.coroutine) == "dead" then self.queue[id] = nil end end end -- luacheck: ignore if not playdate or playdate.TEST_MODE then return actionQueue end