local easing_lib = require "lib.utils.easing" --- Обобщенная асинхронная функция (Task). --- По сути это функция, принимающая коллбэк: `fun(callback: fun(value: T): nil): nil` --- @generic T --- @alias Task fun(callback: fun(value: T): nil): nil local task = {} local activeTweens = {} --- Внутренний хелпер для интерполяции (числа или Vec3) local function lerp(a, b, t) if type(a) == "number" then return a + (b - a) * t elseif type(a) == "table" and getmetatable(a) then -- Предполагаем, что это Vec3 или другой объект с операторами +, -, * return a + (b - a) * t end return b end --- Обновление всех активных анимаций (твинов). --- Нужно вызывать в love.update(dt) function task.update(dt) for i = #activeTweens, 1, -1 do local t = activeTweens[i] t.elapsed = t.elapsed + dt * 1000 local progress = math.min(t.elapsed / t.duration, 1) local value = t.easing(progress) for key, targetValue in pairs(t.properties) do t.target[key] = lerp(t.initial[key], targetValue, value) end if progress >= 1 then table.remove(activeTweens, i) if t.resolve then t.resolve() end end end end --- Возвращает Completer — объект, который позволяет вручную завершить таску. --- @return table completer { complete: fun(val: T) }, Task future function task.completer() local c = { completed = false, value = nil, cb = nil } function c:complete(val) if self.completed then return end self.completed = true self.value = val if self.cb then self.cb(val) end end local future = function(callback) if c.completed then callback(c.value) else c.cb = callback end end return c, future end --- Создает таску, которая плавно меняет свойства объекта. --- @param target table Объект, свойства которого меняем --- @param properties table Набор конечных значений { key = value } --- @param duration number Длительность в мс --- @param easing function? Функция смягчения (по умолчанию linear) --- @return Task function task.tween(target, properties, duration, easing) local initial = {} for k, _ in pairs(properties) do initial[k] = target[k] if type(initial[k]) == "table" and initial[k].copy then initial[k] = initial[k]:copy() -- Для Vec3 end end local comp, future = task.completer() table.insert(activeTweens, { target = target, initial = initial, properties = properties, duration = duration or 1000, easing = easing or easing_lib.linear, elapsed = 0, resolve = function() comp:complete() end }) return future end --- Возвращает таску, которая завершится сразу с переданным значением. function task.fromValue(val) return function(callback) callback(val) end end --- Возвращает новый Task, который завершится после завершения всех переданных `tasks`. --- @param tasks Task[] --- @return Task function task.wait(tasks) if #tasks == 0 then return task.fromValue({}) end local count = #tasks local results = {} return function(callback) for i, t in ipairs(tasks) do t(function(result) results[i] = result count = count - 1 if count == 0 then callback(results) end end) end end end --- Последовательно объединяет два `Task` в один. --- @param t Task --- @param onCompleted fun(value: any): Task --- @return Task function task.chain(t, onCompleted) return function(callback) t(function(value) local t2 = onCompleted(value) t2(callback) end) end end return task