From dd1d64506d4f1060672e1bcfeaf8c5a7aa067fa0 Mon Sep 17 00:00:00 2001 From: PeaAshMeter Date: Fri, 5 Sep 2025 15:44:02 +0300 Subject: [PATCH] spells framework API & walk implementation --- lib/character/character.lua | 6 ++-- lib/character/logic.lua | 2 -- lib/level/grid/character_grid.lua | 11 +++++++ lib/level/level.lua | 28 +--------------- lib/level/selector.lua | 15 ++------- lib/spellbook.lua | 54 +++++++++++++++++++++++++++++++ lib/ui/layout.lua | 6 ++-- main.lua | 2 ++ 8 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 lib/spellbook.lua diff --git a/lib/character/character.lua b/lib/character/character.lua index 79e6f83..fd44a80 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -1,4 +1,3 @@ -local anim8 = require "lib.utils.anim8" require 'lib.utils.vec3' @@ -12,7 +11,8 @@ local characterId = 1 --- @field info Info --- @field graphics Graphics --- @field logic Logic ---- @field cast boolean @todo тестовое поле, которое отвечает за то, кастуется ли перемещение в данный момент +--- @field spellbook Spell[] собственный набор спеллов персонажа +--- @field cast Spell | nil ссылка на активный спелл из спеллбука local character = {} character.__index = character @@ -45,11 +45,13 @@ end function character:update(dt) self.logic:update(dt) + if self.cast then self.cast:update(self, dt) end self.graphics:update(dt) end function character:draw() self.graphics:draw() + if self.cast then self.cast:draw() end end return { spawn = spawn } diff --git a/lib/character/logic.lua b/lib/character/logic.lua index 0eb966f..34cf4a6 100644 --- a/lib/character/logic.lua +++ b/lib/character/logic.lua @@ -61,8 +61,6 @@ function logic:update(dt) self.mapLogic.displayedPosition = utils.lerp(self.mapLogic.position, self.mapLogic.runTarget, fraction) -- линейный интерполятор end end - - Tree.level.characterGrid:add(self.id) end return { new = new } diff --git a/lib/level/grid/character_grid.lua b/lib/level/grid/character_grid.lua index e217982..1c51d2c 100644 --- a/lib/level/grid/character_grid.lua +++ b/lib/level/grid/character_grid.lua @@ -1,3 +1,4 @@ +local utils = require "lib.utils.utils" --- @class CharacterGrid : Grid --- @field __grid {string: Id|nil} local grid = setmetatable({}, require "lib.level.grid.base") @@ -20,6 +21,16 @@ function grid:add(id) 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 --- @return CharacterGrid local function new() diff --git a/lib/level/level.lua b/lib/level/level.lua index e8c38a8..1c603ff 100644 --- a/lib/level/level.lua +++ b/lib/level/level.lua @@ -27,27 +27,11 @@ local function new(type, template) }, level) end -local mposCache = nil - function level:update(dt) - self.characterGrid:reset() + self.characterGrid:reload() utils.each(self.characters, function(el) el:update(dt) 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.selector:update(dt) @@ -55,16 +39,6 @@ end function level: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) el:draw() end) diff --git a/lib/level/selector.lua b/lib/level/selector.lua index 3d66af7..2814ea3 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -12,10 +12,6 @@ function selector:select(characterId) self.id = characterId end --- function selector:deselect() --- self.id = nil --- end - --- TODO: сделать обработчик селектора function selector:update(dt) if not Tree.controls:isJustPressed("select") then return end @@ -27,16 +23,11 @@ function selector:update(dt) end 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] if char.cast then - char.cast = false - local charPos = char.logic.mapLogic.position - 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) + char.cast:cast(char, mousePosition) + char.cast = nil end end self:select(characterId) diff --git a/lib/spellbook.lua b/lib/spellbook.lua new file mode 100644 index 0000000..40e8193 --- /dev/null +++ b/lib/spellbook.lua @@ -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 diff --git a/lib/ui/layout.lua b/lib/ui/layout.lua index 874adae..dcd5ea0 100644 --- a/lib/ui/layout.lua +++ b/lib/ui/layout.lua @@ -4,16 +4,16 @@ local ui = require "lib.ui.core" --- @class SkillButton : Rectangle --- @field owner Character +--- @field spellId number local SkillButton = ui.Rectangle { size = Vec3 { 100, 100 }, color = { 1, 0, 0 }, - owner = nil } function SkillButton:update(dt) ui.Rectangle.update(self, dt) self.color = self.owner.cast and { 0, 1, 0 } or { 1, 0, 0 } 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 @@ -34,7 +34,7 @@ function layout:build() local r = ui.Row { children = { - setmetatable({ owner = Tree.level.characters[id] }, { __index = SkillButton }) + setmetatable({ owner = Tree.level.characters[id], spellId = 1 }, { __index = SkillButton }) } } skillRows[id] = r diff --git a/main.lua b/main.lua index bc6a5b8..5b1ebcb 100644 --- a/main.lua +++ b/main.lua @@ -3,6 +3,7 @@ local character = require "lib/character/character" require "lib/tree" local layout = require "lib.ui.layout" +local spellbook = require "lib.spellbook" function love.conf(t) t.console = true @@ -15,6 +16,7 @@ function love.load() local c = character.spawn("Hero", "warrior", Tree.assets.files.sprites.character) c.logic.mapLogic.position = Vec3 { x, y } c.logic.mapLogic.displayedPosition = Vec3 { x, y } + c.spellbook = spellbook.of { spellbook.walk } end end end