Compare commits
3 Commits
a8c188b24e
...
14225002e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 14225002e2 | |||
| 21dbf99435 | |||
| 72eb93baf7 |
@ -1,5 +1,6 @@
|
|||||||
--- @class Selector
|
--- @class Selector
|
||||||
--- @field id Id | nil
|
--- @field id Id | nil
|
||||||
|
--- @field lastId Id | nil
|
||||||
--- @field locked boolean
|
--- @field locked boolean
|
||||||
local selector = {}
|
local selector = {}
|
||||||
selector.__index = selector
|
selector.__index = selector
|
||||||
@ -15,6 +16,7 @@ function selector:select(characterId)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function selector:update(dt)
|
function selector:update(dt)
|
||||||
|
self.lastId = self.id
|
||||||
if self.locked or not Tree.controls:isJustPressed("select") then return end
|
if self.locked or not Tree.controls:isJustPressed("select") then return end
|
||||||
|
|
||||||
local mousePosition = Tree.level.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor()
|
local mousePosition = Tree.level.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor()
|
||||||
@ -53,6 +55,16 @@ function selector:unlock()
|
|||||||
self.locked = false
|
self.locked = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- If a character was selected during this tick, returns its Id
|
||||||
|
function selector:selected()
|
||||||
|
if self.id and self.id ~= self.lastId then return self.id end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- If a character was **de**selected during this tick, returns its Id
|
||||||
|
function selector:deselected()
|
||||||
|
if not self.id and self.lastId then return self.lastId end
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
new = new
|
new = new
|
||||||
}
|
}
|
||||||
|
|||||||
189
lib/simple_ui/level_layout.lua
Normal file
189
lib/simple_ui/level_layout.lua
Normal file
@ -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
|
||||||
@ -10,11 +10,13 @@
|
|||||||
local AnimationNode = require "lib.animation_node"
|
local AnimationNode = require "lib.animation_node"
|
||||||
|
|
||||||
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
||||||
|
--- @field tag string
|
||||||
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
|
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
|
||||||
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
|
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
|
||||||
--- @field cast fun(self: Spell, caster: Character, target: Vec3): boolean Вызывается в момент каста, изменяет мир. Возвращает bool в зависимости от того, получилось ли скастовать
|
--- @field cast fun(self: Spell, caster: Character, target: Vec3): boolean Вызывается в момент каста, изменяет мир. Возвращает bool в зависимости от того, получилось ли скастовать
|
||||||
local spell = {}
|
local spell = {}
|
||||||
spell.__index = spell
|
spell.__index = spell
|
||||||
|
spell.tag = "base"
|
||||||
|
|
||||||
function spell:update(caster, dt) end
|
function spell:update(caster, dt) end
|
||||||
|
|
||||||
@ -26,6 +28,7 @@ local walk = setmetatable({
|
|||||||
--- @type Deque
|
--- @type Deque
|
||||||
path = nil
|
path = nil
|
||||||
}, spell)
|
}, spell)
|
||||||
|
walk.tag = "dev_move"
|
||||||
|
|
||||||
function walk:cast(caster, target)
|
function walk:cast(caster, target)
|
||||||
if not caster:try(Tree.behaviors.stats, function(stats)
|
if not caster:try(Tree.behaviors.stats, function(stats)
|
||||||
@ -73,6 +76,7 @@ function walk:draw()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local regenerateMana = setmetatable({}, spell)
|
local regenerateMana = setmetatable({}, spell)
|
||||||
|
regenerateMana.tag = "dev_mana"
|
||||||
|
|
||||||
function regenerateMana:cast(caster, target)
|
function regenerateMana:cast(caster, target)
|
||||||
caster:try(Tree.behaviors.stats, function(stats)
|
caster:try(Tree.behaviors.stats, function(stats)
|
||||||
@ -92,6 +96,7 @@ function regenerateMana:cast(caster, target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local attack = setmetatable({}, spell)
|
local attack = setmetatable({}, spell)
|
||||||
|
attack.tag = "dev_attack"
|
||||||
|
|
||||||
function attack:cast(caster, target)
|
function attack:cast(caster, target)
|
||||||
if caster:try(Tree.behaviors.map, function(map)
|
if caster:try(Tree.behaviors.map, function(map)
|
||||||
@ -102,7 +107,7 @@ function attack:cast(caster, target)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
caster:try(Tree.behaviors.stats, function (stats)
|
caster:try(Tree.behaviors.stats, function(stats)
|
||||||
stats.mana = stats.mana - 2
|
stats.mana = stats.mana - 2
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
11
main.lua
11
main.lua
@ -3,7 +3,7 @@
|
|||||||
local character = require "lib/character/character"
|
local character = require "lib/character/character"
|
||||||
require "lib/tree"
|
require "lib/tree"
|
||||||
local layout = require "lib.ui.layout"
|
local layout = require "lib.ui.layout"
|
||||||
|
local testLayout = require "lib.simple_ui.level_layout"
|
||||||
|
|
||||||
function love.conf(t)
|
function love.conf(t)
|
||||||
t.console = true
|
t.console = true
|
||||||
@ -19,8 +19,9 @@ local lt = "0"
|
|||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
local t1 = love.timer.getTime()
|
local t1 = love.timer.getTime()
|
||||||
Tree.controls:poll()
|
Tree.controls:poll()
|
||||||
Widgets = layout:build()
|
testLayout:update(dt)
|
||||||
Widgets:update(dt) -- логика UI-слоя должна отработать раньше всех, потому что нужно перехватить жесты и не пустить их дальше
|
--Widgets = layout:build()
|
||||||
|
--Widgets:update(dt) -- логика UI-слоя должна отработать раньше всех, потому что нужно перехватить жесты и не пустить их дальше
|
||||||
Tree.panning:update(dt)
|
Tree.panning:update(dt)
|
||||||
Tree.level:update(dt)
|
Tree.level:update(dt)
|
||||||
Tree.controls:cache()
|
Tree.controls:cache()
|
||||||
@ -54,8 +55,8 @@ function love.draw()
|
|||||||
Tree.level:draw()
|
Tree.level:draw()
|
||||||
|
|
||||||
Tree.level.camera:detach()
|
Tree.level.camera:detach()
|
||||||
|
testLayout:draw()
|
||||||
Widgets:draw()
|
--Widgets:draw()
|
||||||
love.graphics.setColor(1, 1, 1)
|
love.graphics.setColor(1, 1, 1)
|
||||||
|
|
||||||
local stats = "fps: " .. love.timer.getFPS() .. " lt: " .. lt .. " dt: " .. dt
|
local stats = "fps: " .. love.timer.getFPS() .. " lt: " .. lt .. " dt: " .. dt
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user