diff --git a/lib/character/behaviors/ai.lua b/lib/character/behaviors/ai.lua index 5aea6d3..b5e7b82 100644 --- a/lib/character/behaviors/ai.lua +++ b/lib/character/behaviors/ai.lua @@ -1,4 +1,4 @@ -local easing = require "lib.utils.easing" +local easing = require "lib.utils.easing" local function closestCharacter(char) local caster = Vec3 {} @@ -39,17 +39,24 @@ function behavior:makeTurn() self.target = Vec3 { b.position.x, b.position.y + 1 } --- @todo тут захардкожено + 1, но мы должны как-то хитро определять с какой стороны обойти end) - spellB.spellbook[1]:cast(self.owner, self.target)(function() - -- здесь мы оказываемся после того, как сходили в первый раз - print("[AI]: finished move 1") - local newTarget = Vec3 { 1, 1 } - -- поэтому позиция персонажа для нового каста пересчитается динамически - spellB.spellbook[1]:cast(self.owner, newTarget)(function() - print("[AI]: finished move 2") - -- дергаем функцию после завершения хода - callback() - end) - end) + local task1 = spellB.spellbook[1]:cast(self.owner, self.target) + if task1 then + task1( + function() + -- здесь мы оказываемся после того, как сходили в первый раз + local newTarget = Vec3 { 1, 1 } + local task2 = spellB.spellbook[1]:cast(self.owner, newTarget) + if task2 then + -- дергаем функцию после завершения хода + task2(callback) + else + callback() + end + end + ) + else + callback() + end end) end end diff --git a/lib/character/behaviors/spellcaster.lua b/lib/character/behaviors/spellcaster.lua index 37c128b..aa0ad60 100644 --- a/lib/character/behaviors/spellcaster.lua +++ b/lib/character/behaviors/spellcaster.lua @@ -1,11 +1,13 @@ --- @class SpellcasterBehavior : Behavior --- @field spellbook Spell[] собственный набор спеллов персонажа --- @field cast Spell | nil ссылка на активный спелл из спеллбука +--- @field cooldowns {[string]: integer} текущий кулдаун спеллов по тегам --- @field state "idle" | "casting" | "running" local behavior = {} behavior.__index = behavior behavior.id = "spellcaster" behavior.state = "idle" +behavior.cooldowns = {} ---@param spellbook Spell[] | nil ---@return SpellcasterBehavior @@ -13,6 +15,7 @@ function behavior.new(spellbook) local spb = require "lib.spellbook" --- @todo временное добавление ходьбы (и читов) всем персонажам local t = {} t.spellbook = spellbook or spb.of { spb.walk, spb.regenerateMana, spb.attack } + t.cooldowns = {} return setmetatable(t, behavior) end @@ -23,6 +26,14 @@ function behavior:endCast() Tree.level.selector:unlock() end +function behavior:processCooldowns() + local cds = {} + for tag, cd in pairs(self.cooldowns) do + cds[tag] = (cd - 1) >= 0 and cd - 1 or 0 + end + self.cooldowns = cds +end + function behavior:update(dt) if Tree.level.selector:deselected() then self.state = "idle" diff --git a/lib/level/selector.lua b/lib/level/selector.lua index 3c60a6f..06ec4a9 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -38,16 +38,17 @@ function selector:update(dt) return end local task = b.cast:cast(char, mousePosition) -- в task функция, которая запускает анимацию спелла - if task then - self:lock() - b.state = "running" + if not task then return end -- не получилось скастовать - task( - function(_) -- это коллбэк, который сработает по окончании анимации спелла - b:endCast() - end - ) - end + self:lock() + b.state = "running" + + task( + function(_) -- это коллбэк, который сработает по окончании анимации спелла + b:endCast() + if not char:has(Tree.behaviors.ai) then self:select(char.id) end -- выделяем персонажа обратно после того, как посмотрели на каст + end + ) end) end end diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index a059197..08e9eec 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -57,10 +57,20 @@ function turnOrder:next() end) end +--- Производим действия в конце раунда +--- --- Меняем местами очередь сходивших и не сходивших (пустую) function turnOrder:endRound() assert(self.pendingQueue:size() == 0, "[TurnOrder]: tried to end the round before everyone had a turn") print("[TurnOrder]: end of the round") + + for _, id in ipairs(self.actedQueue.data) do + local char = Tree.level.characters[id] + char:try(Tree.behaviors.spellcaster, function(spellcaster) + spellcaster:processCooldowns() + end) + end + self.actedQueue, self.pendingQueue = self.pendingQueue, self.actedQueue self.current = self.pendingQueue:pop() end diff --git a/lib/simple_ui/level/bar.lua b/lib/simple_ui/level/bar.lua index 9bacb90..35601af 100644 --- a/lib/simple_ui/level/bar.lua +++ b/lib/simple_ui/level/bar.lua @@ -57,7 +57,7 @@ function barElement:draw() love.graphics.setColor(1, 1, 1) --- текст поверх if self.drawText then - local font = Tree.fonts:getDefaultTheme():getVariant("medium") + local font = Tree.fonts:getDefaultTheme():getVariant("small") local t = love.graphics.newText(font, tostring(self.value) .. "/" .. tostring(self.maxValue)) love.graphics.draw(t, math.floor(self.bounds.x + self.bounds.width / 2 - t:getWidth() / 2), math.floor(self.bounds.y + self.bounds.height / 2 - t:getHeight() / 2)) diff --git a/lib/simple_ui/level/end_turn.lua b/lib/simple_ui/level/end_turn.lua index 5621311..21b95d7 100644 --- a/lib/simple_ui/level/end_turn.lua +++ b/lib/simple_ui/level/end_turn.lua @@ -22,7 +22,7 @@ function endTurnButton:update(dt) end function endTurnButton:layout() - local font = Tree.fonts:getDefaultTheme():getVariant("headline") + local font = Tree.fonts:getDefaultTheme():getVariant("large") self.text = love.graphics.newText(font, "Завершить ход") self.bounds.width = self.text:getWidth() + 32 self.bounds.height = self.text:getHeight() + 16 diff --git a/lib/simple_ui/level/skill_row.lua b/lib/simple_ui/level/skill_row.lua index 6f7ddc7..b02e5c2 100644 --- a/lib/simple_ui/level/skill_row.lua +++ b/lib/simple_ui/level/skill_row.lua @@ -8,6 +8,7 @@ local UI_SCALE = require "lib.simple_ui.level.scale" --- @field hovered boolean --- @field selected boolean --- @field onClick function? +--- @field getCooldown function? --- @field icon? string local skillButton = setmetatable({}, Element) skillButton.__index = skillButton @@ -18,7 +19,11 @@ function skillButton:update(dt) if self:hitTest(mx, my) then self.hovered = true if Tree.controls:isJustPressed("select") then - if self.onClick then self.onClick() end + local cd = self.getCooldown and self.getCooldown() or 0 + if cd == 0 then + if self.onClick then self.onClick() end + end + Tree.controls:consume("select") end else @@ -29,6 +34,7 @@ end function skillButton:draw() love.graphics.setLineWidth(2) + local cd = self.getCooldown and self.getCooldown() or 0 if not self.icon then love.graphics.setColor(0.05, 0.05, 0.05) @@ -53,6 +59,25 @@ function skillButton:draw() love.graphics.setColor(0.7, 1, 0.7, 0.5) love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) end + + + if cd > 0 then + love.graphics.setColor(0, 0, 0, 0.5) + love.graphics.rectangle("fill", self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height) + + local font = Tree.fonts:getDefaultTheme():getVariant("headline") + love.graphics.setColor(0, 0, 0) + local t = love.graphics.newText(font, tostring(cd)) + love.graphics.draw(t, math.floor(self.bounds.x + 2 + self.bounds.width / 2 - t:getWidth() / 2), + math.floor(self.bounds.y + 2 + self.bounds.height / 2 - t:getHeight() / 2)) + + love.graphics.setColor(1, 1, 1) + love.graphics.draw(t, math.floor(self.bounds.x + self.bounds.width / 2 - t:getWidth() / 2), + math.floor(self.bounds.y + self.bounds.height / 2 - t:getHeight() / 2)) + else + + end + love.graphics.setColor(1, 1, 1) end @@ -92,6 +117,9 @@ function skillRow.new(characterId) behavior.cast = nil end end + skb.getCooldown = function() + return behavior.cooldowns[spell.tag] or 0 + end t.children[i] = skb end end) diff --git a/lib/spell/spell.lua b/lib/spell/spell.lua new file mode 100644 index 0000000..577b01b --- /dev/null +++ b/lib/spell/spell.lua @@ -0,0 +1,104 @@ +local Query = require "lib.spell.target_query" +local targetTest = require "lib.spell.target_test" +local task = require "lib.utils.task" + +--- @alias SpellPreview "default" Подсветка возможных целей +--- | "path" Подсветка пути до цели + +--- @class Spell +--- @field tag string +--- @field baseCost integer Базовые затраты маны на каст +--- @field baseCooldown integer Базовый кулдаун в ходах +--- @field targetQuery SpellTargetQuery Селектор возможных целей +--- @field previewType SpellPreview Вид превью во время каста +--- @field distance? integer Сторона квадрата с центром в позиции кастера, в пределах которого должна находиться цель, либо отсутствие ограничения +--- @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): Task? Вызывается в момент каста, изменяет мир. +local spell = {} +spell.__index = spell +spell.tag = "spell_base" +spell.baseCost = 1 +spell.baseCooldown = 1 +spell.targetQuery = Query(targetTest.any) +spell.previewType = "default" + +function spell:update(caster, dt) + if self.previewType == "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() + if self.targetQuery.test(caster, mpos) then + self.path = require "lib.pathfinder" (charPos, mpos) + else + self.path = nil + end + end +end + +function spell:draw() + if self.previewType == "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) + 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.setCanvas() + Tree.level.camera:detach() + love.graphics.setColor(1, 1, 1) + end +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?} +--- @return Spell +function spell.new(data) + local newSpell = setmetatable({ + tag = data.tag, + baseCost = data.baseCost, + baseCooldown = data.baseCooldown, + targetQuery = data.targetQuery, + previewType = data.previewType, + distance = data.distance + }, spell) + + newSpell.targetQuery = newSpell.distance + and newSpell.targetQuery:intersect(Query(targetTest.distance(newSpell.distance))) + or newSpell.targetQuery + + function newSpell:cast(caster, target) + if caster:try(Tree.behaviors.spellcaster, function(spellcaster) -- проверка на кулдаун + return (spellcaster.cooldowns[self.tag] or 0) > 0 + end) then + return + end + + if not self.targetQuery.test(caster, target) then return end -- проверка корректности цели + + -- проверка на достаточное количество маны + if caster:try(Tree.behaviors.stats, function(stats) + return stats.mana < self.baseCost + end) then + return + end + + caster:try(Tree.behaviors.stats, function(stats) + stats.mana = stats.mana - self.baseCost + end) + + caster:try(Tree.behaviors.spellcaster, function(spellcaster) + spellcaster.cooldowns[self.tag] = self.baseCooldown + end) + return data.onCast(caster, target) + end + + return newSpell +end + +return spell diff --git a/lib/spell/target_query.lua b/lib/spell/target_query.lua new file mode 100644 index 0000000..d84de28 --- /dev/null +++ b/lib/spell/target_query.lua @@ -0,0 +1,67 @@ +--- Тип, отвечающий за выбор и фильтрацию подходящих тайлов как цели спелла +--- теория множеств my beloved? +--- @class SpellTargetQuery +local query = {} +query.__index = query + +--- Проверяет координаты на соответствие внутреннему условию +--- @param caster Character +--- @param position Vec3 +--- @return boolean +function query.test(caster, position) + return true +end + +--- Объединение +--- @param q SpellTargetQuery +function query:join(q) + return setmetatable({ + test = function(caster, pos) + return self.test(caster, pos) or q.test(caster, pos) + end + }, query) +end + +--- Пересечение +--- @param q SpellTargetQuery +function query:intersect(q) + return setmetatable({ + test = function(caster, pos) + return self.test(caster, pos) and q.test(caster, pos) + end + }, query) +end + +--- Исключение (не коммутативное, "те, что есть в query, но нет в q") +--- @param q SpellTargetQuery +function query:exclude(q) + return setmetatable({ + test = function(caster, pos) + return self.test(caster, pos) and not q.test(caster, pos) + end + }, query) +end + +--- Находит все соответствующие условиям координаты тайлов и возвращает их в виде списка +--- @param caster Character +--- @return Vec3[] +function query:asSet(caster) + --- @TODO: оптимизировать и брать не всю карту для выборки + local res = {} + for _, tile in pairs(Tree.level.tileGrid) do + if self.test(caster, tile.position) then + table.insert(res, tile.position) + end + end + + return res +end + +--- @param test SpellTargetTest +local function new(test) + return setmetatable({ + test = test + }, query) +end + +return new diff --git a/lib/spell/target_test.lua b/lib/spell/target_test.lua new file mode 100644 index 0000000..ee7a3ed --- /dev/null +++ b/lib/spell/target_test.lua @@ -0,0 +1,27 @@ +--- @alias SpellTargetTest fun(caster: Character, targetPosition: Vec3) : boolean + +return { + -- любой тайл + any = function() return true end, + -- тайл, где находится кастующий + caster = function(caster, targetPosition) + local targetCharacterId = Tree.level.characterGrid:get(targetPosition) + return caster.id == targetCharacterId + end, + -- тайл, где находится любой персонаж + character = function(caster, targetPosition) + local targetCharacterId = Tree.level.characterGrid:get(targetPosition) + return not not targetCharacterId + end, + -- тайл в пределах окружности в нашей кривой метрике + --- @param radius number + distance = function(radius) + return function(caster, targetPosition) + return caster:try(Tree.behaviors.positioned, function(p) + local dist = math.max(math.abs(p.position.x - targetPosition.x), + math.abs(p.position.y - targetPosition.y)) + return dist <= radius + end) + end + end +} diff --git a/lib/spellbook.lua b/lib/spellbook.lua index 7c95ea5..b9c0960 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -7,157 +7,122 @@ --- --TODO: каждый каст должен возвращать объект, который позволит отследить момент завершения анимации спелла --- Да, это Future/Promise/await/async -local task = require 'lib.utils.task' +local task = require 'lib.utils.task' +local spell = require 'lib.spell.spell' +local targetTest = require 'lib.spell.target_test' +local Query = require "lib.spell.target_query" +local easing = require "lib.utils.easing" ---- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell ---- @field tag string ---- @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): Task | nil Вызывается в момент каста, изменяет мир. -local spell = {} -spell.__index = spell -spell.tag = "base" +local walk = spell.new { + tag = "dev_move", + previewType = "path", + baseCooldown = 1, + baseCost = 2, + targetQuery = Query(targetTest.any):exclude(Query(targetTest.character)), + distance = 3, + onCast = function(caster, target) + local initialPos = caster:has(Tree.behaviors.positioned).position:floor() + local path = require "lib.pathfinder" (initialPos, target) + path:pop_front() + if path:is_empty() then + print("[Walk]: the path is empty", initialPos, target) + return + end -function spell:update(caster, dt) end + local sprite = caster:has(Tree.behaviors.sprite) + assert(sprite, "[Walk]", "WTF DUDE WHERE'S YOUR SPRITE") + if not sprite then + return + end -function spell:draw() end - -function spell:cast(caster, target) return end - -local walk = setmetatable({ - --- @type Deque - path = nil -}, spell) -walk.tag = "dev_move" - -function walk:cast(caster, target) - if not caster:try(Tree.behaviors.stats, function(stats) - return stats.mana >= 2 - end) then - return + return caster:has(Tree.behaviors.tiled):followPath(path) end +} - local initialPos = caster:has(Tree.behaviors.positioned).position:floor() - local path = require "lib.pathfinder" (initialPos, target) - path:pop_front() - if path:is_empty() then - print("[Walk]: the path is empty", initialPos, target) - return - end +local regenerateMana = spell.new { + tag = "dev_mana", + baseCooldown = 2, + baseCost = 0, + targetQuery = Query(targetTest.caster), + distance = 0, + onCast = function(caster, target) + caster:try(Tree.behaviors.stats, function(stats) + stats.mana = 10 + stats.initiative = stats.initiative + 10 + end) - caster:try(Tree.behaviors.stats, function(stats) - stats.mana = stats.mana - 2 - end) + local sprite = caster:has(Tree.behaviors.sprite) + if not sprite then return end + print(caster.id, "has regenerated mana and gained initiative") - local sprite = caster:has(Tree.behaviors.sprite) - assert(sprite, "[Walk]", "WTF DUDE WHERE'S YOUR SPRITE") - if not sprite then - return - end - - return caster:has(Tree.behaviors.tiled):followPath(path) -end - -function walk:update(caster, dt) - 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() - self.path = require "lib.pathfinder" (charPos, mpos) -end - -function walk:draw() - if not self.path then return end - --- Это отрисовка пути персонажа к мышке - Tree.level.camera:attach() - love.graphics.setCanvas(Tree.level.render.textures.overlayLayer) - 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.setCanvas() - Tree.level.camera:detach() - love.graphics.setColor(1, 1, 1) -end - -local regenerateMana = setmetatable({}, spell) -regenerateMana.tag = "dev_mana" - -function regenerateMana:cast(caster, target) - caster:try(Tree.behaviors.stats, function(stats) - stats.mana = 10 - stats.initiative = stats.initiative + 10 - end) - - local sprite = caster:has(Tree.behaviors.sprite) - if not sprite then return nil end - print(caster.id, "has regenerated mana and gained initiative") - - local light = require "lib/character/character".spawn("Light Effect") - light:addBehavior { - Tree.behaviors.light.new { color = Vec3 { 0.6, 0.3, 0.3 }, intensity = 4 }, - Tree.behaviors.residentsleeper.new(), - Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }), - } - - local flash = function(callback) - light:has(Tree.behaviors.light):animateColor(Vec3 {})( - function() - light:die() - callback() - end - ) - end - - return task.wait { - flash, - sprite:animate("hurt") - } -end - -local attack = setmetatable({}, spell) -attack.tag = "dev_attack" - -function attack:cast(caster, target) - if caster:try(Tree.behaviors.positioned, function(p) - local dist = math.max(math.abs(p.position.x - target.x), math.abs(p.position.y - target.y)) - print("dist:", dist) - return dist > 2 - end) then - return - end - - caster:try(Tree.behaviors.stats, function(stats) - stats.mana = stats.mana - 2 - end) - - --- @type Character - local targetCharacterId = Tree.level.characterGrid:get(target) - if not targetCharacterId or targetCharacterId == caster.id then return end - local targetCharacter = Tree.level.characters[targetCharacterId] - targetCharacter:try(Tree.behaviors.stats, function(stats) - stats.hp = stats.hp - 4 - end) - - local sprite = caster:has(Tree.behaviors.sprite) - local targetSprite = targetCharacter:has(Tree.behaviors.sprite) - if not sprite or not targetSprite then return end - - caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end) - - return - task.wait { - sprite:animate("attack"), - task.wait { - task.chain(targetCharacter:has(Tree.behaviors.residentsleeper):sleep(200), - function() return targetSprite:animate("hurt") end - ), - Tree.audio:play(Tree.assets.files.audio.sounds.hurt) - } + local light = require "lib/character/character".spawn("Light Effect") + light:addBehavior { + Tree.behaviors.light.new { color = Vec3 { 0.3, 0.3, 0.6 }, intensity = 4 }, + Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }), } -end + + return task.wait { + task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]], + { intensity = 1, color = Vec3 {} }, 800, easing.easeInCubic), function() + light:die() + return task.fromValue() + end), + sprite:animate("hurt") + } + end +} + +local attack = spell.new { + tag = "dev_attack", + baseCooldown = 1, + baseCost = 2, + targetQuery = Query(targetTest.character):exclude(Query(targetTest.caster)), + distance = 1, + onCast = function(caster, target) + --- @type Character + local targetCharacterId = Tree.level.characterGrid:get(target) + local targetCharacter = Tree.level.characters[targetCharacterId] + targetCharacter:try(Tree.behaviors.stats, function(stats) + stats.hp = stats.hp - 4 + end) + + local sprite = caster:has(Tree.behaviors.sprite) + local targetSprite = targetCharacter:has(Tree.behaviors.sprite) + if not sprite or not targetSprite then return end + + caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end) + + return + task.wait { + sprite:animate("attack"), + task.wait { + task.chain(targetCharacter:has(Tree.behaviors.residentsleeper):sleep(500), + function() + local light = require "lib/character/character".spawn("Light Effect") + light:addBehavior { + Tree.behaviors.light.new { color = Vec3 { 0.6, 0.3, 0.3 }, intensity = 4 }, + Tree.behaviors.positioned.new(targetCharacter:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }), + } + return + task.wait { + task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]], + { intensity = 1, color = Vec3 {} }, 1000, easing.easeInCubic), function() + light:die() + return task.fromValue() + end), + targetSprite:animate("hurt") + } + end + ), + + Tree.audio:play(Tree.assets.files.audio.sounds.hurt) + } + } + end +} ---------------------------------------- -local spellbook = { +local spellbook = { walk = walk, regenerateMana = regenerateMana, attack = attack diff --git a/lib/utils/font_manager.lua b/lib/utils/font_manager.lua index 0bec070..f9c48df 100644 --- a/lib/utils/font_manager.lua +++ b/lib/utils/font_manager.lua @@ -4,11 +4,11 @@ --- @field private _sizes {[FontVariant]: integer} local theme = { _sizes = { - smallest = 10, - small = 12, - medium = 14, - large = 16, - headline = 20, + smallest = 12, + small = 14, + medium = 16, + large = 22, + headline = 32, } } theme.__index = theme diff --git a/lib/utils/priority_queue.lua b/lib/utils/priority_queue.lua index 0097604..4b09c2e 100644 --- a/lib/utils/priority_queue.lua +++ b/lib/utils/priority_queue.lua @@ -1,5 +1,5 @@ ---@class PriorityQueue ----@field private data any[] внутренний массив-куча (индексация с 1) +---@field data any[] внутренний массив-куча (индексация с 1) ---@field private cmp fun(a:any, b:any):boolean компаратор: true, если a выше по приоритету, чем b local PriorityQueue = {} PriorityQueue.__index = PriorityQueue diff --git a/main.lua b/main.lua index a0127ce..25db8f6 100644 --- a/main.lua +++ b/main.lua @@ -111,7 +111,7 @@ function love.draw() testLayout:draw() love.graphics.setColor(1, 1, 1) - love.graphics.setFont(Tree.fonts:getTheme("Roboto_Mono"):getVariant("medium")) + love.graphics.setFont(Tree.fonts:getTheme("Roboto_Mono"):getVariant("small")) local stats = "fps: " .. love.timer.getFPS() .. " lt: " .. lt .. " dt: " .. dt .. " mem: " .. string.format("%.2f MB", collectgarbage("count") / 1000)