diff --git a/lib/simple_ui/level/layout.lua b/lib/simple_ui/level/layout.lua index 623afc5..38c5707 100644 --- a/lib/simple_ui/level/layout.lua +++ b/lib/simple_ui/level/layout.lua @@ -1,179 +1,18 @@ -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" local Element = require "lib.simple_ui.element" local Rect = require "lib.simple_ui.rect" +local SkillRow = require "lib.simple_ui.level.skill_row" ---- @class SkillButton : UIElement ---- @field hovered boolean ---- @field selected boolean ---- @field onClick function? ---- @field icon string -local skillButton = setmetatable({}, Element) -skillButton.__index = skillButton - -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 -end - -function skillButton:draw() - love.graphics.push() - love.graphics.applyTransform(self.transform) - - local r, g, b, a = love.graphics.getColor() - - if self.selected then - love.graphics.setColor(0.3, 1, 0.3, a) - elseif self.hovered then - love.graphics.setColor(0.7, 1, 0.7, a) - else - love.graphics.setColor(1, 1, 1, a) - end - - love.graphics.translate(0, self.bounds.y) - love.graphics.draw(icons.atlas, icons:pickQuad(self.icon)) - love.graphics.pop() -end - ---- @class SkillRow : UIElement ---- @field characterId Id ---- @field selected SkillButton? ---- @field showNode AnimationNode? ---- @field showT number ---- @field hideNode AnimationNode? ---- @field hideT number ---- @field state "show" | "hide" ---- @field children SkillButton[] -local skillRow = setmetatable({}, Element) -skillRow.__index = skillRow - ---- @param characterId Id -function skillRow.new(characterId) - local t = { - characterId = characterId, - showT = 0, - hideT = 0, - state = "show", - 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 { icon = 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(animationNode) - self.state = "show" - if self.showNode then self.showNode:finish() end - self.showT = 0 - self.showNode = animationNode -end - -function skillRow:hide(animationNode) - self.state = "hide" - if self.hideNode then self.hideNode:finish() end - self.hideT = 0 - self.hideNode = animationNode -end - -function skillRow:update(dt) - if self.showT < 1 and self.state == "show" then - self.showT = self.showT + dt / 0.3 -- в знаменателе продолжительность анимации в секундах - elseif self.showNode then - self.showNode:finish() - self.showNode = nil - end - - if self.hideT < 1 and self.state == "hide" then - self.hideT = self.hideT + dt / 0.3 - elseif self.hideNode then - self.hideNode:finish() - self.hideNode = nil - end - - local iconSize = icons.tileSize - local scale = (64 / iconSize) - local screenW, screenH = love.graphics.getDimensions() - local padding = 8 - local count = #self.children - - self.bounds = Rect { - width = count * icons.tileSize + (count - 1) * padding, - height = iconSize, - y = 10 * easing.easeInCubic(1 - self.showT) - } - self.transform = love.math.newTransform():translate(screenW / 2, - screenH - 16):scale(scale, scale):translate(-self.bounds.width / 2, -iconSize) - - for i, skb in ipairs(self.children) do - skb.bounds = Rect { height = iconSize, width = iconSize } - skb.transform = self.transform:clone():translate(self.bounds.x + (i - 1) * iconSize + - (i - 1) * - padding, -- левый край ряда + размер предыдущих иконок + размер предыдущих отступов - self.bounds.y -- высота не меняется - ) - skb:update(dt) - end -end - -function skillRow:draw() - local alpha = self.state == "show" and easing.easeInSine(self.showT) or 1 - easing.easeInSine(self.hideT) - love.graphics.setColor(1, 1, 1, alpha) - 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) - AnimationNode { - function(animationNode) - self.skillRow:show(animationNode) - end, - }:run() - end - if Tree.level.selector:deselected() then - AnimationNode { - function(animationNode) - self.skillRow:hide(animationNode) - end, - onEnd = function() - self.skillRow = nil - end - }:run() + self.skillRow = SkillRow(cid) + self.skillRow:show() + elseif Tree.level.selector:deselected() then + self.skillRow:hide() end if self.skillRow then self.skillRow:update(dt) end end diff --git a/lib/simple_ui/level/skill_row.lua b/lib/simple_ui/level/skill_row.lua new file mode 100644 index 0000000..d8219f2 --- /dev/null +++ b/lib/simple_ui/level/skill_row.lua @@ -0,0 +1,161 @@ +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" +local Element = require "lib.simple_ui.element" +local Rect = require "lib.simple_ui.rect" + +--- @class SkillButton : UIElement +--- @field hovered boolean +--- @field selected boolean +--- @field onClick function? +--- @field icon string +local skillButton = setmetatable({}, Element) +skillButton.__index = skillButton + +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 +end + +function skillButton:draw() + love.graphics.push() + love.graphics.applyTransform(self.transform) + + local r, g, b, a = love.graphics.getColor() + + if self.selected then + love.graphics.setColor(0.3, 1, 0.3, a) + elseif self.hovered then + love.graphics.setColor(0.7, 1, 0.7, a) + else + love.graphics.setColor(1, 1, 1, a) + end + + love.graphics.translate(0, self.bounds.y) + love.graphics.draw(icons.atlas, icons:pickQuad(self.icon)) + love.graphics.pop() +end + +-------------------------------------------------------------------------------- + +--- @class SkillRow : UIElement +--- @field characterId Id +--- @field selected SkillButton? +--- @field animationNode AnimationNode +--- @field state "show" | "idle" | "hide" +--- @field children SkillButton[] +local skillRow = setmetatable({}, Element) +skillRow.__index = skillRow + +--- @param characterId Id +--- @return SkillRow +function skillRow.new(characterId) + local t = { + characterId = characterId, + state = "show", + 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 { icon = 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() + 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) + if self.animationNode then self.animationNode:update(dt) end + + local iconSize = icons.tileSize + local scale = (64 / iconSize) + local screenW, screenH = love.graphics.getDimensions() + local padding = 8 + local count = #self.children + + self.bounds = Rect { + width = count * icons.tileSize + (count - 1) * padding, + height = iconSize, + y = self.state == "show" and 10 * (1 - self.animationNode:getValue()) or 0 + } + self.transform = love.math.newTransform():translate(screenW / 2, + screenH - 16):scale(scale, scale):translate(-self.bounds.width / 2, -iconSize) + + for i, skb in ipairs(self.children) do + skb.bounds = Rect { height = iconSize, width = iconSize } + skb.transform = self.transform:clone():translate(self.bounds.x + (i - 1) * iconSize + + (i - 1) * + padding, -- левый край ряда + размер предыдущих иконок + размер предыдущих отступов + self.bounds.y -- высота не меняется + ) + skb:update(dt) + end +end + +function skillRow:draw() + 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) + for _, skb in ipairs(self.children) do + skb:draw() + end +end + +return skillRow.new