FreeKill/lua/server/gameevent.lua

112 lines
2.9 KiB
Lua

-- SPDX-License-Identifier: GPL-3.0-or-later
---@class GameEvent: Object
---@field public room Room
---@field public event integer
---@field public data any
---@field public parent GameEvent
---@field public main_func fun(self: GameEvent)
---@field public clear_func fun(self: GameEvent)
---@field public extra_clear_funcs any[]
---@field public interrupted boolean
local GameEvent = class("GameEvent")
GameEvent.functions = {}
GameEvent.cleaners = {}
local function wrapCoFunc(f, ...)
if not f then return nil end
local args = {...}
return function() return f(table.unpack(args)) end
end
local function dummyFunc() end
function GameEvent:initialize(event, ...)
self.room = RoomInstance
self.event = event
self.data = { ... }
self.main_func = wrapCoFunc(GameEvent.functions[event], self) or dummyFunc
self.clear_func = wrapCoFunc(GameEvent.cleaners[event], self) or dummyFunc
self.extra_clear_funcs = {}
self.interrupted = false
end
function GameEvent:findParent(eventType)
local e = self.parent
repeat
if e.event == eventType then return e end
e = e.parent
until not e
return nil
end
function GameEvent:clear()
for _, f in ipairs(self.extra_clear_funcs) do
if type(f) == "function" then f(self) end
end
self:clear_func()
end
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
self.parent = logic:getCurrentEvent()
logic.game_event_stack:push(self)
local co = coroutine.create(self.main_func)
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
self.interrupted = true
self:clear()
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
-- yield to corresponding GameEvent, first pop self from stack
self.interrupted = true
self:clear()
logic.game_event_stack:pop(self)
-- then, call yield
coroutine.yield(yield_result)
elseif yield_result == "__breakEvent" then
-- try to break this event
local cancelEvent = GameEvent:new(GameEvent.BreakEvent, self)
local notcanceled = cancelEvent:exec()
if not notcanceled then
self.interrupted = true
self:clear()
ret = true
extra_ret = extra_yield_result
break
end
else
-- normally exit, simply break the loop
self:clear()
extra_ret = yield_result
break
end
end
logic.game_event_stack:pop(self)
return ret, extra_ret
end
return GameEvent