spells framework API & walk implementation

This commit is contained in:
PeaAshMeter 2025-09-05 15:44:02 +03:00
parent bab4b006ca
commit dd1d64506d
8 changed files with 78 additions and 46 deletions

View File

@ -1,4 +1,3 @@
local anim8 = require "lib.utils.anim8"
require 'lib.utils.vec3' require 'lib.utils.vec3'
@ -12,7 +11,8 @@ local characterId = 1
--- @field info Info --- @field info Info
--- @field graphics Graphics --- @field graphics Graphics
--- @field logic Logic --- @field logic Logic
--- @field cast boolean @todo тестовое поле, которое отвечает за то, кастуется ли перемещение в данный момент --- @field spellbook Spell[] собственный набор спеллов персонажа
--- @field cast Spell | nil ссылка на активный спелл из спеллбука
local character = {} local character = {}
character.__index = character character.__index = character
@ -45,11 +45,13 @@ end
function character:update(dt) function character:update(dt)
self.logic:update(dt) self.logic:update(dt)
if self.cast then self.cast:update(self, dt) end
self.graphics:update(dt) self.graphics:update(dt)
end end
function character:draw() function character:draw()
self.graphics:draw() self.graphics:draw()
if self.cast then self.cast:draw() end
end end
return { spawn = spawn } return { spawn = spawn }

View File

@ -61,8 +61,6 @@ function logic:update(dt)
self.mapLogic.displayedPosition = utils.lerp(self.mapLogic.position, self.mapLogic.runTarget, fraction) -- линейный интерполятор self.mapLogic.displayedPosition = utils.lerp(self.mapLogic.position, self.mapLogic.runTarget, fraction) -- линейный интерполятор
end end
end end
Tree.level.characterGrid:add(self.id)
end end
return { new = new } return { new = new }

View File

@ -1,3 +1,4 @@
local utils = require "lib.utils.utils"
--- @class CharacterGrid : Grid --- @class CharacterGrid : Grid
--- @field __grid {string: Id|nil} --- @field __grid {string: Id|nil}
local grid = setmetatable({}, require "lib.level.grid.base") local grid = setmetatable({}, require "lib.level.grid.base")
@ -20,6 +21,16 @@ function grid:add(id)
end end
end end
--- fills the grid with the actual data
---
--- should be called as early as possible during every tick
function grid:reload()
self:reset()
utils.each(Tree.level.characters, function(c)
self:add(c.id)
end)
end
--- Generates an empty grid --- Generates an empty grid
--- @return CharacterGrid --- @return CharacterGrid
local function new() local function new()

View File

@ -27,27 +27,11 @@ local function new(type, template)
}, level) }, level)
end end
local mposCache = nil
function level:update(dt) function level:update(dt)
self.characterGrid:reset() self.characterGrid:reload()
utils.each(self.characters, function(el) utils.each(self.characters, function(el)
el:update(dt) el:update(dt)
end) end)
if self.characters[self.selector.id] then
local charPos = self.characters[self.selector.id].logic.mapLogic.position:floor()
--- @type Vec3
local mpos = self.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor()
-- ДУШИ здесь больше нет, приводите рыжих обратно
if not path or tostring(mpos) ~= mposCache then
path = require "lib.pathfinder" (charPos, mpos) -- ?????
end
mposCache = tostring(mpos)
-- path = (require "lib.pathfinder")(charPos, mpos)
else
mposCache = nil
end
self.camera:update(dt) self.camera:update(dt)
self.selector:update(dt) self.selector:update(dt)
@ -55,16 +39,6 @@ end
function level:draw() function level:draw()
self.tileGrid:draw() self.tileGrid:draw()
--- Это отрисовка пути персонажа к мышке
if self.selector.id and self.characters[self.selector.id].cast and path then
love.graphics.setColor(0.6, 0.75, 0.5)
for p in path:values() do
love.graphics.circle("fill", p.x + 0.45, p.y + 0.45, 0.1)
end
love.graphics.setColor(1, 1, 1)
end
utils.each(self.characters, function(el) utils.each(self.characters, function(el)
el:draw() el:draw()
end) end)

View File

@ -12,10 +12,6 @@ function selector:select(characterId)
self.id = characterId self.id = characterId
end end
-- function selector:deselect()
-- self.id = nil
-- end
--- TODO: сделать обработчик селектора --- TODO: сделать обработчик селектора
function selector:update(dt) function selector:update(dt)
if not Tree.controls:isJustPressed("select") then return end if not Tree.controls:isJustPressed("select") then return end
@ -27,16 +23,11 @@ function selector:update(dt)
end end
local characterId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y }) local characterId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y })
if not characterId and self.id then -- временная обработка события "побежать к точке" if not characterId and self.id then -- Когда кликаем по тайлу за персонажа в режиме каста, кастуем спелл
local char = Tree.level.characters[self.id] local char = Tree.level.characters[self.id]
if char.cast then if char.cast then
char.cast = false char.cast:cast(char, mousePosition)
local charPos = char.logic.mapLogic.position char.cast = nil
local path = (require "lib.pathfinder")(charPos, mousePosition)
path:pop_front()
print("Following path: ")
for p in path:values() do print(p) end
char:followPath(path)
end end
end end
self:select(characterId) self:select(characterId)

54
lib/spellbook.lua Normal file
View File

@ -0,0 +1,54 @@
--- @class Spell
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
--- @field cast fun(self: Spell, caster: Character, target: Vec3): nil Вызывается в момент каста, изменяет мир
local spell = {}
spell.__index = spell
local walk = setmetatable({
--- @type Deque
path = nil
}, spell)
function walk:cast(caster, target)
caster.cast = nil
local path = self.path
path:pop_front()
print("Following path: ")
for p in path:values() do print(p) end
caster:followPath(path)
end
function walk:update(caster, dt)
local charPos = caster.logic.mapLogic.position:floor()
--- @type Vec3
local mpos = Tree.level.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor()
self.path = require "lib.pathfinder" (charPos, mpos)
end
function walk:draw()
if not self.path then return end
--- Это отрисовка пути персонажа к мышке
love.graphics.setColor(0.6, 0.75, 0.5)
for p in self.path:values() do
love.graphics.circle("fill", p.x + 0.45, p.y + 0.45, 0.1)
end
love.graphics.setColor(1, 1, 1)
end
----------------------------------------
local spellbook = {
walk = walk
}
--- Создает новый спеллбук с уникальными спеллами (а не ссылками на шаблоны)
--- @param list Spell[]
function spellbook.of(list)
local spb = {}
for i, sp in ipairs(list) do
spb[i] = setmetatable({}, { __index = sp })
end
return spb
end
return spellbook

View File

@ -4,16 +4,16 @@ local ui = require "lib.ui.core"
--- @class SkillButton : Rectangle --- @class SkillButton : Rectangle
--- @field owner Character --- @field owner Character
--- @field spellId number
local SkillButton = ui.Rectangle { local SkillButton = ui.Rectangle {
size = Vec3 { 100, 100 }, size = Vec3 { 100, 100 },
color = { 1, 0, 0 }, color = { 1, 0, 0 },
owner = nil
} }
function SkillButton:update(dt) function SkillButton:update(dt)
ui.Rectangle.update(self, dt) ui.Rectangle.update(self, dt)
self.color = self.owner.cast and { 0, 1, 0 } or { 1, 0, 0 } self.color = self.owner.cast and { 0, 1, 0 } or { 1, 0, 0 }
self:onTap(function() self:onTap(function()
self.owner.cast = not self.owner.cast self.owner.cast = self.owner.cast and nil or self.owner.spellbook[self.spellId]
end) end)
end end
@ -34,7 +34,7 @@ function layout:build()
local r = local r =
ui.Row { ui.Row {
children = { children = {
setmetatable({ owner = Tree.level.characters[id] }, { __index = SkillButton }) setmetatable({ owner = Tree.level.characters[id], spellId = 1 }, { __index = SkillButton })
} }
} }
skillRows[id] = r skillRows[id] = r

View File

@ -3,6 +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 spellbook = require "lib.spellbook"
function love.conf(t) function love.conf(t)
t.console = true t.console = true
@ -15,6 +16,7 @@ function love.load()
local c = character.spawn("Hero", "warrior", Tree.assets.files.sprites.character) local c = character.spawn("Hero", "warrior", Tree.assets.files.sprites.character)
c.logic.mapLogic.position = Vec3 { x, y } c.logic.mapLogic.position = Vec3 { x, y }
c.logic.mapLogic.displayedPosition = Vec3 { x, y } c.logic.mapLogic.displayedPosition = Vec3 { x, y }
c.spellbook = spellbook.of { spellbook.walk }
end end
end end
end end