2023-04-09 13:35:35 +08:00
|
|
|
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
---@class GameEvent: Object
|
2023-06-09 01:10:16 +08:00
|
|
|
|
---@field public id integer @ 事件的id,随着时间推移自动增加并分配给新事件
|
|
|
|
|
---@field public end_id integer @ 事件的对应结束id,如果整个事件中未插入事件,那么end_id就是自己的id
|
|
|
|
|
---@field public room Room @ room实例
|
|
|
|
|
---@field public event integer @ 该事件对应的EventType
|
|
|
|
|
---@field public data any @ 事件的附加数据,视类型而定
|
|
|
|
|
---@field public parent GameEvent @ 事件的父事件(栈中的上一层事件)
|
2023-08-12 00:50:17 +08:00
|
|
|
|
---@field public prepare_func fun(self: GameEvent) @ 事件即将开始时执行的函数
|
2023-06-09 01:10:16 +08:00
|
|
|
|
---@field public main_func fun(self: GameEvent) @ 事件的主函数
|
|
|
|
|
---@field public clear_func fun(self: GameEvent) @ 事件结束时执行的函数
|
|
|
|
|
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
|
|
|
|
|
---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数
|
|
|
|
|
---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数
|
2023-10-07 23:00:25 +08:00
|
|
|
|
---@field public interrupted boolean @ 事件是否是因为被中断而结束的,可能是防止事件或者被杀
|
|
|
|
|
---@field public killed boolean @ 事件因为终止一切结算而被中断(所谓的“被杀”)
|
|
|
|
|
---@field public revived boolean @ 事件被killed,但因为在cleaner中发生而被复活
|
2023-03-01 01:43:44 +08:00
|
|
|
|
local GameEvent = class("GameEvent")
|
|
|
|
|
|
2023-08-12 00:50:17 +08:00
|
|
|
|
---@type (fun(self: GameEvent): bool)[]
|
|
|
|
|
GameEvent.prepare_funcs = {}
|
|
|
|
|
|
2023-08-11 03:30:59 +08:00
|
|
|
|
---@type (fun(self: GameEvent): bool)[]
|
2023-03-01 01:43:44 +08:00
|
|
|
|
GameEvent.functions = {}
|
2023-06-09 01:10:16 +08:00
|
|
|
|
|
2023-08-11 03:30:59 +08:00
|
|
|
|
---@type (fun(self: GameEvent): bool)[]
|
2023-03-01 01:43:44 +08:00
|
|
|
|
GameEvent.cleaners = {}
|
2023-06-09 01:10:16 +08:00
|
|
|
|
|
2023-08-11 03:30:59 +08:00
|
|
|
|
---@type (fun(self: GameEvent): bool)[]
|
2023-06-09 01:10:16 +08:00
|
|
|
|
GameEvent.exit_funcs = {}
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
local function wrapCoFunc(f, ...)
|
|
|
|
|
if not f then return nil end
|
|
|
|
|
local args = {...}
|
|
|
|
|
return function() return f(table.unpack(args)) end
|
|
|
|
|
end
|
2023-06-09 01:10:16 +08:00
|
|
|
|
local dummyFunc = Util.DummyFunc
|
2023-03-01 01:43:44 +08:00
|
|
|
|
|
|
|
|
|
function GameEvent:initialize(event, ...)
|
2023-06-09 01:10:16 +08:00
|
|
|
|
self.id = -1
|
|
|
|
|
self.end_id = -1
|
2023-03-01 01:43:44 +08:00
|
|
|
|
self.room = RoomInstance
|
|
|
|
|
self.event = event
|
|
|
|
|
self.data = { ... }
|
2023-08-12 00:50:17 +08:00
|
|
|
|
self.prepare_func = GameEvent.prepare_funcs[event] or dummyFunc
|
2023-03-01 01:43:44 +08:00
|
|
|
|
self.main_func = wrapCoFunc(GameEvent.functions[event], self) or dummyFunc
|
2023-04-22 15:52:26 +08:00
|
|
|
|
self.clear_func = GameEvent.cleaners[event] or dummyFunc
|
2023-06-09 01:10:16 +08:00
|
|
|
|
self.extra_clear_funcs = Util.DummyTable
|
|
|
|
|
self.exit_func = GameEvent.exit_funcs[event] or dummyFunc
|
|
|
|
|
self.extra_exit_funcs = Util.DummyTable
|
2023-03-07 14:55:28 +08:00
|
|
|
|
self.interrupted = false
|
|
|
|
|
end
|
|
|
|
|
|
2023-07-14 23:12:46 +08:00
|
|
|
|
-- 静态函数,实际定义在events/init.lua
|
|
|
|
|
function GameEvent:translate(id)
|
|
|
|
|
error('static')
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-22 15:52:26 +08:00
|
|
|
|
function GameEvent:__tostring()
|
2023-06-09 01:10:16 +08:00
|
|
|
|
return string.format("<%s #%d>", GameEvent:translate(self.event), self.id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function GameEvent:addCleaner(f)
|
|
|
|
|
if self.extra_clear_funcs == Util.DummyTable then
|
|
|
|
|
self.extra_clear_funcs = {}
|
|
|
|
|
end
|
|
|
|
|
table.insert(self.extra_clear_funcs, f)
|
2023-04-22 15:52:26 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-06-09 01:10:16 +08:00
|
|
|
|
function GameEvent:addExitFunc(f)
|
|
|
|
|
if self.extra_exit_funcs == Util.DummyTable then
|
|
|
|
|
self.extra_exit_funcs = {}
|
|
|
|
|
end
|
|
|
|
|
table.insert(self.extra_exit_funcs, f)
|
|
|
|
|
end
|
|
|
|
|
|
2023-09-19 14:27:54 +08:00
|
|
|
|
function GameEvent:prependExitFunc(f)
|
|
|
|
|
if self.extra_exit_funcs == Util.DummyTable then
|
|
|
|
|
self.extra_exit_funcs = {}
|
|
|
|
|
end
|
|
|
|
|
table.insert(self.extra_exit_funcs, 1, f)
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-09 01:10:16 +08:00
|
|
|
|
function GameEvent:findParent(eventType, includeSelf)
|
|
|
|
|
if includeSelf and self.event == eventType then return self end
|
2023-04-09 11:44:19 +08:00
|
|
|
|
local e = self.parent
|
|
|
|
|
repeat
|
|
|
|
|
if e.event == eventType then return e end
|
|
|
|
|
e = e.parent
|
|
|
|
|
until not e
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-09 01:10:16 +08:00
|
|
|
|
-- 找n个id介于from和to之间的事件。
|
|
|
|
|
local function bin_search(events, from, to, n, func)
|
|
|
|
|
local left = 1
|
|
|
|
|
local right = #events
|
|
|
|
|
local mid
|
|
|
|
|
local ret = {}
|
|
|
|
|
|
|
|
|
|
if from < events[1].id then
|
|
|
|
|
mid = 1
|
|
|
|
|
elseif from > events[right].id then
|
|
|
|
|
return ret
|
|
|
|
|
else
|
|
|
|
|
while true do
|
|
|
|
|
if left > right then return ret end
|
|
|
|
|
mid = (left + right) // 2
|
|
|
|
|
local id = events[mid].id
|
|
|
|
|
local id_left = mid == 1 and -math.huge or events[mid - 1].id
|
|
|
|
|
|
|
|
|
|
if from < id then
|
|
|
|
|
if from >= id_left then
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
right = mid - 1
|
|
|
|
|
else
|
|
|
|
|
left = mid + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for i = mid, #events do
|
|
|
|
|
local v = events[i]
|
2023-08-02 23:01:28 +08:00
|
|
|
|
if v.id <= to and func(v) then
|
2023-06-09 01:10:16 +08:00
|
|
|
|
table.insert(ret, v)
|
|
|
|
|
end
|
|
|
|
|
if #ret >= n then break end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 从某个区间中,找出类型符合且符合func函数检测的至多n个事件。
|
|
|
|
|
---@param eventType integer @ 要查找的事件类型
|
|
|
|
|
---@param n integer @ 最多找多少个
|
|
|
|
|
---@param func fun(e: GameEvent): boolean @ 过滤用的函数
|
|
|
|
|
---@param endEvent GameEvent|nil @ 区间终止点,默认为本事件结束
|
|
|
|
|
---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个
|
|
|
|
|
function GameEvent:searchEvents(eventType, n, func, endEvent)
|
|
|
|
|
local logic = self.room.logic
|
2023-06-10 20:54:48 +08:00
|
|
|
|
local events = logic.event_recorder[eventType] or Util.DummyTable
|
2023-06-09 01:10:16 +08:00
|
|
|
|
local from = self.id
|
|
|
|
|
local to = endEvent and endEvent.id or self.end_id
|
|
|
|
|
if to == -1 then to = #logic.all_game_events end
|
|
|
|
|
n = n or 1
|
2023-08-12 00:50:17 +08:00
|
|
|
|
func = func or Util.TrueFunc
|
2023-06-09 01:10:16 +08:00
|
|
|
|
|
|
|
|
|
local ret
|
|
|
|
|
if #events < 6 then
|
|
|
|
|
ret = {}
|
|
|
|
|
for _, v in ipairs(events) do
|
2023-08-02 23:01:28 +08:00
|
|
|
|
if v.id >= from and v.id <= to and func(v) then
|
2023-06-09 01:10:16 +08:00
|
|
|
|
table.insert(ret, v)
|
|
|
|
|
end
|
|
|
|
|
if #ret >= n then break end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
ret = bin_search(events, from, to, n, func)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-07 14:55:28 +08:00
|
|
|
|
function GameEvent:clear()
|
2023-04-27 14:15:08 +08:00
|
|
|
|
local clear_co = coroutine.create(function()
|
2023-06-09 01:10:16 +08:00
|
|
|
|
self:clear_func()
|
2023-04-27 14:15:08 +08:00
|
|
|
|
for _, f in ipairs(self.extra_clear_funcs) do
|
|
|
|
|
if type(f) == "function" then f(self) end
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
2023-10-07 23:00:25 +08:00
|
|
|
|
local zhuran_jmp, zhuran_msg -- SB老朱然
|
|
|
|
|
|
2023-04-27 14:15:08 +08:00
|
|
|
|
while true do
|
|
|
|
|
local err, yield_result, extra_yield_result = coroutine.resume(clear_co)
|
|
|
|
|
|
|
|
|
|
if err == false then
|
|
|
|
|
-- handle error, then break
|
|
|
|
|
if not string.find(yield_result, "__manuallyBreak") then
|
|
|
|
|
fk.qCritical(yield_result)
|
2023-07-14 23:12:46 +08:00
|
|
|
|
print(debug.traceback(clear_co))
|
2023-04-27 14:15:08 +08:00
|
|
|
|
end
|
|
|
|
|
coroutine.close(clear_co)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if yield_result == "__handleRequest" then
|
|
|
|
|
-- yield to requestLoop
|
|
|
|
|
coroutine.yield(yield_result, extra_yield_result)
|
2023-10-07 23:00:25 +08:00
|
|
|
|
|
|
|
|
|
elseif type(yield_result) == "table" and yield_result.class
|
|
|
|
|
and yield_result:isInstanceOf(GameEvent) and self ~= yield_result then
|
|
|
|
|
|
|
|
|
|
-- 不是,谁TM还在cleaner里面玩老朱然啊
|
|
|
|
|
-- 总之,cleaner不能断
|
|
|
|
|
-- 倒是没必要手动resume,新一轮while true会自动resume,只要把返回值
|
|
|
|
|
-- 传回去就行
|
|
|
|
|
|
|
|
|
|
-- 一般来说都是由cleaner中的trigger引起
|
|
|
|
|
-- 以胆守合击为例就是trigger -> SkillEffect事件 -> UseCard事件 -> 胆守
|
|
|
|
|
-- 此时胆守的话最后从SkillEffect事件的exec内部yield出来
|
|
|
|
|
-- 当前协程就应该正在执行room:useSkill函数,resume会去只会让那个函数返回
|
|
|
|
|
|
|
|
|
|
if zhuran_jmp == nil or zhuran_jmp.id > yield_result.id then
|
|
|
|
|
zhuran_jmp = yield_result
|
|
|
|
|
zhuran_msg = extra_yield_result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 自己本来应该被杀的但是因为自己正在执行self:clear()而逃过一劫啊
|
|
|
|
|
-- 还是得标记一下被杀才行,顺便因为实际上没死所以标记被复活
|
|
|
|
|
self.killed = true
|
|
|
|
|
self.revived = true
|
|
|
|
|
-- 什么都不做,等下轮while自己resume
|
2023-04-27 14:15:08 +08:00
|
|
|
|
else
|
|
|
|
|
coroutine.close(clear_co)
|
|
|
|
|
break
|
|
|
|
|
end
|
2023-03-07 14:55:28 +08:00
|
|
|
|
end
|
2023-06-09 01:10:16 +08:00
|
|
|
|
|
2023-10-07 23:00:25 +08:00
|
|
|
|
-- cleaner顺利执行完了,出栈吧
|
2023-06-09 01:10:16 +08:00
|
|
|
|
local logic = RoomInstance.logic
|
|
|
|
|
local end_id = logic.current_event_id + 1
|
|
|
|
|
if self.id ~= end_id - 1 then
|
2023-08-24 21:37:24 +08:00
|
|
|
|
logic.all_game_events[end_id] = self.event
|
2023-06-09 01:10:16 +08:00
|
|
|
|
logic.current_event_id = end_id
|
|
|
|
|
self.end_id = end_id
|
|
|
|
|
else
|
|
|
|
|
self.end_id = self.id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
logic.game_event_stack:pop()
|
|
|
|
|
|
2023-10-07 23:00:25 +08:00
|
|
|
|
-- 好了确保cleaner走完了,此时中断就会进入下层事件的正常中断处理
|
|
|
|
|
if zhuran_jmp then
|
|
|
|
|
coroutine.close(self._co)
|
|
|
|
|
coroutine.yield(zhuran_jmp, zhuran_msg)
|
|
|
|
|
|
|
|
|
|
-- 此时仍可能出现在插结在其他事件的clear函数中
|
|
|
|
|
-- 但就算被交付回去了,也能安然返回而不是继续while
|
|
|
|
|
-- 但愿如此吧
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- 保险而已,其实如果被杀的话应该已经在前面的yield终止了
|
|
|
|
|
-- 但担心cleaner嵌套(三国杀是这样的)还是补一刀
|
|
|
|
|
if self.killed then return end
|
|
|
|
|
|
|
|
|
|
-- 恭喜没被杀掉,我们来执行一些事件结束之后的结算吧
|
2023-06-16 10:56:33 +08:00
|
|
|
|
Pcall(self.exit_func, self)
|
2023-06-09 01:10:16 +08:00
|
|
|
|
for _, f in ipairs(self.extra_exit_funcs) do
|
|
|
|
|
if type(f) == "function" then
|
2023-06-16 10:56:33 +08:00
|
|
|
|
Pcall(f, self)
|
2023-06-09 01:10:16 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2023-03-01 01:43:44 +08:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-22 15:52:26 +08:00
|
|
|
|
local function breakEvent(self, extra_yield_result)
|
|
|
|
|
local cancelEvent = GameEvent:new(GameEvent.BreakEvent, self)
|
2023-08-12 00:50:17 +08:00
|
|
|
|
cancelEvent.toId = self.id
|
2023-04-22 15:52:26 +08:00
|
|
|
|
local notcanceled = cancelEvent:exec()
|
|
|
|
|
local ret, extra_ret = false, nil
|
|
|
|
|
if not notcanceled then
|
|
|
|
|
self.interrupted = true
|
|
|
|
|
self:clear()
|
|
|
|
|
ret = true
|
|
|
|
|
extra_ret = extra_yield_result
|
|
|
|
|
end
|
|
|
|
|
return ret, extra_ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
function GameEvent:exec()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local logic = room.logic
|
|
|
|
|
local ret = false -- false or nil means this event is running normally
|
|
|
|
|
local extra_ret
|
2023-04-09 11:44:19 +08:00
|
|
|
|
self.parent = logic:getCurrentEvent()
|
2023-08-12 00:50:17 +08:00
|
|
|
|
|
|
|
|
|
if self:prepare_func() then return true end
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
logic.game_event_stack:push(self)
|
|
|
|
|
|
2023-06-09 01:10:16 +08:00
|
|
|
|
logic.current_event_id = logic.current_event_id + 1
|
|
|
|
|
self.id = logic.current_event_id
|
|
|
|
|
logic.all_game_events[self.id] = self
|
|
|
|
|
logic.event_recorder[self.event] = logic.event_recorder[self.event] or {}
|
|
|
|
|
table.insert(logic.event_recorder[self.event], self)
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
local co = coroutine.create(self.main_func)
|
2023-10-07 23:00:25 +08:00
|
|
|
|
self._co = co
|
2023-03-01 01:43:44 +08:00
|
|
|
|
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)
|
|
|
|
|
print(debug.traceback(co))
|
|
|
|
|
end
|
2023-03-07 14:55:28 +08:00
|
|
|
|
self.interrupted = true
|
|
|
|
|
self:clear()
|
2023-03-01 01:43:44 +08:00
|
|
|
|
ret = true
|
2023-04-22 15:52:26 +08:00
|
|
|
|
coroutine.close(co)
|
2023-03-01 01:43:44 +08:00
|
|
|
|
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
|
|
|
|
|
|
2023-04-22 15:52:26 +08:00
|
|
|
|
if self ~= yield_result then
|
|
|
|
|
-- yield to corresponding GameEvent, first pop self from stack
|
|
|
|
|
self.interrupted = true
|
2023-10-07 23:00:25 +08:00
|
|
|
|
self.killed = true -- 老朱然!你不得好死
|
2023-04-22 15:52:26 +08:00
|
|
|
|
self:clear()
|
2023-06-09 01:10:16 +08:00
|
|
|
|
-- logic.game_event_stack:pop(self)
|
2023-04-22 15:52:26 +08:00
|
|
|
|
coroutine.close(co)
|
|
|
|
|
|
|
|
|
|
-- then, call yield
|
|
|
|
|
coroutine.yield(yield_result, extra_yield_result)
|
2023-10-07 23:00:25 +08:00
|
|
|
|
|
|
|
|
|
-- 如果是在cleaner/exit里面发生此类中断的话是会被cleaner原地返回的
|
|
|
|
|
-- 此时正常执行程序流就变成继续while循环了,这是不行的
|
|
|
|
|
break
|
2023-04-22 15:52:26 +08:00
|
|
|
|
elseif extra_yield_result == "__breakEvent" then
|
|
|
|
|
if breakEvent(self) then
|
|
|
|
|
coroutine.close(co)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-03-01 01:43:44 +08:00
|
|
|
|
|
|
|
|
|
elseif yield_result == "__breakEvent" then
|
|
|
|
|
-- try to break this event
|
2023-04-22 15:52:26 +08:00
|
|
|
|
if breakEvent(self) then
|
|
|
|
|
coroutine.close(co)
|
2023-03-01 01:43:44 +08:00
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
-- normally exit, simply break the loop
|
2023-03-07 14:55:28 +08:00
|
|
|
|
self:clear()
|
2023-03-01 01:43:44 +08:00
|
|
|
|
extra_ret = yield_result
|
2023-04-22 15:52:26 +08:00
|
|
|
|
coroutine.close(co)
|
2023-03-01 01:43:44 +08:00
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return ret, extra_ret
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-22 15:52:26 +08:00
|
|
|
|
function GameEvent:shutdown()
|
|
|
|
|
-- yield to self and break
|
|
|
|
|
coroutine.yield(self, "__breakEvent")
|
|
|
|
|
end
|
|
|
|
|
|
2023-03-01 01:43:44 +08:00
|
|
|
|
return GameEvent
|