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() 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 }