113 lines
4.3 KiB
Lua

local PriorityQueue = require "lib.utils.priority_queue"
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
end)
return res
end)
return res or false
end
--- @class TurnOrder
--- @field actedQueue PriorityQueue Очередь тех, кто сделал ход в текущем раунде
--- @field pendingQueue PriorityQueue Очередь тех, кто ждет своего хода в текущем раунде
--- @field current? Id Считаем того, кто сейчас ходит, отдельно, т.к. он ВСЕГДА первый в списке
--- @field isTurnsEnabled boolean
local turnOrder = {}
turnOrder.__index = turnOrder
local function new()
return setmetatable({
actedQueue = PriorityQueue.new(initiativeComparator),
pendingQueue = PriorityQueue.new(initiativeComparator),
isTurnsEnabled = true,
}, turnOrder)
end
--- Перемещаем активного персонажа в очередь сходивших
---
--- Если в очереди на ход больше никого нет, заканчиваем раунд
function turnOrder:next()
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 _acted, _pending = PriorityQueue.new(initiativeComparator), PriorityQueue.new(initiativeComparator)
--- сортировка отдельно кучи не ходивших и ходивших
while self.pendingQueue:peek() do
_pending:insert(self.pendingQueue:pop())
end
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 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)
self.actedQueue:insert(id) -- новые персонажи по умолчанию попадают в очередь следующего хода
end
return { new = new }