hp-bar-the-dumb-way #22

Merged
PeaAshMeter merged 7 commits from hp-bar-the-dumb-way into main 2025-12-07 20:35:58 +03:00
4 changed files with 198 additions and 87 deletions
Showing only changes of commit bc730ef48c - Show all commits

20
lib/simple_ui/color.lua Normal file
View File

@ -0,0 +1,20 @@
--- @class Color
--- @field r number
--- @field g number
--- @field b number
--- @field a number
local color = {
r = 1,
g = 1,
b = 1,
a = 1
}
color.__index = color
--- @param rgba {r?: number, g?: number, b?: number, a?: number}
--- @return Color
function color.new(rgba)
return setmetatable(rgba, color)
end
return color.new

View File

@ -33,7 +33,7 @@ end
--- @return T --- @return T
function uiElement.new(self, values) function uiElement.new(self, values)
values.bounds = values.bounds or Rect {} values.bounds = values.bounds or Rect {}
values.overlayGradientMesh = values.overlayGradientMesh or uiElement.bounds; values.overlayGradientMesh = values.overlayGradientMesh or uiElement.overlayGradientMesh;
return setmetatable(values, self) return setmetatable(values, self)
end end

View File

@ -3,13 +3,19 @@ local AnimationNode = require "lib.animation_node"
local Element = require "lib.simple_ui.element" local Element = require "lib.simple_ui.element"
local Rect = require "lib.simple_ui.rect" local Rect = require "lib.simple_ui.rect"
local SkillRow = require "lib.simple_ui.level.skill_row" local SkillRow = require "lib.simple_ui.level.skill_row"
local Color = require "lib.simple_ui.color"
--- @class BarElement : UIElement --- @class BarElement : UIElement
--- @field getter fun() : number --- @field getter fun() : number
--- @field value number --- @field value number
--- @field maxValue number --- @field maxValue number
--- @field color Color
--- @field useDividers boolean
--- @field drawText boolean
local barElement = setmetatable({}, Element) local barElement = setmetatable({}, Element)
barElement.__index = barElement barElement.__index = barElement
barElement.useDividers = false
barElement.drawText = false
function barElement:update(dt) function barElement:update(dt)
local val = self.getter() local val = self.getter()
@ -17,20 +23,54 @@ function barElement:update(dt)
end end
function barElement:draw() function barElement:draw()
love.graphics.push() local valueWidth = self.bounds.width * self.value / self.maxValue
love.graphics.applyTransform(self.transform) local emptyWidth = self.bounds.width - valueWidth
love.graphics.setColor(1, 1, 1) --- шум
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.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width * self.value / self.maxValue, --- закраска пустой части
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) 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) love.graphics.setColor(1, 1, 1)
love.graphics.pop() --- текст поверх
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:drawGradientOverlay()
end end
--- @class BottomBars : UIElement --- @class BottomBars : UIElement
--- @field children BarElement[] --- @field hpBar BarElement
--- @field manaBar BarElement
local bottomBars = setmetatable({}, Element) local bottomBars = setmetatable({}, Element)
bottomBars.__index = bottomBars; bottomBars.__index = bottomBars;
@ -38,7 +78,7 @@ bottomBars.__index = bottomBars;
function bottomBars.new(cid) function bottomBars.new(cid)
local t = setmetatable({}, bottomBars) local t = setmetatable({}, bottomBars)
t.children = { t.hpBar =
barElement:new { barElement:new {
getter = function() getter = function()
local char = Tree.level.characters[cid] local char = Tree.level.characters[cid]
@ -46,9 +86,12 @@ function bottomBars.new(cid)
return stats.hp or 0 return stats.hp or 0
end) end)
end, end,
color = Color { r = 130 / 255, g = 8 / 255, b = 8 / 255 },
drawText = true,
maxValue = 20 maxValue = 20
}, }
t.manaBar =
barElement:new { barElement:new {
getter = function() getter = function()
local char = Tree.level.characters[cid] local char = Tree.level.characters[cid]
@ -56,63 +99,154 @@ function bottomBars.new(cid)
return stats.mana or 0 return stats.mana or 0
end) end)
end, end,
color = Color { r = 51 / 255, g = 105 / 255, b = 30 / 255 },
useDividers = true,
maxValue = 10 maxValue = 10
} }
}
return t return t
end end
function bottomBars:update(dt) function bottomBars:update(dt)
local padding = 8 local height = 14
local screenW, screenH = love.graphics.getDimensions() local margin = 2
self.bounds = Rect {
height = 40 + 2 * padding, self.bounds.height = height
width = screenW * 0.25, self.bounds.y = self.bounds.y - height
y = screenH - 64 - 1 - 2 * padding - 40
self.hpBar.bounds = Rect {
width = -2 * margin + self.bounds.width / 2,
height = height,
x = self.bounds.x + margin,
y = self.bounds.y
} }
self.transform = love.math.newTransform():translate(-self.bounds.width / 2 + screenW / 2, self.bounds.y) self.manaBar.bounds = Rect {
width = -2 * margin + self.bounds.width / 2,
for i = 1, #self.children do height = height,
self.children[i].bounds = Rect { x = self.bounds.x + margin + self.bounds.width / 2,
width = screenW * 0.25, y = self.bounds.y
height = 20
} }
self.children[i].transform = self.transform:clone():translate(0,
self.children[i].bounds.height * (i - 1) + (i - 1) * padding)
end
self.hpBar:update(dt)
for _, el in ipairs(self.children) do self.manaBar:update(dt)
el:update(dt)
end
end end
function bottomBars:draw() function bottomBars:draw()
for _, el in ipairs(self.children) do -- шум
el:draw() love.graphics.setShader(Tree.assets.files.shaders.soft_uniform_noise)
end 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 end
local c = love.graphics.newCanvas(1280, 720) --- @TODO: выставлять канвасу правильный размер в зависимости от окна
--- @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
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
}
end
function characterPanel:draw()
love.graphics.setCanvas(c)
love.graphics.clear()
self.skillRow:draw()
self.bars:draw()
self:drawBorder("outer")
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.setCanvas()
love.graphics.setColor(1, 1, 1, alpha)
love.graphics.draw(c)
love.graphics.setColor(1, 1, 1)
end
-----------------------------------
local layout = {} local layout = {}
function layout:update(dt) function layout:update(dt)
local cid = Tree.level.selector:selected() local cid = Tree.level.selector:selected()
if cid then if cid then
self.skillRow = SkillRow(cid) self.characterPanel = characterPanel.new(cid)
self.skillRow:show() self.characterPanel:show()
-- self.bottomBars = bottomBars.new(cid)
elseif Tree.level.selector:deselected() then elseif Tree.level.selector:deselected() then
self.skillRow:hide() self.characterPanel:hide()
-- self.bottomBars = nil
end end
if self.skillRow then self.skillRow:update(dt) end if self.characterPanel then self.characterPanel:update(dt) end
-- if self.bottomBars then self.bottomBars:update(dt) end
end end
function layout:draw() function layout:draw()
if self.skillRow then self.skillRow:draw() end if self.characterPanel then self.characterPanel:draw() end
-- if self.bottomBars then self.bottomBars:draw() end
end end
return layout return layout

View File

@ -63,8 +63,6 @@ end
--- @class SkillRow : UIElement --- @class SkillRow : UIElement
--- @field characterId Id --- @field characterId Id
--- @field selected SkillButton? --- @field selected SkillButton?
--- @field animationNode AnimationNode
--- @field state "show" | "idle" | "hide"
--- @field children SkillButton[] --- @field children SkillButton[]
local skillRow = setmetatable({}, Element) local skillRow = setmetatable({}, Element)
skillRow.__index = skillRow skillRow.__index = skillRow
@ -74,7 +72,6 @@ skillRow.__index = skillRow
function skillRow.new(characterId) function skillRow.new(characterId)
local t = { local t = {
characterId = characterId, characterId = characterId,
state = "show",
children = {} children = {}
} }
@ -85,7 +82,6 @@ function skillRow.new(characterId)
for i, spell in ipairs(behavior.spellbook) do for i, spell in ipairs(behavior.spellbook) do
local skb = skillButton:new { icon = spell.tag } local skb = skillButton:new { icon = spell.tag }
skb.onClick = function() skb.onClick = function()
if t.state ~= "idle" then return end
skb.selected = not skb.selected skb.selected = not skb.selected
if t.selected then t.selected.selected = false end if t.selected then t.selected.selected = false end
t.selected = skb t.selected = skb
@ -109,36 +105,7 @@ function skillRow.new(characterId)
return t return t
end 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) function skillRow:update(dt)
if self.animationNode then self.animationNode:update(dt) end
local iconSize = 64 * UI_SCALE local iconSize = 64 * UI_SCALE
local screenW, screenH = love.graphics.getDimensions() local screenW, screenH = love.graphics.getDimensions()
local padding, margin = 8, 4 local padding, margin = 8, 4
@ -161,6 +128,7 @@ end
local c = love.graphics.newCanvas(1280, 720) --- @TODO: выставлять канвасу правильный размер в зависимости от окна local c = love.graphics.newCanvas(1280, 720) --- @TODO: выставлять канвасу правильный размер в зависимости от окна
function skillRow:draw() function skillRow:draw()
local oldCanvas = love.graphics.getCanvas()
love.graphics.setCanvas({ c, stencil = true }) love.graphics.setCanvas({ c, stencil = true })
love.graphics.clear() love.graphics.clear()
love.graphics.setColor(1, 1, 1) love.graphics.setColor(1, 1, 1)
@ -195,24 +163,13 @@ function skillRow:draw()
love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height)
love.graphics.setBlendMode("alpha") love.graphics.setBlendMode("alpha")
--граница
self:drawBorder("outer")
love.graphics.setStencilTest() love.graphics.setStencilTest()
--затенение --затенение
self:drawGradientOverlay() self:drawGradientOverlay()
love.graphics.setColor(1, 1, 1) love.graphics.setColor(1, 1, 1)
love.graphics.setCanvas() love.graphics.setCanvas(oldCanvas)
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)
love.graphics.draw(c) love.graphics.draw(c)
end end