--- @alias voidCallback fun(): nil --- @alias animationRunner fun(node: AnimationNode) --- Узел дерева анимаций. --- --- Отслеживает завершение всех анимаций всех дочерних узлов и оповещает вышестоящий узел. --- --- Дочерние узлы одного уровня запускают свою анимацию одновременно после завершения анимации родителя. --- Example: --- ```lua --- AnimationNode { --- function (node) residentsleeper:sleep(1000, node) end, -- must pass itself down as the parameter --- onEnd = function() print("completed") end, --- children = { -- children run in parallel after the parent animation is completed --- AnimationNode { --- function (node) sprite:animate("attack", node) end --- }, --- AnimationNode { --- function (node) other_sprite:animate("hurt", node) end --- }, --- } --- }:run() --- ``` --- @class AnimationNode --- @field count integer --- @field run animationRunner --- @field parent AnimationNode? --- @field children AnimationNode[] --- @field finish voidCallback --- @field onEnd voidCallback? local animation = {} animation.__index = animation --- Регистрация завершения дочерней анимации function animation:bubbleUp() self.count = self.count - 1 if self.count > 0 then return end if self.onEnd then self.onEnd() end if self.parent then self.parent:bubbleUp() end end --- @param children AnimationNode[] --- Запланировать анимации после текущей, которые запустятся одновременно друг с другом function animation:chain(children) for _, child in ipairs(children) do child.parent = self table.insert(self.children, child) self.count = self.count + 1 end return self end --- @param data {[1]: animationRunner?, onEnd?: voidCallback, children?: AnimationNode[]} --- @return AnimationNode local function new(data) local t = setmetatable({}, animation) t.run = data[1] or function(self) self:finish() end t.onEnd = data.onEnd t.count = 1 -- своя анимация t.children = {} t:chain(data.children or {}) t.finish = function() t:bubbleUp() for _, anim in ipairs(t.children) do anim:run() end end return t end return new