From 6dd02ed2ec4a91b29231154d026ae411662021e3 Mon Sep 17 00:00:00 2001 From: neckrat Date: Mon, 3 Nov 2025 18:24:54 +0300 Subject: [PATCH 1/9] turn order init --- lib/character/character.lua | 1 + lib/level/level.lua | 2 ++ lib/level/turn_order.lua | 43 +++++++++++++++++++++++++++++++++++++ lib/spellbook.lua | 2 ++ main.lua | 2 ++ 5 files changed, 50 insertions(+) create mode 100644 lib/level/turn_order.lua diff --git a/lib/character/character.lua b/lib/character/character.lua index 4e7aec1..11ca80b 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -35,6 +35,7 @@ local function spawn(name, spriteDir, position, size, level) } Tree.level.characters[char.id] = char + Tree.level.turnOrder:updateOrder() return char end diff --git a/lib/level/level.lua b/lib/level/level.lua index eb182e9..8dd3d33 100644 --- a/lib/level/level.lua +++ b/lib/level/level.lua @@ -7,6 +7,7 @@ local utils = require "lib.utils.utils" --- @field selector Selector --- @field camera Camera --- @field tileGrid TileGrid +--- @field turnOrder TurnOrder local level = {} level.__index = level @@ -24,6 +25,7 @@ local function new(type, template) tileGrid = (require "lib.level.grid.tile_grid").new(type, template, size), selector = (require "lib.level.selector").new(), camera = (require "lib.level.camera").new(), + turnOrder = (require "lib.level.turn_order").new(), }, level) end diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua new file mode 100644 index 0000000..d874198 --- /dev/null +++ b/lib/level/turn_order.lua @@ -0,0 +1,43 @@ +--- @class TurnOrder +--- @field order Id[] возможно в будущем тут будет не айди персонажа, а айди фракции, в которой состоят несколько персонажей +local turnOrder = {} +turnOrder.__index = turnOrder + +local function new() + return setmetatable({ + order = {} + }, turnOrder) +end + +function turnOrder:updateOrder() + local order = {} + local count = 1 + for k, _ in ipairs(Tree.level.characters) do + order[count] = k + count = count + 1 + end + self.order = order + + print('[Level]: Turn order has been updated!') + for _, v in ipairs(self.order) do + print('[Level]: id:', v) + end +end + +function turnOrder:next() + self.order[#self.order + 1] = self.order[1] + self.order[1] = nil + + local order = {} + for i = 1, #self.order do + order[i] = self.order[i + 1] + end + + self.order = order + print('[Level]: Triggered next order') + for _, v in ipairs(self.order) do + print('[Level]: id:', v) + end +end + +return { new = new } diff --git a/lib/spellbook.lua b/lib/spellbook.lua index e117065..c1f91ba 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -52,6 +52,8 @@ function walk:cast(caster, target) onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end, }:run() + Tree.level.turnOrder:next() + return true end diff --git a/main.lua b/main.lua index 9c740fa..2722bee 100644 --- a/main.lua +++ b/main.lua @@ -12,6 +12,8 @@ end function love.load() character.spawn("Foodor", Tree.assets.files.sprites.character) character.spawn("Baris", Tree.assets.files.sprites.character, Vec3 { 3, 3 }) + character.spawn("Foodor Jr", Tree.assets.files.sprites.character, Vec3 { 0, 3 }) + character.spawn("Baris Jr", Tree.assets.files.sprites.character, Vec3 { 0, 6 }) love.window.setMode(1080, 720, { resizable = true, msaa = 4, vsync = true }) end From 369e7db7e01727fe14ff2daccd84ea76cdb01c6c Mon Sep 17 00:00:00 2001 From: neckrat Date: Mon, 3 Nov 2025 19:05:31 +0300 Subject: [PATCH 2/9] fix little typo --- lib/level/turn_order.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index d874198..83e7572 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -34,7 +34,7 @@ function turnOrder:next() end self.order = order - print('[Level]: Triggered next order') + print('[Level]: Triggered next turn') for _, v in ipairs(self.order) do print('[Level]: id:', v) end From 73d8f6a3a5d5eb39c14a002d9e76cc4db866e573 Mon Sep 17 00:00:00 2001 From: neckrat Date: Tue, 4 Nov 2025 01:00:07 +0300 Subject: [PATCH 3/9] now you can make moves only when your turn --- lib/controls.lua | 3 ++- lib/level/selector.lua | 2 ++ lib/spellbook.lua | 2 -- main.lua | 7 +++++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/controls.lua b/lib/controls.lua index ea0a42f..e91e21b 100644 --- a/lib/controls.lua +++ b/lib/controls.lua @@ -18,7 +18,8 @@ controls.keymap = { cameraMoveDown = control("key", "s"), cameraMoveScroll = control("mouse", "3"), fullMana = control("key", "m"), - select = control("mouse", "1") + select = control("mouse", "1"), + endTurnTest = control("key", "e") } local currentKeys = {} diff --git a/lib/level/selector.lua b/lib/level/selector.lua index fb9f9f6..db894bb 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -25,6 +25,8 @@ function selector:update(dt) local selectedId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y }) if not self.id then + -- print(selectedId, Tree.level.turnOrder.order[1]) + if selectedId ~= Tree.level.turnOrder.order[1] then return end return self:select(selectedId) else local char = Tree.level.characters[self.id] diff --git a/lib/spellbook.lua b/lib/spellbook.lua index c1f91ba..e117065 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -52,8 +52,6 @@ function walk:cast(caster, target) onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end, }:run() - Tree.level.turnOrder:next() - return true end diff --git a/main.lua b/main.lua index 2722bee..24847cf 100644 --- a/main.lua +++ b/main.lua @@ -25,6 +25,13 @@ function love.update(dt) Widgets:update(dt) -- логика UI-слоя должна отработать раньше всех, потому что нужно перехватить жесты и не пустить их дальше Tree.panning:update(dt) Tree.level:update(dt) + + -- для тестов очереди ходов + -- удалить как только появится ui для людей + if Tree.controls:isJustPressed("endTurnTest") then + Tree.level.turnOrder:next() + end + Tree.controls:cache() local t2 = love.timer.getTime() From 7beb421486b6b32365de7cd9c43c1a69190e2f91 Mon Sep 17 00:00:00 2001 From: neckrat Date: Tue, 4 Nov 2025 01:26:07 +0300 Subject: [PATCH 4/9] toggle turn mode --- lib/controls.lua | 3 ++- lib/level/selector.lua | 2 +- lib/level/turn_order.lua | 5 +++++ main.lua | 4 ++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/controls.lua b/lib/controls.lua index e91e21b..652d26a 100644 --- a/lib/controls.lua +++ b/lib/controls.lua @@ -19,7 +19,8 @@ controls.keymap = { cameraMoveScroll = control("mouse", "3"), fullMana = control("key", "m"), select = control("mouse", "1"), - endTurnTest = control("key", "e") + endTurnTest = control("key", "e"), + toggleTurns = control("key", "r"), } local currentKeys = {} diff --git a/lib/level/selector.lua b/lib/level/selector.lua index db894bb..e70f333 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -26,7 +26,7 @@ function selector:update(dt) if not self.id then -- print(selectedId, Tree.level.turnOrder.order[1]) - if selectedId ~= Tree.level.turnOrder.order[1] then return end + if selectedId ~= Tree.level.turnOrder.order[1] and Tree.level.turnOrder.isTurnsEnabled then return end return self:select(selectedId) else local char = Tree.level.characters[self.id] diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index 83e7572..2f8ff3b 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -1,5 +1,6 @@ --- @class TurnOrder --- @field order Id[] возможно в будущем тут будет не айди персонажа, а айди фракции, в которой состоят несколько персонажей +--- @field isTurnsEnabled boolean local turnOrder = {} turnOrder.__index = turnOrder @@ -40,4 +41,8 @@ function turnOrder:next() end end +function turnOrder:toggleTurns() + self.isTurnsEnabled = not self.isTurnsEnabled +end + return { new = new } diff --git a/main.lua b/main.lua index 24847cf..fe01759 100644 --- a/main.lua +++ b/main.lua @@ -31,6 +31,10 @@ function love.update(dt) if Tree.controls:isJustPressed("endTurnTest") then Tree.level.turnOrder:next() end + if Tree.controls:isJustPressed("toggleTurns") then + print('toggle turns') + Tree.level.turnOrder:toggleTurns() + end Tree.controls:cache() From 3caa3b926e6048c7affedc315b638746aab63be0 Mon Sep 17 00:00:00 2001 From: neckrat Date: Tue, 4 Nov 2025 13:32:48 +0300 Subject: [PATCH 5/9] fix turns when you click on another char --- lib/level/selector.lua | 3 ++- lib/level/turn_order.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/level/selector.lua b/lib/level/selector.lua index e70f333..3951998 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -25,7 +25,7 @@ function selector:update(dt) local selectedId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y }) if not self.id then - -- print(selectedId, Tree.level.turnOrder.order[1]) + print(selectedId, Tree.level.turnOrder.order[1]) if selectedId ~= Tree.level.turnOrder.order[1] and Tree.level.turnOrder.isTurnsEnabled then return end return self:select(selectedId) else @@ -33,6 +33,7 @@ function selector:update(dt) char:try(Tree.behaviors.spellcaster, function(b) if not b.cast then + if selectedId ~= Tree.level.turnOrder.order[1] and Tree.level.turnOrder.isTurnsEnabled then return end self:select(selectedId) return end diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index 2f8ff3b..eeeb95a 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -6,7 +6,8 @@ turnOrder.__index = turnOrder local function new() return setmetatable({ - order = {} + order = {}, + isTurnsEnabled = true }, turnOrder) end From 8821cb4cf7eeafd30c01a2ebddcfccb1c334859d Mon Sep 17 00:00:00 2001 From: neckrat Date: Fri, 7 Nov 2025 09:25:21 +0300 Subject: [PATCH 6/9] try to add initiative --- lib/character/behaviors/stats.lua | 16 ++++++---- lib/character/character.lua | 6 ++-- lib/level/selector.lua | 9 ++++-- lib/level/turn_order.lua | 49 +++++++++++++++++++------------ main.lua | 2 +- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/lib/character/behaviors/stats.lua b/lib/character/behaviors/stats.lua index a6161bd..965d290 100644 --- a/lib/character/behaviors/stats.lua +++ b/lib/character/behaviors/stats.lua @@ -1,17 +1,23 @@ --- @class StatsBehavior : Behavior --- @field hp integer --- @field mana integer +--- @field initiative integer +--- @field isInTurnOrder boolean local behavior = {} behavior.__index = behavior behavior.id = "stats" --- @param hp? integer --- @param mana? integer -function behavior.new(hp, mana) - return setmetatable({ +--- @param initiative? integer +--- @param isInTurnOrder? boolean +function behavior.new(hp, mana, initiative, isInTurnOrder) + return setmetatable({ hp = hp or 20, - mana = mana or 10 - }, behavior) + mana = mana or 10, + initiative = initiative or 10, + isInTurnOrder = isInTurnOrder or true + }, behavior) end -return behavior \ No newline at end of file +return behavior diff --git a/lib/character/character.lua b/lib/character/character.lua index 11ca80b..a11e069 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -16,8 +16,8 @@ character.__index = character --- @param spriteDir table --- @param position? Vec3 --- @param size? Vec3 ---- @param level? integer -local function spawn(name, spriteDir, position, size, level) +--- @param initiative? integer +local function spawn(name, spriteDir, position, size, initiative) local char = {} char = setmetatable(char, character) @@ -28,7 +28,7 @@ local function spawn(name, spriteDir, position, size, level) char:addBehavior { Tree.behaviors.residentsleeper.new(), - Tree.behaviors.stats.new(), + Tree.behaviors.stats.new(nil, nil, initiative), Tree.behaviors.map.new(position, size), Tree.behaviors.sprite.new(spriteDir), Tree.behaviors.spellcaster.new() diff --git a/lib/level/selector.lua b/lib/level/selector.lua index 3951998..b89946d 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -25,15 +25,18 @@ function selector:update(dt) local selectedId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y }) if not self.id then - print(selectedId, Tree.level.turnOrder.order[1]) - if selectedId ~= Tree.level.turnOrder.order[1] and Tree.level.turnOrder.isTurnsEnabled then return end + if selectedId ~= Tree.level.turnOrder.notWalked[1] and Tree.level.turnOrder.isTurnsEnabled then return end return self:select(selectedId) else local char = Tree.level.characters[self.id] char:try(Tree.behaviors.spellcaster, function(b) if not b.cast then - if selectedId ~= Tree.level.turnOrder.order[1] and Tree.level.turnOrder.isTurnsEnabled then return end + -- тут какая-то страшная дичь, я даже не уверен что оно работает + -- зато я точно уверен, что это надо было писать не так + if not selectedId then self:select(selectedId) end + -- print(selectedId ~= next(Tree.level.turnOrder.notWalked), Tree.level.turnOrder.isTurnsEnabled) + if selectedId ~= Tree.level.turnOrder.notWalked[1] and Tree.level.turnOrder.isTurnsEnabled then return end self:select(selectedId) return end diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index eeeb95a..7ef3130 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -1,44 +1,57 @@ --- @class TurnOrder ---- @field order Id[] возможно в будущем тут будет не айди персонажа, а айди фракции, в которой состоят несколько персонажей +--- @field walked integer[] +--- @field notWalked integer[] --- @field isTurnsEnabled boolean local turnOrder = {} turnOrder.__index = turnOrder local function new() return setmetatable({ - order = {}, + walked = {}, + notWalked = {}, isTurnsEnabled = true }, turnOrder) end function turnOrder:updateOrder() - local order = {} + local notWalked = {} local count = 1 - for k, _ in ipairs(Tree.level.characters) do - order[count] = k - count = count + 1 + for id, char in ipairs(Tree.level.characters) do + char:try(Tree.behaviors.stats, function(stats) + notWalked[count] = id + count = count + 1 + end) end - self.order = order + table.sort(notWalked, function(id_a, id_b) + local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats) + local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats) + return astats.initiative > bstats.initiative + end) + return res + end) + return res or false + end) + self.notWalked = notWalked print('[Level]: Turn order has been updated!') - for _, v in ipairs(self.order) do - print('[Level]: id:', v) - end end function turnOrder:next() - self.order[#self.order + 1] = self.order[1] - self.order[1] = nil + table.insert(self.walked, self.notWalked[1]) + self.notWalked[1] = nil - local order = {} - for i = 1, #self.order do - order[i] = self.order[i + 1] + local notWalked = {} + for i = 1, #self.notWalked do + notWalked[i] = self.notWalked[i + 1] + print(notWalked[i], i) end - self.order = order + self.notWalked = notWalked print('[Level]: Triggered next turn') - for _, v in ipairs(self.order) do - print('[Level]: id:', v) + print('[Level]:', next(self.notWalked)) + if #self.notWalked == 0 then + self:updateOrder() + self.walked = {} end end diff --git a/main.lua b/main.lua index fe01759..6fc77d0 100644 --- a/main.lua +++ b/main.lua @@ -11,7 +11,7 @@ end function love.load() character.spawn("Foodor", Tree.assets.files.sprites.character) - character.spawn("Baris", Tree.assets.files.sprites.character, Vec3 { 3, 3 }) + character.spawn("Baris", Tree.assets.files.sprites.character, Vec3 { 3, 3 }, nil, 12) character.spawn("Foodor Jr", Tree.assets.files.sprites.character, Vec3 { 0, 3 }) character.spawn("Baris Jr", Tree.assets.files.sprites.character, Vec3 { 0, 6 }) love.window.setMode(1080, 720, { resizable = true, msaa = 4, vsync = true }) From 242b37de83888155f8ffd599a1240a3dd43d1176 Mon Sep 17 00:00:00 2001 From: neckrat Date: Fri, 7 Nov 2025 21:51:35 +0300 Subject: [PATCH 7/9] fix bag, when you leave selector on your character when end your turn --- lib/level/turn_order.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index 7ef3130..1fd5ff4 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -47,6 +47,9 @@ function turnOrder:next() end self.notWalked = notWalked + + Tree.level.selector:select(nil) + print('[Level]: Triggered next turn') print('[Level]:', next(self.notWalked)) if #self.notWalked == 0 then From 4169aa4b61384f21effda497386b0fd7f2b3b027 Mon Sep 17 00:00:00 2001 From: neckrat Date: Sun, 9 Nov 2025 01:43:06 +0300 Subject: [PATCH 8/9] refactor turn-order i should leave this peace of oxygen etc etc Co-authored-by: Ivan Yuriev --- lib/level/turn_order.lua | 82 +++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index 1fd5ff4..427228f 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -1,65 +1,59 @@ +local PriorityQueue = require "lib.utils.priority_queue" + +local sortInitiative = function(id_a, id_b) + local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats) + local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats) + return astats.initiative > bstats.initiative + end) + return res + end) + return res or false +end + --- @class TurnOrder ---- @field walked integer[] ---- @field notWalked integer[] +--- @field characterIds Id[] +--- @field walked PriorityQueue +--- @field notWalked PriorityQueue --- @field isTurnsEnabled boolean local turnOrder = {} turnOrder.__index = turnOrder local function new() return setmetatable({ - walked = {}, - notWalked = {}, + walked = PriorityQueue.new(sortInitiative), + notWalked = PriorityQueue.new(sortInitiative), isTurnsEnabled = true }, turnOrder) end -function turnOrder:updateOrder() - local notWalked = {} - local count = 1 - for id, char in ipairs(Tree.level.characters) do - char:try(Tree.behaviors.stats, function(stats) - notWalked[count] = id - count = count + 1 - end) - end - table.sort(notWalked, function(id_a, id_b) - local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats) - local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats) - return astats.initiative > bstats.initiative - end) - return res - end) - return res or false - end) - self.notWalked = notWalked - - print('[Level]: Turn order has been updated!') -end - function turnOrder:next() - table.insert(self.walked, self.notWalked[1]) - self.notWalked[1] = nil + local cur = self.notWalked:pop() + self.walked:insert(cur) +end - local notWalked = {} - for i = 1, #self.notWalked do - notWalked[i] = self.notWalked[i + 1] - print(notWalked[i], i) +function turnOrder:reorder() + local _walked, _notWalked = PriorityQueue.new(sortInitiative), PriorityQueue.new(sortInitiative) + local charId = self.walked:pop() + while charId do + _walked:insert(charId) + charId = self.walked:pop() end - self.notWalked = notWalked - - Tree.level.selector:select(nil) - - print('[Level]: Triggered next turn') - print('[Level]:', next(self.notWalked)) - if #self.notWalked == 0 then - self:updateOrder() - self.walked = {} + charId = self.notWalked:pop() + while charId do + _notWalked:insert(charId) + charId = self.notWalked:pop() end end -function turnOrder:toggleTurns() - self.isTurnsEnabled = not self.isTurnsEnabled +--- @param count number +function turnOrder:getOrder(count) + +end + +--- @param id Id +function turnOrder:add(id) + table.insert(self.characterIds, id) end return { new = new } From 6b2fb08a5b70fc5b527a737459d96d38903881d3 Mon Sep 17 00:00:00 2001 From: PeaAshMeter Date: Sun, 9 Nov 2025 06:35:51 +0300 Subject: [PATCH 9/9] Reimplement turn order management and update character initiative logic --- lib/character/behaviors/spellcaster.lua | 1 + lib/character/character.lua | 1 - lib/level/selector.lua | 8 +-- lib/level/turn_order.lua | 93 +++++++++++++++++++------ lib/spellbook.lua | 3 +- lib/utils/priority_queue.lua | 22 ++++-- main.lua | 14 ++-- 7 files changed, 106 insertions(+), 36 deletions(-) diff --git a/lib/character/behaviors/spellcaster.lua b/lib/character/behaviors/spellcaster.lua index f1ec586..c6cd9af 100644 --- a/lib/character/behaviors/spellcaster.lua +++ b/lib/character/behaviors/spellcaster.lua @@ -19,6 +19,7 @@ end function behavior:endCast() self.state = "idle" self.cast = nil + Tree.level.turnOrder:reorder() Tree.level.selector:unlock() end diff --git a/lib/character/character.lua b/lib/character/character.lua index a11e069..bde0c2c 100644 --- a/lib/character/character.lua +++ b/lib/character/character.lua @@ -35,7 +35,6 @@ local function spawn(name, spriteDir, position, size, initiative) } Tree.level.characters[char.id] = char - Tree.level.turnOrder:updateOrder() return char end diff --git a/lib/level/selector.lua b/lib/level/selector.lua index b89946d..c5db37d 100644 --- a/lib/level/selector.lua +++ b/lib/level/selector.lua @@ -25,7 +25,7 @@ function selector:update(dt) local selectedId = Tree.level.characterGrid:get(Vec3 { mousePosition.x, mousePosition.y }) if not self.id then - if selectedId ~= Tree.level.turnOrder.notWalked[1] and Tree.level.turnOrder.isTurnsEnabled then return end + if selectedId ~= Tree.level.turnOrder.current and Tree.level.turnOrder.isTurnsEnabled then return end return self:select(selectedId) else local char = Tree.level.characters[self.id] @@ -35,10 +35,8 @@ function selector:update(dt) -- тут какая-то страшная дичь, я даже не уверен что оно работает -- зато я точно уверен, что это надо было писать не так if not selectedId then self:select(selectedId) end - -- print(selectedId ~= next(Tree.level.turnOrder.notWalked), Tree.level.turnOrder.isTurnsEnabled) - if selectedId ~= Tree.level.turnOrder.notWalked[1] and Tree.level.turnOrder.isTurnsEnabled then return end - self:select(selectedId) - return + if selectedId ~= Tree.level.turnOrder.current and Tree.level.turnOrder.isTurnsEnabled then return end + return self:select(selectedId) end if b.cast:cast(char, mousePosition) then self:lock() diff --git a/lib/level/turn_order.lua b/lib/level/turn_order.lua index 427228f..6ea46ba 100644 --- a/lib/level/turn_order.lua +++ b/lib/level/turn_order.lua @@ -1,6 +1,6 @@ local PriorityQueue = require "lib.utils.priority_queue" -local sortInitiative = function(id_a, id_b) +local initiativeComparator = function(id_a, id_b) local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats) local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats) return astats.initiative > bstats.initiative @@ -11,49 +11,102 @@ local sortInitiative = function(id_a, id_b) end --- @class TurnOrder ---- @field characterIds Id[] ---- @field walked PriorityQueue ---- @field notWalked PriorityQueue +--- @field actedQueue PriorityQueue Очередь тех, кто сделал ход в текущем раунде +--- @field pendingQueue PriorityQueue Очередь тех, кто ждет своего хода в текущем раунде +--- @field current? Id Считаем того, кто сейчас ходит, отдельно, т.к. он ВСЕГДА первый в списке --- @field isTurnsEnabled boolean local turnOrder = {} turnOrder.__index = turnOrder local function new() return setmetatable({ - walked = PriorityQueue.new(sortInitiative), - notWalked = PriorityQueue.new(sortInitiative), - isTurnsEnabled = true + actedQueue = PriorityQueue.new(initiativeComparator), + pendingQueue = PriorityQueue.new(initiativeComparator), + isTurnsEnabled = true, }, turnOrder) end +--- Перемещаем активного персонажа в очередь сходивших +--- +--- Если в очереди на ход больше никого нет, заканчиваем раунд function turnOrder:next() - local cur = self.notWalked:pop() - self.walked:insert(cur) + Tree.level.selector.id = nil + self.actedQueue:insert(self.current) + local next = self.pendingQueue:peek() + if not next then return self:endRound() end + self.current = self.pendingQueue:pop() 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") + self.actedQueue, self.pendingQueue = self.pendingQueue, self.actedQueue + self.current = self.pendingQueue:pop() +end + +--- Пересчитать очередность хода function turnOrder:reorder() - local _walked, _notWalked = PriorityQueue.new(sortInitiative), PriorityQueue.new(sortInitiative) - local charId = self.walked:pop() - while charId do - _walked:insert(charId) - charId = self.walked:pop() + local _acted, _pending = PriorityQueue.new(initiativeComparator), PriorityQueue.new(initiativeComparator) + + --- сортировка отдельно кучи не ходивших и ходивших + while self.pendingQueue:peek() do + _pending:insert(self.pendingQueue:pop()) end - charId = self.notWalked:pop() - while charId do - _notWalked:insert(charId) - charId = self.notWalked:pop() + while self.actedQueue:peek() do + _acted:insert(self.actedQueue:pop()) end + + self.actedQueue, self.pendingQueue = _acted, _pending + + local t = {} + for id in self:getOrder(10) do + table.insert(t, id) + end + print("[TurnOrder]: next 10 turns") + print(table.concat(t, ", ")) end ---- @param count number +--- Итератор по бесконечной цикличной очереди хода +--- @param count integer? function turnOrder:getOrder(count) + local order = { self.current } + local _acted, _pending = self.actedQueue:copy(), self.pendingQueue:copy() + local i, j = 0, 0 + local nextTurn = false -- если вышли за пределы текущего хода, то сортируем список, чтобы поставить активного персонажа на свое место + return function() + -------------------- Очередь этого хода: активный + не сходившие + i = i + 1 + if count and count < 1 then return nil end + if count and i > count then return nil end + if i == 1 then return self.current end + + if _pending:peek() then + local next = _pending:pop() + table.insert(order, next) + return next + end + + -------------------- Очередь следующих ходов: цикл по всем персонажам в порядке инициативы + if not nextTurn then + while _acted:peek() do + table.insert(order, _acted:pop()) + end + table.sort(order, initiativeComparator) + nextTurn = true + end + + j = j + 1 + if j % #order == 0 then return order[#order] end + return order[j % #order] + end end --- @param id Id function turnOrder:add(id) - table.insert(self.characterIds, id) + self.actedQueue:insert(id) -- новые персонажи по умолчанию попадают в очередь следующего хода end return { new = new } diff --git a/lib/spellbook.lua b/lib/spellbook.lua index e117065..69be855 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -77,8 +77,9 @@ local regenerateMana = setmetatable({}, spell) function regenerateMana:cast(caster, target) caster:try(Tree.behaviors.stats, function(stats) stats.mana = 10 + stats.initiative = stats.initiative + 10 end) - print(caster.id, "has regenerated mana") + print(caster.id, "has regenerated mana and gained initiative") local sprite = caster:has(Tree.behaviors.sprite) if not sprite then return true end AnimationNode { diff --git a/lib/utils/priority_queue.lua b/lib/utils/priority_queue.lua index 89f2b32..0097604 100644 --- a/lib/utils/priority_queue.lua +++ b/lib/utils/priority_queue.lua @@ -1,11 +1,11 @@ ---@class PriorityQueue ----@field private data any[] # внутренний массив-куча (индексация с 1) ----@field private cmp fun(a:any, b:any):boolean # компаратор: true, если a выше по приоритету, чем b +---@field private data any[] внутренний массив-куча (индексация с 1) +---@field private cmp fun(a:any, b:any):boolean компаратор: true, если a выше по приоритету, чем b local PriorityQueue = {} PriorityQueue.__index = PriorityQueue ---Создать очередь с приоритетом. ----@param cmp fun(a:any, b:any):boolean|nil # если nil, используется a < b (мин-куча) +---@param cmp fun(a:any, b:any):boolean|nil если nil, используется a < b (мин-куча) ---@return PriorityQueue function PriorityQueue.new(cmp) local self = setmetatable({}, PriorityQueue) @@ -16,8 +16,8 @@ end -- ===== Внутренние утилиты ===== ----@param i integer @индекс узла ----@param j integer @индекс узла +---@param i integer индекс узла +---@param j integer индекс узла function PriorityQueue:_swap(i, j) self.data[i], self.data[j] = self.data[j], self.data[i] end @@ -103,4 +103,16 @@ function PriorityQueue:is_empty() return #self.data == 0 end +--- Shallow-копирование очереди +function PriorityQueue:copy() + local _data = {} + for i, v in ipairs(self.data) do + _data[i] = v + end + return setmetatable({ + data = _data, + cmp = self.cmp + }, PriorityQueue) +end + return PriorityQueue diff --git a/main.lua b/main.lua index 6fc77d0..cccd3a5 100644 --- a/main.lua +++ b/main.lua @@ -10,10 +10,15 @@ function love.conf(t) end function love.load() - character.spawn("Foodor", Tree.assets.files.sprites.character) - character.spawn("Baris", Tree.assets.files.sprites.character, Vec3 { 3, 3 }, nil, 12) - character.spawn("Foodor Jr", Tree.assets.files.sprites.character, Vec3 { 0, 3 }) - character.spawn("Baris Jr", Tree.assets.files.sprites.character, Vec3 { 0, 6 }) + character.spawn("Foodor", Tree.assets.files.sprites.character, nil, nil, 1) + character.spawn("Baris", Tree.assets.files.sprites.character, Vec3 { 3, 3 }, nil, 2) + character.spawn("Foodor Jr", Tree.assets.files.sprites.character, Vec3 { 0, 3 }, nil, 3) + character.spawn("Baris Jr", Tree.assets.files.sprites.character, Vec3 { 0, 6 }, nil, 4) + for id, _ in pairs(Tree.level.characters) do + Tree.level.turnOrder:add(id) + end + Tree.level.turnOrder:endRound() + print("Now playing:", Tree.level.turnOrder.current) love.window.setMode(1080, 720, { resizable = true, msaa = 4, vsync = true }) end @@ -30,6 +35,7 @@ function love.update(dt) -- удалить как только появится ui для людей if Tree.controls:isJustPressed("endTurnTest") then Tree.level.turnOrder:next() + print("Now playing:", Tree.level.turnOrder.current) end if Tree.controls:isJustPressed("toggleTurns") then print('toggle turns')