diff --git a/lib/simple_ui/level_layout.lua b/lib/simple_ui/level_layout.lua new file mode 100644 index 0000000..fc0249a --- /dev/null +++ b/lib/simple_ui/level_layout.lua @@ -0,0 +1,189 @@ +local icons = require("lib.utils.sprite_atlas").load(Tree.assets.files.dev_icons) +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 + +local function easeIn(x) + return x * x * x +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 < 300 then + self.showT = self.showT + dt * 1000 + self.y = 10 * easeIn(-1 + self.showT / 300) + 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 = self.showT / 300 + + 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 children _SkillButton[] +local skillRow = setmetatable({}, uiElement) +skillRow.__index = skillRow + +--- @param characterId Id +function skillRow.new(characterId) + local t = { + characterId = characterId, + children = {} + } + 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 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 setmetatable(t, skillRow) +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 diff --git a/main.lua b/main.lua index 9c740fa..4ec8c8c 100644 --- a/main.lua +++ b/main.lua @@ -3,7 +3,7 @@ local character = require "lib/character/character" require "lib/tree" local layout = require "lib.ui.layout" - +local testLayout = require "lib.simple_ui.level_layout" function love.conf(t) t.console = true @@ -19,8 +19,9 @@ local lt = "0" function love.update(dt) local t1 = love.timer.getTime() Tree.controls:poll() - Widgets = layout:build() - Widgets:update(dt) -- логика UI-слоя должна отработать раньше всех, потому что нужно перехватить жесты и не пустить их дальше + testLayout:update(dt) + --Widgets = layout:build() + --Widgets:update(dt) -- логика UI-слоя должна отработать раньше всех, потому что нужно перехватить жесты и не пустить их дальше Tree.panning:update(dt) Tree.level:update(dt) Tree.controls:cache() @@ -54,8 +55,8 @@ function love.draw() Tree.level:draw() Tree.level.camera:detach() - - Widgets:draw() + testLayout:draw() + --Widgets:draw() love.graphics.setColor(1, 1, 1) local stats = "fps: " .. love.timer.getFPS() .. " lt: " .. lt .. " dt: " .. dt