From 2012035eb6036bca6451a3e2540315ea07a14918 Mon Sep 17 00:00:00 2001 From: PeaAshMeter Date: Fri, 12 Dec 2025 02:36:51 +0300 Subject: [PATCH] refactor level layout --- lib/simple_ui/level/bar.lua | 69 ++++++++ lib/simple_ui/level/bottom_bars.lua | 86 +++++++++ lib/simple_ui/level/cpanel.lua | 118 +++++++++++++ lib/simple_ui/level/layout.lua | 264 +--------------------------- lib/simple_ui/level/skill_row.lua | 2 - 5 files changed, 275 insertions(+), 264 deletions(-) create mode 100644 lib/simple_ui/level/bar.lua create mode 100644 lib/simple_ui/level/bottom_bars.lua create mode 100644 lib/simple_ui/level/cpanel.lua diff --git a/lib/simple_ui/level/bar.lua b/lib/simple_ui/level/bar.lua new file mode 100644 index 0000000..aa3d723 --- /dev/null +++ b/lib/simple_ui/level/bar.lua @@ -0,0 +1,69 @@ +local Element = require "lib.simple_ui.element" + + +--- @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 + +return function(values) return barElement:new(values) end diff --git a/lib/simple_ui/level/bottom_bars.lua b/lib/simple_ui/level/bottom_bars.lua new file mode 100644 index 0000000..d7e3a91 --- /dev/null +++ b/lib/simple_ui/level/bottom_bars.lua @@ -0,0 +1,86 @@ +local Element = require "lib.simple_ui.element" +local Rect = require "lib.simple_ui.rect" +local Color = require "lib.simple_ui.color" +local Bar = require "lib.simple_ui.level.bar" + +--- @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 = + Bar { + 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 = + Bar { + 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 + +return bottomBars.new diff --git a/lib/simple_ui/level/cpanel.lua b/lib/simple_ui/level/cpanel.lua new file mode 100644 index 0000000..93ebb53 --- /dev/null +++ b/lib/simple_ui/level/cpanel.lua @@ -0,0 +1,118 @@ +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 Bars = require "lib.simple_ui.level.bottom_bars" + + +--- @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 = Bars(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 + +return characterPanel.new diff --git a/lib/simple_ui/level/layout.lua b/lib/simple_ui/level/layout.lua index 1ee1ae3..7a3280c 100644 --- a/lib/simple_ui/level/layout.lua +++ b/lib/simple_ui/level/layout.lua @@ -1,270 +1,10 @@ -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" +local CPanel = require "lib.simple_ui.level.cpanel" ---- @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 = CPanel(cid) self.characterPanel:show() elseif Tree.level.selector:deselected() then self.characterPanel:hide() diff --git a/lib/simple_ui/level/skill_row.lua b/lib/simple_ui/level/skill_row.lua index 1f0f039..6f7ddc7 100644 --- a/lib/simple_ui/level/skill_row.lua +++ b/lib/simple_ui/level/skill_row.lua @@ -179,8 +179,6 @@ function skillRow:draw() end love.graphics.setColor(1, 1, 1) - -- love.graphics.setCanvas(oldCanvas) - -- love.graphics.draw(c) end return skillRow.new