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" local SkillRow = require "lib.simple_ui.level.skill_row" local Color = require "lib.simple_ui.color" --- @class BarElement : UIElement --- @field getter fun() : number --- @field value number --- @field maxValue number --- @field color Color --- @field useDividers boolean --- @field drawText boolean local barElement = setmetatable({}, Element) barElement.__index = barElement barElement.useDividers = false barElement.drawText = false function barElement:update(dt) local val = self.getter() self.value = val < 0 and 0 or val > self.maxValue and self.maxValue or val end function barElement:draw() local valueWidth = self.bounds.width * self.value / self.maxValue local emptyWidth = self.bounds.width - valueWidth --- шум love.graphics.setShader(Tree.assets.files.shaders.soft_uniform_noise) love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) love.graphics.setShader() --- закраска пустой части love.graphics.setColor(0.05, 0.05, 0.05) love.graphics.setBlendMode("multiply", "premultiplied") love.graphics.rectangle("fill", self.bounds.x + valueWidth, self.bounds.y, emptyWidth, self.bounds.height) love.graphics.setBlendMode("alpha") --- закраска значимой части её цветом love.graphics.setColor(self.color.r, self.color.g, self.color.b) love.graphics.setBlendMode("multiply", "premultiplied") love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, valueWidth, self.bounds.height) love.graphics.setBlendMode("alpha") --- мерки love.graphics.setColor(38 / 255, 50 / 255, 56 / 255) if self.useDividers then local count = self.maxValue - 1 local measureWidth = self.bounds.width / self.maxValue for i = 1, count, 1 do love.graphics.line(self.bounds.x + i * measureWidth, self.bounds.y, self.bounds.x + i * measureWidth, self.bounds.y + self.bounds.height) end end love.graphics.setColor(1, 1, 1) --- текст поверх if self.drawText then love.graphics.printf(tostring(self.value) .. "/" .. tostring(self.maxValue), self.bounds.x, self.bounds.y, self.bounds.width, "center") end self:drawBorder("inner") self:drawGradientOverlay() end --- @class BottomBars : UIElement --- @field hpBar BarElement --- @field manaBar BarElement local bottomBars = setmetatable({}, Element) bottomBars.__index = bottomBars; --- @param cid Id function bottomBars.new(cid) local t = setmetatable({}, bottomBars) t.hpBar = barElement:new { getter = function() local char = Tree.level.characters[cid] return char:try(Tree.behaviors.stats, function(stats) return stats.hp or 0 end) end, color = Color { r = 130 / 255, g = 8 / 255, b = 8 / 255 }, drawText = true, maxValue = 20 } t.manaBar = barElement:new { getter = function() local char = Tree.level.characters[cid] return char:try(Tree.behaviors.stats, function(stats) return stats.mana or 0 end) end, color = Color { r = 51 / 255, g = 105 / 255, b = 30 / 255 }, useDividers = true, maxValue = 10 } return t end function bottomBars:update(dt) local height = 16 local margin = 2 self.bounds.height = height self.bounds.y = self.bounds.y - height self.hpBar.bounds = Rect { width = -2 * margin + self.bounds.width / 2, height = height - margin, x = self.bounds.x + margin, y = self.bounds.y + margin } self.manaBar.bounds = Rect { width = -2 * margin + self.bounds.width / 2, height = height - margin, x = self.bounds.x + margin + self.bounds.width / 2, y = self.bounds.y + margin } self.hpBar:update(dt) self.manaBar:update(dt) end function bottomBars:draw() -- шум love.graphics.setShader(Tree.assets.files.shaders.soft_uniform_noise) love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) love.graphics.setShader() love.graphics.setColor(38 / 255, 50 / 255, 56 / 255) love.graphics.setBlendMode("multiply", "premultiplied") love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) love.graphics.setBlendMode("alpha") self.hpBar:draw() self.manaBar:draw() end --- @class CharacterPanel : UIElement --- @field animationNode AnimationNode --- @field state "show" | "idle" | "hide" --- @field skillRow SkillRow --- @field bars BottomBars local characterPanel = setmetatable({}, Element) characterPanel.__index = characterPanel function characterPanel.new(characterId) local t = {} t.state = "show" t.skillRow = SkillRow(characterId) t.bars = bottomBars.new(characterId) return setmetatable(t, characterPanel) end function characterPanel: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 characterPanel: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 --- @type love.Canvas local characterPanelCanvas; function characterPanel:update(dt) if self.animationNode then self.animationNode:update(dt) end self.skillRow:update(dt) self.bars.bounds = Rect { width = self.skillRow.bounds.width, x = self.skillRow.bounds.x, y = self.skillRow.bounds.y } self.bars:update(dt) self.bounds = Rect { x = self.bars.bounds.x, y = self.bars.bounds.y, width = self.bars.bounds.width, height = self.bars.bounds.height + self.skillRow.bounds.height } if not characterPanelCanvas then characterPanelCanvas = love.graphics.newCanvas(self.bounds.width, self.bounds.height) end --- анимация появления local alpha = 1 if self.state == "show" then alpha = self.animationNode:getValue() elseif self.state == "hide" then alpha = 1 - self.animationNode:getValue() end local revealShader = Tree.assets.files.shaders.reveal revealShader:send("t", alpha) end function characterPanel:draw() self.skillRow:draw() --- @TODO: переписать этот ужас с жонглированием координатами, а то слишком хардкод (skillRow рисуется относительно нуля и не закрывает канвас) love.graphics.push() local canvas = love.graphics.getCanvas() love.graphics.translate(0, self.bars.bounds.height) love.graphics.setCanvas(characterPanelCanvas) love.graphics.clear() love.graphics.draw(canvas) love.graphics.pop() love.graphics.push() love.graphics.translate(-self.bounds.x, -self.bounds.y) self.bars:draw() self:drawBorder("outer") love.graphics.pop() --- рисуем текстуру шейдером появления love.graphics.setCanvas() love.graphics.setShader(Tree.assets.files.shaders.reveal) love.graphics.setColor(1, 1, 1, 1) love.graphics.push() love.graphics.translate(self.bounds.x, self.bounds.y) love.graphics.draw(characterPanelCanvas) love.graphics.setColor(1, 1, 1) love.graphics.pop() love.graphics.setShader() end ----------------------------------- local layout = {} function layout:update(dt) local cid = Tree.level.selector:selected() if cid then self.characterPanel = characterPanel.new(cid) self.characterPanel:show() elseif Tree.level.selector:deselected() then self.characterPanel:hide() end if self.characterPanel then self.characterPanel:update(dt) end end function layout:draw() if self.characterPanel then self.characterPanel:draw() end end return layout