diff --git a/assets/overlay_icons/atlas.png b/assets/overlay_icons/atlas.png index 8f186a6..1443205 100644 Binary files a/assets/overlay_icons/atlas.png and b/assets/overlay_icons/atlas.png differ diff --git a/assets/overlay_icons/manifest.lua b/assets/overlay_icons/manifest.lua index 498b26d..64b4b1b 100644 --- a/assets/overlay_icons/manifest.lua +++ b/assets/overlay_icons/manifest.lua @@ -2,4 +2,5 @@ return { tileSize = 32, ["dev_target"] = { 0 }, ["dev_path"] = { 1 }, + ["dev_path_closed"] = { 2 }, } diff --git a/lib/spell/spell.lua b/lib/spell/spell.lua index ee6e49c..5ef80f0 100644 --- a/lib/spell/spell.lua +++ b/lib/spell/spell.lua @@ -2,6 +2,7 @@ local Query = require "lib.spell.target_query" local targetTest = require "lib.spell.target_test" local task = require "lib.utils.task" local easing = require "lib.utils.easing" +local pf = require "lib.pathfinder" --- @alias SpellPreview "default" Подсветка возможных целей --- | "path" Подсветка пути до цели @@ -11,7 +12,7 @@ local easing = require "lib.utils.easing" --- @field baseCost integer Базовые затраты маны на каст --- @field baseCooldown integer Базовый кулдаун в ходах --- @field targetQuery SpellTargetQuery Селектор возможных целей ---- @field previewType SpellPreview Вид превью во время каста +--- @field targetType SpellPreview Вид превью во время каста --- @field distance? integer Сторона квадрата с центром в позиции кастера, в пределах которого должна находиться цель, либо отсутствие ограничения --- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла --- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире @@ -22,7 +23,7 @@ spell.tag = "spell_base" spell.baseCost = 1 spell.baseCooldown = 1 spell.targetQuery = Query(targetTest.any) -spell.previewType = "default" +spell.targetType = "default" --- Вызывается, когда игрок выбирает спелл на панели заклинаний @@ -34,7 +35,7 @@ function spell:onSelected(caster) end function spell:update(caster, dt) - if self.previewType == "path" then + if self.targetType == "path" then local charPos = caster:has(Tree.behaviors.positioned).position:floor() --- @type Vec3 local mpos = Tree.level.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor() @@ -49,31 +50,34 @@ end local icons = require("lib.utils.sprite_atlas").load(Tree.assets.files.overlay_icons) function spell:draw() - Tree.level.camera:attach() - love.graphics.setCanvas(Tree.level.render.textures.overlayLayer) - love.graphics.setColor(1, 1, 1, 0.5) - for _, p in pairs(self.targets) do - local s = self.tSize / Tree.level.camera.pixelsPerMeter - local quad = icons:pickQuad('dev_target') - love.graphics.draw(icons.atlas, quad, p.x + 0.5 - self.tSize / 2, p.y + 0.5 - self.tSize / 2, 0, s, s) - end - love.graphics.setShader() - - love.graphics.setCanvas() - Tree.level.camera:detach() - love.graphics.setColor(1, 1, 1) - - if self.previewType == "path" then + if self.targetType == "path" then local path = self.path --[[@as Deque?]] if not path then return end --- Это отрисовка пути персонажа к мышке Tree.level.camera:attach() love.graphics.setCanvas(Tree.level.render.textures.overlayLayer) + local i = 0 + path:pop_front() for p in path:values() do + i = i + 1 local s = 1 / Tree.level.camera.pixelsPerMeter - local quad = icons:pickQuad('dev_path') + local quad = i > self.distance and icons:pickQuad('dev_path_closed') or icons:pickQuad('dev_path') love.graphics.draw(icons.atlas, quad, p.x, p.y, 0, s, s) end + love.graphics.setCanvas() + Tree.level.camera:detach() + love.graphics.setColor(1, 1, 1) + else + Tree.level.camera:attach() + love.graphics.setCanvas(Tree.level.render.textures.overlayLayer) + love.graphics.setColor(1, 1, 1, 0.5) + for _, p in pairs(self.targets) do + local s = self.tSize / Tree.level.camera.pixelsPerMeter + local quad = icons:pickQuad('dev_target') + love.graphics.draw(icons.atlas, quad, p.x + 0.5 - self.tSize / 2, p.y + 0.5 - self.tSize / 2, 0, s, s) + end + love.graphics.setShader() + love.graphics.setCanvas() Tree.level.camera:detach() love.graphics.setColor(1, 1, 1) @@ -83,7 +87,7 @@ end function spell:cast(caster, target) return task.fromValue() end --- Конструктор [Spell] ---- @param data {tag: string, baseCost: integer, baseCooldown: integer, targetQuery: SpellTargetQuery?, previewType: SpellPreview?, distance: integer?, onCast: fun(caster: Character, target: Vec3): Task?} +--- @param data {tag: string, baseCost: integer, baseCooldown: integer, targetQuery: SpellTargetQuery?, targetType: SpellPreview?, distance: integer?, onCast: fun(caster: Character, target: Vec3): Task?} --- @return Spell function spell.new(data) local newSpell = setmetatable({ @@ -91,11 +95,11 @@ function spell.new(data) baseCost = data.baseCost, baseCooldown = data.baseCooldown, targetQuery = data.targetQuery, - previewType = data.previewType, + targetType = data.targetType, distance = data.distance }, spell) - newSpell.targetQuery = newSpell.distance + newSpell.targetQuery = (newSpell.distance and newSpell.targetType ~= "path") and newSpell.targetQuery:intersect(Query(targetTest.distance(newSpell.distance))) or newSpell.targetQuery @@ -107,6 +111,15 @@ function spell.new(data) end if not self.targetQuery.test(caster, target) then return end -- проверка корректности цели + if self.targetType == "path" then + -- дополнительное условие для спеллов с путями (количество шагов) + if not caster:has(Tree.behaviors.tiled) or not caster:has(Tree.behaviors.positioned) then return end + local i = 1 + for _ in pf(caster:has(Tree.behaviors.positioned).position, target):values() do + if i > self.distance + 1 then return end -- учитывается начальная точка, где находится кастер + i = i + 1 + end + end -- проверка на достаточное количество маны if caster:try(Tree.behaviors.stats, function(stats) diff --git a/lib/spellbook.lua b/lib/spellbook.lua index b9c0960..52bf978 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -15,7 +15,7 @@ local easing = require "lib.utils.easing" local walk = spell.new { tag = "dev_move", - previewType = "path", + targetType = "path", baseCooldown = 1, baseCost = 2, targetQuery = Query(targetTest.any):exclude(Query(targetTest.character)),