heroes-of-nerevelon/lib/simple_ui/level_layout.lua

193 lines
5.4 KiB
Lua

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"
--- @class UIElement
local uiElement = {
x = 0,
y = 0,
width = 0,
height = 0,
transform = love.math.newTransform()
}
uiElement.__index = uiElement
function uiElement:update(dt) end
function uiElement:draw() end
function uiElement:show(animationNode) end
function uiElement:hide(animationNode) end
function uiElement:hitTest(screenX, screenY)
local lx, ly = self.transform:inverseTransformPoint(screenX, screenY)
local belongs = function(value, lower, upper)
return value >= lower and value < upper
end
return belongs(lx, self.x, self.x + self.width) and belongs(ly, self.y, self.y + self.height)
end
--- @class SkillButton : UIElement
--- @field showNode AnimationNode?
--- @field showT number
--- @field hovered boolean
--- @field selected boolean
--- @field onClick function?
--- @field icon string
local skillButton = setmetatable({}, uiElement)
skillButton.__index = skillButton
function skillButton.new(icon)
return setmetatable({
icon = icon
}, skillButton)
end
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
if self.showT < 1 then
self.showT = self.showT + dt * 1 / 0.3 -- в знаменателе продолжительность анимации в секундах
self.y = 10 * easing.easeInCubic(1 - self.showT)
return
end
if self.showNode then
self.showNode:finish()
self.showNode = nil
end
end
--- @param animationNode AnimationNode
function skillButton:show(animationNode)
if self.showNode then
self.showNode:finish()
end
self.showT = 0
self.showNode = animationNode
end
function skillButton:draw()
love.graphics.push()
love.graphics.applyTransform(self.transform)
local alpha = easing.easeInSine(self.showT)
if self.selected then
love.graphics.setColor(0.3, 1, 0.3, alpha)
elseif self.hovered then
love.graphics.setColor(0.7, 1, 0.7, alpha)
else
love.graphics.setColor(1, 1, 1, alpha)
end
love.graphics.translate(0, self.y)
love.graphics.draw(icons.atlas, icons:pickQuad(self.icon))
love.graphics.setColor(1, 1, 1)
love.graphics.pop()
end
--- @class SkillRow : UIElement
--- @field characterId Id
--- @field selected SkillButton?
--- @field children SkillButton[]
local skillRow = setmetatable({}, uiElement)
skillRow.__index = skillRow
--- @param characterId Id
function skillRow.new(characterId)
local t = {
characterId = characterId,
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(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()
for _, skb in ipairs(self.children) do
AnimationNode {
function(animationNode)
skb:show(animationNode)
end,
}:run()
end
end
function skillRow:update(dt)
local iconSize = icons.tileSize
local scale = (64 / iconSize)
local screenW, screenH = love.graphics.getDimensions()
local padding = 8
local count = #self.children
self.width, self.height, self.x, self.y = count * icons.tileSize + (count - 1) * padding, iconSize, 0,
0 -- в локальных координатах
self.transform = love.math.newTransform():translate(screenW / 2,
screenH - 16):scale(scale, scale):translate(-self.width / 2, -iconSize)
for i, skb in ipairs(self.children) do
skb.width, skb.height = iconSize, iconSize
skb.transform = self.transform:clone():translate(self.x + (i - 1) * iconSize +
(i - 1) *
padding, -- левый край ряда + размер предыдущих иконок + размер предыдущих отступов
0 -- высота не меняется
)
skb:update(dt)
end
end
function skillRow:draw()
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)
self.skillRow:show()
end
if Tree.level.selector:deselected() then self.skillRow = nil 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