local icons = require("lib.utils.sprite_atlas").load(Tree.assets.files.dev_icons) local easing = require "lib.utils.easing" local AnimationNode = require "lib.animation_node" local Element = require "lib.simple_ui.element" local Rect = require "lib.simple_ui.rect" --- @class SkillButton : UIElement --- @field hovered boolean --- @field selected boolean --- @field onClick function? --- @field icon string local skillButton = setmetatable({}, Element) skillButton.__index = skillButton function skillButton:update(dt) local mx, my = love.mouse.getPosition() if self:hitTest(mx, my) then self.hovered = true if Tree.controls:isJustPressed("select") then if self.onClick then self.onClick() end Tree.controls:consume("select") end else self.hovered = false end end function skillButton:draw() love.graphics.push() love.graphics.applyTransform(self.transform) local r, g, b, a = love.graphics.getColor() if self.selected then love.graphics.setColor(0.3, 1, 0.3, a) elseif self.hovered then love.graphics.setColor(0.7, 1, 0.7, a) else love.graphics.setColor(1, 1, 1, a) end love.graphics.translate(0, self.bounds.y) love.graphics.draw(icons.atlas, icons:pickQuad(self.icon)) love.graphics.pop() end --- @class SkillRow : UIElement --- @field characterId Id --- @field selected SkillButton? --- @field showNode AnimationNode? --- @field showT number --- @field hideNode AnimationNode? --- @field hideT number --- @field state "show" | "hide" --- @field children SkillButton[] local skillRow = setmetatable({}, Element) skillRow.__index = skillRow --- @param characterId Id function skillRow.new(characterId) local t = { characterId = characterId, showT = 0, hideT = 0, state = "show", children = {} } setmetatable(t, skillRow) local char = Tree.level.characters[characterId] char:try(Tree.behaviors.spellcaster, function(behavior) for i, spell in ipairs(behavior.spellbook) do local skb = skillButton:new { icon = spell.tag } skb.onClick = function() skb.selected = not skb.selected if t.selected then t.selected.selected = false end t.selected = skb if not behavior.cast then behavior.cast = behavior.spellbook[i] behavior.state = "casting" else behavior.state = "idle" behavior.cast = nil end end t.children[i] = skb end end) return t end function skillRow:show(animationNode) self.state = "show" if self.showNode then self.showNode:finish() end self.showT = 0 self.showNode = animationNode end function skillRow:hide(animationNode) self.state = "hide" if self.hideNode then self.hideNode:finish() end self.hideT = 0 self.hideNode = animationNode end function skillRow:update(dt) if self.showT < 1 and self.state == "show" then self.showT = self.showT + dt / 0.3 -- в знаменателе продолжительность анимации в секундах elseif self.showNode then self.showNode:finish() self.showNode = nil end if self.hideT < 1 and self.state == "hide" then self.hideT = self.hideT + dt / 0.3 elseif self.hideNode then self.hideNode:finish() self.hideNode = nil end local iconSize = icons.tileSize local scale = (64 / iconSize) local screenW, screenH = love.graphics.getDimensions() local padding = 8 local count = #self.children self.bounds = Rect { width = count * icons.tileSize + (count - 1) * padding, height = iconSize, y = 10 * easing.easeInCubic(1 - self.showT) } self.transform = love.math.newTransform():translate(screenW / 2, screenH - 16):scale(scale, scale):translate(-self.bounds.width / 2, -iconSize) for i, skb in ipairs(self.children) do skb.bounds = Rect { height = iconSize, width = iconSize } skb.transform = self.transform:clone():translate(self.bounds.x + (i - 1) * iconSize + (i - 1) * padding, -- левый край ряда + размер предыдущих иконок + размер предыдущих отступов self.bounds.y -- высота не меняется ) skb:update(dt) end end function skillRow:draw() local alpha = self.state == "show" and easing.easeInSine(self.showT) or 1 - easing.easeInSine(self.hideT) love.graphics.setColor(1, 1, 1, alpha) for _, skb in ipairs(self.children) do skb:draw() end end local layout = {} function layout:update(dt) local cid = Tree.level.selector:selected() if cid then self.skillRow = skillRow.new(cid) AnimationNode { function(animationNode) self.skillRow:show(animationNode) end, }:run() end if Tree.level.selector:deselected() then AnimationNode { function(animationNode) self.skillRow:hide(animationNode) end, onEnd = function() self.skillRow = nil end }:run() end if self.skillRow then self.skillRow:update(dt) end end function layout:draw() if self.skillRow then self.skillRow:draw() end end return layout