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 animationNode AnimationNode --- @field state "show" | "idle" | "hide" --- @field children SkillButton[] local skillRow = setmetatable({}, Element) skillRow.__index = skillRow --- @param characterId Id --- @return SkillRow function skillRow.new(characterId) local t = { characterId = characterId, 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() if t.state ~= "idle" then return end 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 { function(animationNode) if self.animationNode then self.animationNode:finish() end self.animationNode = animationNode self.state = "show" end, duration = 300, onEnd = function() self.state = "idle" end, easing = easing.easeOutCubic }:run() end function skillRow:hide() AnimationNode { function(animationNode) if self.animationNode then self.animationNode:finish() end self.animationNode = animationNode self.state = "hide" end, duration = 300, easing = easing.easeOutCubic }:run() end function skillRow:update(dt) if self.animationNode then self.animationNode:update(dt) 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 = self.state == "show" and 10 * (1 - self.animationNode:getValue()) or 0 } 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 = 1 if self.state == "show" then alpha = self.animationNode:getValue() elseif self.state == "hide" then alpha = 1 - self.animationNode:getValue() end love.graphics.setColor(1, 1, 1, alpha) for _, skb in ipairs(self.children) do skb:draw() end end return skillRow.new