2023-04-09 05:35:35 +00:00
|
|
|
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2022-03-30 08:33:56 +00:00
|
|
|
|
---@class GameLogic: Object
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@field public room Room
|
|
|
|
|
---@field public skill_table table<Event, TriggerSkill[]>
|
2023-06-08 17:10:16 +00:00
|
|
|
|
---@field public skill_priority_table table<Event, number[]>
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@field public refresh_skill_table table<Event, TriggerSkill[]>
|
|
|
|
|
---@field public skills string[]
|
|
|
|
|
---@field public game_event_stack Stack
|
2024-01-10 14:51:29 +00:00
|
|
|
|
---@field public cleaner_stack Stack
|
2023-03-26 09:32:45 +00:00
|
|
|
|
---@field public role_table string[][]
|
2023-06-08 17:10:16 +00:00
|
|
|
|
---@field public all_game_events GameEvent[]
|
|
|
|
|
---@field public event_recorder table<integer, GameEvent>
|
|
|
|
|
---@field public current_event_id integer
|
2022-03-28 14:24:30 +00:00
|
|
|
|
local GameLogic = class("GameLogic")
|
|
|
|
|
|
|
|
|
|
function GameLogic:initialize(room)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.room = room
|
|
|
|
|
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
2023-04-22 06:10:06 +00:00
|
|
|
|
self.skill_priority_table = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self.refresh_skill_table = {}
|
|
|
|
|
self.skills = {} -- skillName[]
|
2023-02-28 17:43:44 +00:00
|
|
|
|
self.game_event_stack = Stack:new()
|
2024-01-10 14:51:29 +00:00
|
|
|
|
self.cleaner_stack = Stack:new()
|
2023-06-08 17:10:16 +00:00
|
|
|
|
self.all_game_events = {}
|
|
|
|
|
self.event_recorder = {}
|
|
|
|
|
self.current_event_id = 0
|
2023-12-10 10:55:16 +00:00
|
|
|
|
self.specific_events_id = {
|
2024-02-04 07:29:39 +00:00
|
|
|
|
[GameEvent.Damage] = 1,
|
2023-12-10 10:55:16 +00:00
|
|
|
|
}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
self.role_table = {
|
|
|
|
|
{ "lord" },
|
|
|
|
|
{ "lord", "rebel" },
|
|
|
|
|
{ "lord", "rebel", "renegade" },
|
|
|
|
|
{ "lord", "loyalist", "rebel", "renegade" },
|
|
|
|
|
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
|
|
|
|
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
|
|
|
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
|
|
|
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
|
|
|
|
}
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function GameLogic:run()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
-- default logic
|
2023-04-27 06:15:08 +00:00
|
|
|
|
local room = self.room
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.shuffle(self.room.players)
|
|
|
|
|
self:assignRoles()
|
2023-06-30 20:12:19 +00:00
|
|
|
|
self.room.game_started = true
|
2023-04-27 06:15:08 +00:00
|
|
|
|
room:doBroadcastNotify("StartGame", "")
|
|
|
|
|
room:adjustSeats()
|
2023-10-07 15:13:17 +00:00
|
|
|
|
--[[ 因为未完工,在release版暂时不启用。
|
2023-10-06 19:22:57 +00:00
|
|
|
|
for _, p in ipairs(room.players) do
|
|
|
|
|
p.ai = SmartAI:new(p)
|
|
|
|
|
end
|
2023-10-07 15:13:17 +00:00
|
|
|
|
--]]
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self:chooseGenerals()
|
2023-04-23 13:10:07 +00:00
|
|
|
|
|
|
|
|
|
self:buildPlayerCircle()
|
|
|
|
|
self:broadcastGeneral()
|
|
|
|
|
self:prepareDrawPile()
|
|
|
|
|
self:attachSkillToPlayers()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self:prepareForStart()
|
2023-04-23 13:10:07 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
self:action()
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-07 06:55:28 +00:00
|
|
|
|
local function execGameEvent(type, ...)
|
|
|
|
|
local event = GameEvent:new(type, ...)
|
|
|
|
|
local _, ret = event:exec()
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function GameLogic:assignRoles()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local room = self.room
|
|
|
|
|
local n = #room.players
|
|
|
|
|
local roles = self.role_table[n]
|
|
|
|
|
table.shuffle(roles)
|
|
|
|
|
|
|
|
|
|
for i = 1, n do
|
|
|
|
|
local p = room.players[i]
|
|
|
|
|
p.role = roles[i]
|
|
|
|
|
if p.role == "lord" then
|
2023-01-17 14:34:15 +00:00
|
|
|
|
p.role_shown = true
|
2022-04-30 07:27:56 +00:00
|
|
|
|
room:broadcastProperty(p, "role")
|
|
|
|
|
else
|
|
|
|
|
room:notifyProperty(p, p, "role")
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-03-25 04:28:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
function GameLogic:chooseGenerals()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local room = self.room
|
2023-03-20 06:53:56 +00:00
|
|
|
|
local generalNum = room.settings.generalNum
|
2023-04-19 06:07:16 +00:00
|
|
|
|
local n = room.settings.enableDeputy and 2 or 1
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local lord = room:getLord()
|
2023-07-16 11:17:03 +00:00
|
|
|
|
local lord_generals = {}
|
2023-04-30 10:55:59 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if lord ~= nil then
|
|
|
|
|
room.current = lord
|
2023-09-27 13:02:22 +00:00
|
|
|
|
local generals = room:getNGenerals(generalNum)
|
|
|
|
|
lord_generals = room:askForGeneral(lord, generals, n)
|
2023-07-16 11:17:03 +00:00
|
|
|
|
local lord_general, deputy
|
|
|
|
|
if type(lord_generals) == "table" then
|
|
|
|
|
deputy = lord_generals[2]
|
|
|
|
|
lord_general = lord_generals[1]
|
|
|
|
|
else
|
|
|
|
|
lord_general = lord_generals
|
|
|
|
|
lord_generals = {lord_general}
|
2023-04-19 06:07:16 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-09-27 13:02:22 +00:00
|
|
|
|
generals = table.filter(generals, function(g) return not table.contains(lord_generals, g) end)
|
|
|
|
|
room:returnToGeneralPile(generals)
|
|
|
|
|
|
2023-04-04 18:21:59 +00:00
|
|
|
|
room:setPlayerGeneral(lord, lord_general, true)
|
2023-08-03 12:34:43 +00:00
|
|
|
|
room:askForChooseKingdom({lord})
|
2022-04-30 07:27:56 +00:00
|
|
|
|
room:broadcastProperty(lord, "general")
|
2023-08-13 04:35:23 +00:00
|
|
|
|
room:broadcastProperty(lord, "kingdom")
|
2023-04-19 06:07:16 +00:00
|
|
|
|
room:setDeputyGeneral(lord, deputy)
|
|
|
|
|
room:broadcastProperty(lord, "deputyGeneral")
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-12-20 04:51:54 +00:00
|
|
|
|
local nonlord = room:getOtherPlayers(lord, true)
|
2023-09-27 13:02:22 +00:00
|
|
|
|
local generals = room:getNGenerals(#nonlord * generalNum)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.shuffle(generals)
|
2023-09-27 13:02:22 +00:00
|
|
|
|
for i, p in ipairs(nonlord) do
|
|
|
|
|
local arg = table.slice(generals, (i - 1) * generalNum + 1, i * generalNum + 1)
|
2023-04-19 06:07:16 +00:00
|
|
|
|
p.request_data = json.encode{ arg, n }
|
|
|
|
|
p.default_reply = table.random(arg, n)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-14 12:48:08 +00:00
|
|
|
|
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
2022-04-30 07:27:56 +00:00
|
|
|
|
room:doBroadcastRequest("AskForGeneral", nonlord)
|
2023-04-30 10:55:59 +00:00
|
|
|
|
|
2023-09-27 13:02:22 +00:00
|
|
|
|
local selected = {}
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, p in ipairs(nonlord) do
|
|
|
|
|
if p.general == "" and p.reply_ready then
|
2023-09-27 13:02:22 +00:00
|
|
|
|
local general_ret = json.decode(p.client_reply)
|
|
|
|
|
local general = general_ret[1]
|
|
|
|
|
local deputy = general_ret[2]
|
|
|
|
|
table.insertTableIfNeed(selected, general_ret)
|
2023-04-30 10:55:59 +00:00
|
|
|
|
room:setPlayerGeneral(p, general, true, true)
|
2023-04-19 06:07:16 +00:00
|
|
|
|
room:setDeputyGeneral(p, deputy)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
else
|
2023-04-30 10:55:59 +00:00
|
|
|
|
room:setPlayerGeneral(p, p.default_reply[1], true, true)
|
2023-04-19 06:07:16 +00:00
|
|
|
|
room:setDeputyGeneral(p, p.default_reply[2])
|
2022-03-30 06:14:40 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
p.default_reply = ""
|
|
|
|
|
end
|
2023-04-30 10:55:59 +00:00
|
|
|
|
|
2023-09-27 13:02:22 +00:00
|
|
|
|
generals = table.filter(generals, function(g) return not table.contains(selected, g) end)
|
|
|
|
|
room:returnToGeneralPile(generals)
|
|
|
|
|
|
2023-07-16 11:17:03 +00:00
|
|
|
|
room:askForChooseKingdom(nonlord)
|
2022-03-30 06:14:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-23 13:10:07 +00:00
|
|
|
|
function GameLogic:buildPlayerCircle()
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
|
|
|
|
room.alive_players = {table.unpack(players)}
|
|
|
|
|
for i = 1, #players - 1 do
|
|
|
|
|
players[i].next = players[i + 1]
|
|
|
|
|
end
|
|
|
|
|
players[#players].next = players[1]
|
2023-04-23 13:10:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameLogic:broadcastGeneral()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
for _, p in ipairs(players) do
|
|
|
|
|
assert(p.general ~= "")
|
|
|
|
|
local general = Fk.generals[p.general]
|
2023-04-19 06:07:16 +00:00
|
|
|
|
local deputy = Fk.generals[p.deputyGeneral]
|
2023-08-24 13:37:06 +00:00
|
|
|
|
p.maxHp = p:getGeneralMaxHp()
|
2023-04-19 06:07:16 +00:00
|
|
|
|
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
|
|
|
|
|
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
-- TODO: setup AI here
|
|
|
|
|
|
|
|
|
|
if p.role ~= "lord" then
|
|
|
|
|
room:broadcastProperty(p, "general")
|
2023-04-30 10:55:59 +00:00
|
|
|
|
room:broadcastProperty(p, "kingdom")
|
2023-04-19 06:07:16 +00:00
|
|
|
|
room:broadcastProperty(p, "deputyGeneral")
|
2022-04-30 07:27:56 +00:00
|
|
|
|
elseif #players >= 5 then
|
|
|
|
|
p.maxHp = p.maxHp + 1
|
|
|
|
|
p.hp = p.hp + 1
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
room:broadcastProperty(p, "maxHp")
|
|
|
|
|
room:broadcastProperty(p, "hp")
|
2023-04-13 12:17:39 +00:00
|
|
|
|
room:broadcastProperty(p, "shield")
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2023-04-23 13:10:07 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-04-23 13:10:07 +00:00
|
|
|
|
function GameLogic:prepareDrawPile()
|
|
|
|
|
local room = self.room
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local allCardIds = Fk:getAllCardIds()
|
2023-05-20 08:00:03 +00:00
|
|
|
|
|
|
|
|
|
for i = #allCardIds, 1, -1 do
|
|
|
|
|
if Fk:getCardById(allCardIds[i]).is_derived then
|
|
|
|
|
local id = allCardIds[i]
|
|
|
|
|
table.removeOne(allCardIds, id)
|
|
|
|
|
table.insert(room.void, id)
|
|
|
|
|
room:setCardArea(id, Card.Void, nil)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.shuffle(allCardIds)
|
|
|
|
|
room.draw_pile = allCardIds
|
|
|
|
|
for _, id in ipairs(room.draw_pile) do
|
2022-09-14 05:01:10 +00:00
|
|
|
|
self.room:setCardArea(id, Card.DrawPile, nil)
|
|
|
|
|
end
|
2023-04-23 13:10:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameLogic:attachSkillToPlayers()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
2022-09-14 05:01:10 +00:00
|
|
|
|
|
2023-04-15 04:06:24 +00:00
|
|
|
|
local addRoleModSkills = function(player, skillName)
|
|
|
|
|
local skill = Fk.skills[skillName]
|
2024-01-24 19:13:57 +00:00
|
|
|
|
if not skill then
|
|
|
|
|
fk.qCritical("Skill: "..skillName.." doesn't exist!")
|
|
|
|
|
return
|
|
|
|
|
end
|
2023-04-15 04:06:24 +00:00
|
|
|
|
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-30 10:55:59 +00:00
|
|
|
|
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-15 04:06:24 +00:00
|
|
|
|
room:handleAddLoseSkills(player, skillName, nil, false)
|
|
|
|
|
end
|
2022-09-14 05:01:10 +00:00
|
|
|
|
for _, p in ipairs(room.alive_players) do
|
2023-01-21 16:49:11 +00:00
|
|
|
|
local skills = Fk.generals[p.general].skills
|
|
|
|
|
for _, s in ipairs(skills) do
|
2023-04-15 04:06:24 +00:00
|
|
|
|
addRoleModSkills(p, s.name)
|
2023-01-21 16:49:11 +00:00
|
|
|
|
end
|
2023-02-15 11:54:35 +00:00
|
|
|
|
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
|
2023-04-15 04:06:24 +00:00
|
|
|
|
addRoleModSkills(p, sname)
|
2023-02-15 11:54:35 +00:00
|
|
|
|
end
|
2023-04-19 06:07:16 +00:00
|
|
|
|
|
|
|
|
|
local deputy = Fk.generals[p.deputyGeneral]
|
|
|
|
|
if deputy then
|
|
|
|
|
skills = deputy.skills
|
|
|
|
|
for _, s in ipairs(skills) do
|
|
|
|
|
addRoleModSkills(p, s.name)
|
|
|
|
|
end
|
|
|
|
|
for _, sname in ipairs(deputy.other_skills) do
|
|
|
|
|
addRoleModSkills(p, sname)
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2023-04-23 13:10:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameLogic:prepareForStart()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
self:addTriggerSkill(GameRule)
|
|
|
|
|
for _, trig in ipairs(Fk.global_trigger) do
|
|
|
|
|
self:addTriggerSkill(trig)
|
|
|
|
|
end
|
2022-12-18 04:52:52 +00:00
|
|
|
|
|
|
|
|
|
self.room:sendLog{ type = "$GameStart" }
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-30 06:14:40 +00:00
|
|
|
|
function GameLogic:action()
|
2023-05-20 08:00:03 +00:00
|
|
|
|
self:trigger(fk.GamePrepared)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local room = self.room
|
|
|
|
|
|
2023-03-07 06:55:28 +00:00
|
|
|
|
execGameEvent(GameEvent.DrawInitial)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
while true do
|
2023-04-08 12:45:55 +00:00
|
|
|
|
execGameEvent(GameEvent.Round)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if room.game_finished then break end
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@param skill TriggerSkill
|
|
|
|
|
function GameLogic:addTriggerSkill(skill)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
if skill == nil or table.contains(self.skills, skill.name) then
|
|
|
|
|
return
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.insert(self.skills, skill.name)
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, event in ipairs(skill.refresh_events) do
|
|
|
|
|
if self.refresh_skill_table[event] == nil then
|
|
|
|
|
self.refresh_skill_table[event] = {}
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.insert(self.refresh_skill_table[event], skill)
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2022-04-30 07:27:56 +00:00
|
|
|
|
for _, event in ipairs(skill.events) do
|
|
|
|
|
if self.skill_table[event] == nil then
|
|
|
|
|
self.skill_table[event] = {}
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
table.insert(self.skill_table[event], skill)
|
2023-04-22 06:10:06 +00:00
|
|
|
|
|
|
|
|
|
if self.skill_priority_table[event] == nil then
|
|
|
|
|
self.skill_priority_table[event] = {}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local priority_tab = self.skill_priority_table[event]
|
|
|
|
|
local prio = skill.priority_table[event]
|
|
|
|
|
if not table.contains(priority_tab, prio) then
|
|
|
|
|
for i, v in ipairs(priority_tab) do
|
|
|
|
|
if v < prio then
|
|
|
|
|
table.insert(priority_tab, i, prio)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not table.contains(priority_tab, prio) then
|
|
|
|
|
table.insert(priority_tab, prio)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not table.contains(self.skill_priority_table[event],
|
|
|
|
|
skill.priority_table[event]) then
|
|
|
|
|
|
|
|
|
|
table.insert(self.skill_priority_table[event],
|
|
|
|
|
skill.priority_table[event])
|
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if skill.visible then
|
|
|
|
|
if (Fk.related_skills[skill.name] == nil) then return end
|
|
|
|
|
for _, s in ipairs(Fk.related_skills[skill.name]) do
|
|
|
|
|
if (s.class == TriggerSkill) then
|
|
|
|
|
self:addTriggerSkill(s)
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@param event Event
|
2023-12-03 11:35:14 +00:00
|
|
|
|
---@param target? ServerPlayer
|
|
|
|
|
---@param data? any
|
2023-04-22 07:52:26 +00:00
|
|
|
|
function GameLogic:trigger(event, target, data, refresh_only)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
local room = self.room
|
|
|
|
|
local broken = false
|
|
|
|
|
local skills = self.skill_table[event] or {}
|
2023-06-11 08:22:11 +00:00
|
|
|
|
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
2023-04-22 06:10:06 +00:00
|
|
|
|
local _target = room.current -- for iteration
|
2023-02-15 11:54:35 +00:00
|
|
|
|
local player = _target
|
2023-10-07 15:00:25 +00:00
|
|
|
|
local cur_event = self:getCurrentEvent() or {}
|
|
|
|
|
-- 如果当前事件被杀,就强制只refresh
|
|
|
|
|
-- 因为被杀的事件再进行正常trigger只可能在cleaner和exit了
|
|
|
|
|
refresh_only = refresh_only or cur_event.killed
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-04-22 06:10:06 +00:00
|
|
|
|
if #skills_to_refresh > 0 then repeat do
|
2022-04-30 07:27:56 +00:00
|
|
|
|
-- refresh skills. This should not be broken
|
|
|
|
|
for _, skill in ipairs(skills_to_refresh) do
|
|
|
|
|
if skill:canRefresh(event, target, player, data) then
|
|
|
|
|
skill:refresh(event, target, player, data)
|
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
player = player.next
|
2023-04-22 06:10:06 +00:00
|
|
|
|
end until player == _target end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
2023-04-22 07:52:26 +00:00
|
|
|
|
if #skills == 0 or refresh_only then return end
|
2023-04-22 06:10:06 +00:00
|
|
|
|
|
|
|
|
|
local prio_tab = self.skill_priority_table[event]
|
|
|
|
|
local prev_prio = math.huge
|
|
|
|
|
|
|
|
|
|
for _, prio in ipairs(prio_tab) do
|
|
|
|
|
if broken then break end
|
|
|
|
|
if prio >= prev_prio then
|
|
|
|
|
-- continue
|
|
|
|
|
goto trigger_loop_continue
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2023-04-22 06:10:06 +00:00
|
|
|
|
repeat do
|
2023-09-19 06:27:54 +00:00
|
|
|
|
local invoked_skills = {}
|
|
|
|
|
local filter_func = function(skill)
|
2023-04-22 06:10:06 +00:00
|
|
|
|
return skill.priority_table[event] == prio and
|
2023-09-19 06:27:54 +00:00
|
|
|
|
not table.contains(invoked_skills, skill) and
|
2023-04-22 06:10:06 +00:00
|
|
|
|
skill:triggerable(event, target, player, data)
|
2023-09-19 06:27:54 +00:00
|
|
|
|
end
|
2023-04-22 06:10:06 +00:00
|
|
|
|
|
2023-09-19 06:27:54 +00:00
|
|
|
|
local skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
while #skill_names > 0 do
|
2023-04-22 06:10:06 +00:00
|
|
|
|
local skill_name = prio <= 0 and table.random(skill_names) or
|
|
|
|
|
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
|
|
|
|
|
|
|
|
|
local skill = skill_name == "game_rule" and GameRule
|
|
|
|
|
or Fk.skills[skill_name]
|
|
|
|
|
|
2023-09-19 06:27:54 +00:00
|
|
|
|
table.insert(invoked_skills, skill)
|
2022-04-30 07:27:56 +00:00
|
|
|
|
broken = skill:trigger(event, target, player, data)
|
2023-09-19 06:27:54 +00:00
|
|
|
|
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
2023-04-22 06:10:06 +00:00
|
|
|
|
|
|
|
|
|
broken = broken or (event == fk.AskForPeaches
|
2024-01-29 02:19:10 +00:00
|
|
|
|
and room:getPlayerById(data.who).hp > 0) or
|
|
|
|
|
(table.contains({fk.PreDamage, fk.DamageCaused, fk.DamageInflicted}, event) and data.damage < 1) or
|
|
|
|
|
cur_event.killed
|
2023-04-22 06:10:06 +00:00
|
|
|
|
|
2022-04-01 12:51:01 +00:00
|
|
|
|
if broken then break end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
end
|
2022-04-01 12:51:01 +00:00
|
|
|
|
|
2023-04-22 06:10:06 +00:00
|
|
|
|
if broken then break end
|
2022-03-28 14:24:30 +00:00
|
|
|
|
|
2023-04-22 06:10:06 +00:00
|
|
|
|
player = player.next
|
|
|
|
|
end until player == _target
|
|
|
|
|
|
|
|
|
|
prev_prio = prio
|
|
|
|
|
::trigger_loop_continue::
|
|
|
|
|
end
|
2022-04-30 07:27:56 +00:00
|
|
|
|
|
|
|
|
|
return broken
|
2022-03-28 14:24:30 +00:00
|
|
|
|
end
|
|
|
|
|
|
2024-01-10 14:51:29 +00:00
|
|
|
|
-- 此为启动事件管理器并启动第一个事件的初始函数
|
|
|
|
|
function GameLogic:start()
|
|
|
|
|
local root_event = GameEvent:new(GameEvent.Game)
|
|
|
|
|
|
|
|
|
|
self:pushEvent(root_event)
|
|
|
|
|
|
|
|
|
|
-- 此时的协程:room.main_co
|
|
|
|
|
-- 事件管理器协程,同时也是Game事件
|
|
|
|
|
-- 当新事件想要exec时,就切回此处,由这里负责调度协程
|
|
|
|
|
-- 一个事件结束后也切回此处,然后resume
|
|
|
|
|
local co = coroutine.create(root_event.main_func)
|
|
|
|
|
root_event._co = co
|
|
|
|
|
|
|
|
|
|
local jump_to -- shutdown函数用
|
|
|
|
|
|
|
|
|
|
while true do
|
|
|
|
|
-- 对于cleaner和正常事件,处理更后面来的
|
|
|
|
|
local ne = self:getCurrentEvent()
|
|
|
|
|
local ce = self:getCurrentCleaner()
|
|
|
|
|
local e = ce and (ce.id >= ne.id and ce or ne) or ne
|
|
|
|
|
|
|
|
|
|
-- 如果正在jump的话,判断是否需要继续clean,否则正常继续
|
|
|
|
|
if e == ne and jump_to ~= nil then
|
|
|
|
|
e.interrupted = true
|
|
|
|
|
e.killed = e ~= jump_to
|
|
|
|
|
self:clearEvent(e)
|
|
|
|
|
coroutine.close(e._co)
|
2024-02-04 07:54:51 +00:00
|
|
|
|
e.status = "dead"
|
2024-01-10 14:51:29 +00:00
|
|
|
|
if e == jump_to then jump_to = nil end -- shutdown结束了
|
|
|
|
|
e = self:getCurrentCleaner()
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-11 10:36:05 +00:00
|
|
|
|
if not e then -- 没有事件,按理说不应该,平局处理
|
2024-04-01 16:56:04 +00:00
|
|
|
|
self.room:sendLog{
|
|
|
|
|
type = "#NoEventDraw",
|
|
|
|
|
toast = true,
|
|
|
|
|
}
|
2024-01-11 10:36:05 +00:00
|
|
|
|
self.room:gameOver("")
|
|
|
|
|
end
|
|
|
|
|
|
2024-01-10 14:51:29 +00:00
|
|
|
|
-- ret, evt解释:
|
|
|
|
|
-- * true, nil: 中止
|
|
|
|
|
-- * false, nil: 正常结束
|
|
|
|
|
-- * true, GameEvent: 中止直到某event
|
|
|
|
|
-- * false, GameEvent: 未结束,插入新event
|
|
|
|
|
-- 若jump_to不为nil,表示正在中断至某某事件
|
|
|
|
|
local ret, evt = self:resumeEvent(e)
|
|
|
|
|
if evt == nil then
|
|
|
|
|
e.interrupted = ret
|
|
|
|
|
self:clearEvent(e)
|
|
|
|
|
coroutine.close(e._co)
|
2024-02-04 07:54:51 +00:00
|
|
|
|
e.status = "dead"
|
2024-01-10 14:51:29 +00:00
|
|
|
|
elseif ret == true then
|
|
|
|
|
-- 跳到越早发生的事件越好
|
|
|
|
|
if not jump_to then
|
|
|
|
|
jump_to = evt
|
|
|
|
|
else
|
|
|
|
|
jump_to = jump_to.id < evt.id and jump_to or evt
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@param event GameEvent
|
|
|
|
|
function GameLogic:pushEvent(event)
|
|
|
|
|
self.game_event_stack:push(event)
|
|
|
|
|
|
|
|
|
|
self.current_event_id = self.current_event_id + 1
|
|
|
|
|
event.id = self.current_event_id
|
|
|
|
|
self.all_game_events[event.id] = event
|
|
|
|
|
self.event_recorder[event.event] = self.event_recorder[event.event] or {}
|
|
|
|
|
table.insert(self.event_recorder[event.event], event)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 一般来说从GameEvent:exec切回start再被start调用
|
|
|
|
|
-- 作用是启动新事件 都是结构差不多的函数
|
|
|
|
|
---@param event GameEvent
|
|
|
|
|
---@return boolean, GameEvent?
|
|
|
|
|
function GameLogic:resumeEvent(event, ...)
|
|
|
|
|
local ret, evt
|
|
|
|
|
|
|
|
|
|
local co = event._co
|
|
|
|
|
|
|
|
|
|
while true do
|
|
|
|
|
local err, yield_result, extra_yield_result = coroutine.resume(co, ...)
|
|
|
|
|
|
|
|
|
|
if err == false then
|
|
|
|
|
-- handle error, then break
|
|
|
|
|
if not string.find(yield_result, "__manuallyBreak") then
|
|
|
|
|
fk.qCritical(yield_result .. "\n" .. debug.traceback(co))
|
|
|
|
|
end
|
|
|
|
|
ret = true
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if yield_result == "__handleRequest" then
|
|
|
|
|
-- yield to requestLoop
|
|
|
|
|
coroutine.yield(yield_result, extra_yield_result)
|
|
|
|
|
|
|
|
|
|
elseif type(yield_result) == "table" and yield_result.class
|
|
|
|
|
and yield_result:isInstanceOf(GameEvent) then
|
|
|
|
|
|
|
|
|
|
if extra_yield_result == "__newEvent" then
|
|
|
|
|
ret, evt = false, yield_result
|
|
|
|
|
break
|
|
|
|
|
elseif extra_yield_result == "__breakEvent" then
|
|
|
|
|
ret, evt = true, yield_result
|
|
|
|
|
if event.event ~= GameEvent.ClearEvent then break end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
elseif yield_result == "__breakEvent" then
|
|
|
|
|
ret = true
|
|
|
|
|
if event.event ~= GameEvent.ClearEvent then break end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
ret = false
|
|
|
|
|
event.exec_ret = yield_result
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return ret, evt
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@return GameEvent
|
|
|
|
|
function GameLogic:getCurrentCleaner()
|
|
|
|
|
return self.cleaner_stack.t[self.cleaner_stack.p]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 事件中的清理。
|
|
|
|
|
-- cleaner单独开协程运行,exitFunc须转到上个事件的协程内执行
|
|
|
|
|
-- 注意插入新event
|
|
|
|
|
---@param event GameEvent
|
|
|
|
|
function GameLogic:clearEvent(event)
|
|
|
|
|
if event.event == GameEvent.ClearEvent then return end
|
2024-02-04 07:54:51 +00:00
|
|
|
|
if event.status == "exiting" then return end
|
|
|
|
|
event.status = "exiting"
|
2024-01-10 14:51:29 +00:00
|
|
|
|
local ce = GameEvent(GameEvent.ClearEvent, event)
|
|
|
|
|
ce.id = self.current_event_id
|
|
|
|
|
local co = coroutine.create(ce.main_func)
|
|
|
|
|
ce._co = co
|
|
|
|
|
self.cleaner_stack:push(ce)
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-09 03:44:19 +00:00
|
|
|
|
---@return GameEvent
|
2023-03-04 17:28:59 +00:00
|
|
|
|
function GameLogic:getCurrentEvent()
|
|
|
|
|
return self.game_event_stack.t[self.game_event_stack.p]
|
|
|
|
|
end
|
|
|
|
|
|
2023-10-07 15:05:27 +00:00
|
|
|
|
---@param eventType integer
|
|
|
|
|
function GameLogic:getMostRecentEvent(eventType)
|
|
|
|
|
return self:getCurrentEvent():findParent(eventType, true)
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-19 06:27:54 +00:00
|
|
|
|
--- 如果当前事件刚好是技能生效事件,就返回那个技能名,否则返回空串。
|
|
|
|
|
function GameLogic:getCurrentSkillName()
|
|
|
|
|
local skillEvent = self:getCurrentEvent()
|
|
|
|
|
local ret = ""
|
|
|
|
|
if skillEvent.event == GameEvent.SkillEffect then
|
|
|
|
|
local _, _, _skill = table.unpack(skillEvent.data)
|
|
|
|
|
local skill = _skill.main_skill and _skill.main_skill or _skill
|
|
|
|
|
ret = skill.name
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-08 17:10:16 +00:00
|
|
|
|
-- 在指定历史范围中找至多n个符合条件的事件
|
|
|
|
|
---@param eventType integer @ 要查找的事件类型
|
|
|
|
|
---@param n integer @ 最多找多少个
|
|
|
|
|
---@param func fun(e: GameEvent): boolean @ 过滤用的函数
|
|
|
|
|
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
|
|
|
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
|
|
|
|
function GameLogic:getEventsOfScope(eventType, n, func, scope)
|
|
|
|
|
scope = scope or Player.HistoryTurn
|
|
|
|
|
local event = self:getCurrentEvent()
|
|
|
|
|
local start_event ---@type GameEvent
|
|
|
|
|
if scope == Player.HistoryGame then
|
|
|
|
|
start_event = self.all_game_events[1]
|
|
|
|
|
elseif scope == Player.HistoryRound then
|
2023-06-10 12:54:48 +00:00
|
|
|
|
start_event = event:findParent(GameEvent.Round, true)
|
2023-06-08 17:10:16 +00:00
|
|
|
|
elseif scope == Player.HistoryTurn then
|
2023-06-10 12:54:48 +00:00
|
|
|
|
start_event = event:findParent(GameEvent.Turn, true)
|
2023-06-08 17:10:16 +00:00
|
|
|
|
elseif scope == Player.HistoryPhase then
|
2023-06-10 12:54:48 +00:00
|
|
|
|
start_event = event:findParent(GameEvent.Phase, true)
|
2023-06-08 17:10:16 +00:00
|
|
|
|
end
|
2023-12-09 13:57:47 +00:00
|
|
|
|
if not start_event then return {} end
|
2023-06-08 17:10:16 +00:00
|
|
|
|
return start_event:searchEvents(eventType, n, func)
|
|
|
|
|
end
|
|
|
|
|
|
2024-02-04 07:55:44 +00:00
|
|
|
|
-- 在指定历史范围中找符合条件的事件(逆序)
|
|
|
|
|
---@param eventType integer @ 要查找的事件类型
|
|
|
|
|
---@param func fun(e: GameEvent): boolean @ 过滤用的函数
|
|
|
|
|
---@param n integer @ 最多找多少个
|
|
|
|
|
---@param end_id integer @ 查询历史范围:从最后的事件开始逆序查找直到id为end_id的事件(不含)
|
|
|
|
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
|
|
|
|
function GameLogic:getEventsByRule(eventType, n, func, end_id)
|
|
|
|
|
local ret = {}
|
|
|
|
|
local events = self.event_recorder[eventType] or Util.DummyTable
|
|
|
|
|
for i = #events, 1, -1 do
|
|
|
|
|
local e = events[i]
|
|
|
|
|
if e.id <= end_id then break end
|
|
|
|
|
if func(e) then
|
|
|
|
|
table.insert(ret, e)
|
|
|
|
|
if #ret >= n then break end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--- 获取实际的伤害事件
|
|
|
|
|
---@param n integer @ 最多找多少个
|
|
|
|
|
---@param func fun(e: GameEvent): boolean @ 过滤用的函数
|
|
|
|
|
---@param scope? integer @ 查询历史范围,只能是当前阶段/回合/轮次
|
|
|
|
|
---@param end_id? integer @ 查询历史范围:从最后的事件开始逆序查找直到id为end_id的事件(不含)
|
|
|
|
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
|
|
|
|
function GameLogic:getActualDamageEvents(n, func, scope, end_id)
|
|
|
|
|
if not end_id then
|
|
|
|
|
scope = scope or Player.HistoryTurn
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
n = n or 1
|
|
|
|
|
func = func or Util.TrueFunc
|
|
|
|
|
|
|
|
|
|
local eventType = GameEvent.Damage
|
|
|
|
|
local ret = {}
|
|
|
|
|
local endIdRecorded
|
|
|
|
|
local tempEvents = {}
|
|
|
|
|
|
|
|
|
|
local addTempEvents = function(reverse)
|
|
|
|
|
if #tempEvents > 0 and #ret < n then
|
|
|
|
|
table.sort(tempEvents, function(a, b)
|
|
|
|
|
if reverse then
|
|
|
|
|
return a.data[1].dealtRecorderId > b.data[1].dealtRecorderId
|
|
|
|
|
else
|
|
|
|
|
return a.data[1].dealtRecorderId < b.data[1].dealtRecorderId
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
for _, e in ipairs(tempEvents) do
|
|
|
|
|
table.insert(ret, e)
|
|
|
|
|
if #ret >= n then return true end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
endIdRecorded = nil
|
|
|
|
|
tempEvents = {}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if scope then
|
|
|
|
|
local event = self:getCurrentEvent()
|
|
|
|
|
local start_event ---@type GameEvent
|
|
|
|
|
if scope == Player.HistoryGame then
|
|
|
|
|
start_event = self.all_game_events[1]
|
|
|
|
|
elseif scope == Player.HistoryRound then
|
|
|
|
|
start_event = event:findParent(GameEvent.Round, true)
|
|
|
|
|
elseif scope == Player.HistoryTurn then
|
|
|
|
|
start_event = event:findParent(GameEvent.Turn, true)
|
|
|
|
|
elseif scope == Player.HistoryPhase then
|
|
|
|
|
start_event = event:findParent(GameEvent.Phase, true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not start_event then return {} end
|
|
|
|
|
|
|
|
|
|
local events = self.event_recorder[eventType] or Util.DummyTable
|
|
|
|
|
local from = start_event.id
|
|
|
|
|
local to = start_event.end_id
|
|
|
|
|
if math.abs(to) == 1 then to = #self.all_game_events end
|
|
|
|
|
|
|
|
|
|
for _, v in ipairs(events) do
|
|
|
|
|
local damageStruct = v.data[1]
|
|
|
|
|
if damageStruct.dealtRecorderId then
|
|
|
|
|
if endIdRecorded and v.id > endIdRecorded then
|
|
|
|
|
local result = addTempEvents()
|
|
|
|
|
if result then
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if v.id >= from and v.id <= to then
|
|
|
|
|
if not endIdRecorded and v.end_id > -1 and v.end_id > v.id then
|
|
|
|
|
endIdRecorded = v.end_id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if func(v) then
|
|
|
|
|
if endIdRecorded then
|
|
|
|
|
table.insert(tempEvents, v)
|
|
|
|
|
else
|
|
|
|
|
table.insert(ret, v)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if #ret >= n then break end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
addTempEvents()
|
|
|
|
|
else
|
|
|
|
|
local events = self.event_recorder[eventType] or Util.DummyTable
|
|
|
|
|
|
|
|
|
|
for i = #events, 1, -1 do
|
|
|
|
|
local e = events[i]
|
|
|
|
|
if e.id <= end_id then break end
|
|
|
|
|
|
|
|
|
|
local damageStruct = e.data[1]
|
|
|
|
|
if damageStruct.dealtRecorderId then
|
|
|
|
|
if e.end_id == -1 or (endIdRecorded and endIdRecorded > e.end_id) then
|
|
|
|
|
local result = addTempEvents(true)
|
|
|
|
|
if result then
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if func(e) then
|
|
|
|
|
table.insert(ret, e)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
endIdRecorded = e.end_id
|
|
|
|
|
if func(e) then
|
|
|
|
|
table.insert(tempEvents, e)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if #ret >= n then break end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
addTempEvents(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2024-04-01 16:56:04 +00:00
|
|
|
|
--检测最近的伤害事件是否由执行牌的效果触发,即通常描述的使用牌对目标角色造成伤害
|
|
|
|
|
---@param is_exact? bool @ 是否进一步判定使用者和来源是否一致(默认为true)
|
|
|
|
|
---@return bool
|
|
|
|
|
function GameLogic:damageByCardEffect(is_exact)
|
|
|
|
|
is_exact = (is_exact == nil) and true or is_exact
|
|
|
|
|
local d_event = self:getCurrentEvent():findParent(GameEvent.Damage, true)
|
|
|
|
|
if d_event == nil then return false end
|
|
|
|
|
local damage = d_event.data[1]
|
|
|
|
|
if damage.chain or damage.card == nil then return false end
|
|
|
|
|
local c_event = d_event:findParent(GameEvent.CardEffect, false, 2)
|
|
|
|
|
if c_event == nil then return false end
|
|
|
|
|
return damage.card == c_event.data[1].card and
|
|
|
|
|
(not is_exact or d_event.data[1].from.id == c_event.data[1].from)
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-09 03:44:19 +00:00
|
|
|
|
function GameLogic:dumpEventStack(detailed)
|
|
|
|
|
local top = self:getCurrentEvent()
|
|
|
|
|
local i = self.game_event_stack.p
|
|
|
|
|
local inspect = p
|
|
|
|
|
if not top then return end
|
|
|
|
|
|
|
|
|
|
print("===== Start of event stack dump =====")
|
|
|
|
|
if not detailed then print("") end
|
|
|
|
|
|
|
|
|
|
repeat
|
|
|
|
|
local printable_data
|
|
|
|
|
if type(top.data) ~= "table" then
|
|
|
|
|
printable_data = top.data
|
|
|
|
|
else
|
|
|
|
|
printable_data = table.cloneWithoutClass(top.data)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not detailed then
|
2023-04-22 07:52:26 +00:00
|
|
|
|
print("Stack level #" .. i .. ": " .. tostring(top))
|
2023-04-09 03:44:19 +00:00
|
|
|
|
else
|
|
|
|
|
print("\nStack level #" .. i .. ":")
|
|
|
|
|
inspect{
|
|
|
|
|
eventId = GameEvent:translate(top.event),
|
|
|
|
|
data = printable_data or "nil",
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
top = top.parent
|
|
|
|
|
i = i - 1
|
|
|
|
|
until not top
|
|
|
|
|
|
|
|
|
|
print("\n===== End of event stack dump =====")
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-08 17:10:16 +00:00
|
|
|
|
function GameLogic:dumpAllEvents(from, to)
|
|
|
|
|
from = from or 1
|
|
|
|
|
to = to or #self.all_game_events
|
|
|
|
|
assert(from <= to)
|
|
|
|
|
|
|
|
|
|
local indent = 0
|
|
|
|
|
local tab = " "
|
|
|
|
|
for i = from, to, 1 do
|
|
|
|
|
local v = self.all_game_events[i]
|
2023-08-24 13:37:24 +00:00
|
|
|
|
if type(v) ~= "table" then
|
2023-06-08 17:10:16 +00:00
|
|
|
|
indent = math.max(indent - 1, 0)
|
|
|
|
|
-- v = "End"
|
|
|
|
|
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
|
|
|
|
|
else
|
|
|
|
|
print(tab:rep(indent) .. string.format("%s", tostring(v)))
|
|
|
|
|
if v.id ~= v.end_id then
|
|
|
|
|
indent = indent + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-28 17:43:44 +00:00
|
|
|
|
function GameLogic:breakEvent(ret)
|
2023-03-04 17:28:59 +00:00
|
|
|
|
coroutine.yield("__breakEvent", ret)
|
2023-02-28 17:43:44 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-22 07:52:26 +00:00
|
|
|
|
function GameLogic:breakTurn()
|
|
|
|
|
local event = self:getCurrentEvent():findParent(GameEvent.Turn)
|
2024-02-04 07:54:51 +00:00
|
|
|
|
if not event then return end
|
2023-04-22 07:52:26 +00:00
|
|
|
|
event:shutdown()
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-28 14:24:30 +00:00
|
|
|
|
return GameLogic
|