126 lines
4.2 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<T> 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<nil>
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<any[]>
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