otclient-redemption/modules/modulelib/controller.lua

448 lines
11 KiB
Lua
Raw Permalink Normal View History

2023-05-12 21:20:34 -03:00
local TypeEvent = {
MODULE_INIT = 1,
GAME_INIT = 2
}
local function onGameStart(self)
if self.__onGameStart ~= nil then
self.currentTypeEvent = TypeEvent.GAME_INIT
addEvent(function()
self:__onGameStart()
local eventList = self.events[TypeEvent.GAME_INIT]
if eventList ~= nil then
for _, event in pairs(eventList) do
event:connect()
end
end
end)
2023-05-12 21:20:34 -03:00
end
end
local function onGameEnd(self)
if self.__onGameEnd ~= nil then
self:__onGameEnd()
end
local eventList = self.events[TypeEvent.GAME_INIT]
if eventList ~= nil then
for _, event in pairs(eventList) do
2024-08-29 18:07:23 -03:00
event:destroy()
2023-05-12 21:20:34 -03:00
end
self.events[TypeEvent.GAME_INIT] = nil
2023-05-12 21:20:34 -03:00
end
local scheduledEventsList = self.scheduledEvents and self.scheduledEvents[TypeEvent.GAME_INIT]
if scheduledEventsList then
for _, eventId in pairs(scheduledEventsList) do
removeEvent(eventId)
end
if self.scheduledEvents then
self.scheduledEvents[TypeEvent.GAME_INIT] = nil
end
end
2024-07-16 21:36:58 -03:00
if self.dataUI ~= nil and self.dataUI.onGameStart and self.ui then
2024-08-29 18:07:23 -03:00
self:destroyUI()
2023-05-12 21:20:34 -03:00
end
2024-08-29 18:07:23 -03:00
table.remove_if(self.keyboardEvents, function(i, event)
local destroy = event.controllerEventType == TypeEvent.GAME_INIT
if destroy then
g_keyboard['unbind' .. event.name](event.args[1], event.args[2], event.args[3])
end
return destroy
end)
2023-05-12 21:20:34 -03:00
end
Controller = {
2023-05-12 21:20:34 -03:00
ui = nil,
2024-07-16 21:36:58 -03:00
name = nil,
2023-05-12 21:20:34 -03:00
attrs = nil,
extendedOpcodes = nil,
2024-07-03 22:27:40 -03:00
opcodes = nil,
2024-07-16 21:36:58 -03:00
events = nil,
uiEvents = nil,
2025-09-28 14:46:24 -03:00
htmlId = nil,
2024-07-16 21:36:58 -03:00
keyboardAnchor = nil,
scheduledEvents = nil,
keyboardEvents = nil
}
2025-09-28 14:46:24 -03:00
if not G_CONTROLLER_CALLED then
G_CONTROLLER_CALLED = {}
end
function Controller:new()
local module = g_modules.getCurrentModule()
2021-07-19 19:24:59 -03:00
local obj = {
name = module and module:getName() or nil,
2023-05-12 21:20:34 -03:00
currentTypeEvent = TypeEvent.MODULE_INIT,
2021-07-19 19:24:59 -03:00
events = {},
uiEvents = {},
scheduledEvents = {},
2021-07-19 19:24:59 -03:00
keyboardEvents = {},
2023-05-12 21:20:34 -03:00
attrs = {},
extendedOpcodes = {},
opcodes = {},
2021-07-19 19:24:59 -03:00
}
setmetatable(obj, self)
self.__index = self
2025-09-28 14:46:24 -03:00
if obj.name then
G_CONTROLLER_CALLED[obj.name] = obj
end
2021-07-19 19:24:59 -03:00
return obj
end
function Controller:init()
2024-07-12 14:31:39 -03:00
if self.dataUI ~= nil then
self:loadUI()
2023-05-12 21:20:34 -03:00
end
if self.onInit then
2023-05-12 21:20:34 -03:00
self.currentTypeEvent = TypeEvent.MODULE_INIT
self:onInit()
end
2021-07-19 19:24:59 -03:00
2023-05-12 21:20:34 -03:00
self.__onGameStart = self.onGameStart
self.onGameStart = function()
onGameStart(self)
2021-07-19 19:24:59 -03:00
end
2023-05-12 21:20:34 -03:00
connect(g_game, {
onGameStart = self.onGameStart
})
if g_game.isOnline() then
self:onGameStart()
end
self.__onGameEnd = self.onGameEnd
self.onGameEnd = function()
onGameEnd(self)
end
connect(g_game, {
onGameEnd = self.onGameEnd
})
2023-05-12 21:20:34 -03:00
local eventList = self.events[TypeEvent.MODULE_INIT]
if eventList then
for _, event in pairs(eventList) do
event:connect()
2023-05-12 21:20:34 -03:00
end
end
end
2024-07-16 21:36:58 -03:00
function Controller:loadHtml(path, parent)
local suffix = ".html"
if path:sub(- #suffix) ~= suffix then
path = path .. suffix
2024-07-16 21:36:58 -03:00
end
2024-07-16 21:36:58 -03:00
self:setUI(path, parent)
2025-09-28 14:46:24 -03:00
self.htmlId = g_html.load(self.name, path, g_ui.getRootWidget())
self.ui = g_html.getRootWidget(self.htmlId);
end
function Controller:unloadHtml()
if self.htmlId == nil then
error('HTML unload operation failed: no HTML was previously loaded.')
return
end
self:destroyUI()
2024-07-16 21:36:58 -03:00
end
2024-08-29 18:07:23 -03:00
function Controller:destroyUI()
2025-09-28 14:46:24 -03:00
if self.htmlId ~= nil then
g_html.destroy(self.htmlId)
self.ui = nil
2024-08-29 18:07:23 -03:00
end
if self.ui then
self.ui:destroy()
self.ui = nil
end
for _, event in pairs(self.uiEvents) do
event:destroy()
2024-08-29 18:07:23 -03:00
end
self.uiEvents = {}
WidgetWatch.update() -- Prevent warnings from stale widget references in the watch list.
2024-08-29 18:07:23 -03:00
end
2025-09-28 14:46:24 -03:00
function Controller:findWidget(query)
return self.ui and self.ui:querySelector(query:trim())
2024-07-16 21:36:58 -03:00
end
function Controller:findWidgets(query)
2025-09-28 14:46:24 -03:00
return self.ui and self.ui:querySelectorAll(query:trim())
2024-07-16 21:36:58 -03:00
end
2025-09-28 14:46:24 -03:00
function Controller:createWidgetFromHTML(html, parent)
local widget = g_html.createWidgetFromHTML(html, parent, self.htmlId)
return widget
2024-07-16 21:36:58 -03:00
end
2024-07-12 14:31:39 -03:00
function Controller:loadUI(name, parent)
if self.ui then
return
end
if not self.dataUI then
self:setUI(name, parent)
end
self.ui = g_ui.loadUI('/' .. self.name .. '/' .. self.dataUI.name, self.dataUI.parent or g_ui.getRootWidget())
end
2024-07-03 22:27:40 -03:00
function Controller:setKeyboardAnchor(widget)
self.keyboardAnchor = widget
end
2024-07-12 14:31:39 -03:00
function Controller:setUI(name, parent)
self.dataUI = { name = name, parent = parent, onGameStart = self.currentTypeEvent == TypeEvent.GAME_INIT }
end
function Controller:terminate()
2023-05-12 21:20:34 -03:00
if self.onGameStart then
disconnect(g_game, { onGameStart = self.onGameStart })
2021-07-19 19:24:59 -03:00
end
2023-05-12 21:20:34 -03:00
if self.onGameEnd then
if g_game.isOnline() then self:onGameEnd() end
disconnect(g_game, { onGameEnd = self.onGameEnd })
end
2021-07-19 19:24:59 -03:00
if self.onTerminate then
self:onTerminate()
end
for i, event in pairs(self.keyboardEvents) do
2024-07-03 22:27:40 -03:00
g_keyboard['unbind' .. event.name](event.args[1], event.args[2], event.args[3])
end
2021-07-19 19:24:59 -03:00
for i, opcode in pairs(self.extendedOpcodes) do
ProtocolGame.unregisterExtendedOpcode(opcode)
end
2021-07-19 19:24:59 -03:00
for _, opcode in ipairs(self.opcodes) do
ProtocolGame.unregisterOpcode(opcode)
end
for type, events in pairs(self.events) do
if events ~= nil then
for _, event in pairs(events) do
2024-08-29 18:07:23 -03:00
event:destroy()
end
2023-05-12 21:20:34 -03:00
end
end
for type, events in pairs(self.scheduledEvents or {}) do
if events ~= nil then
for _, eventId in pairs(events) do
removeEvent(eventId)
end
end
end
2023-05-12 21:20:34 -03:00
if self.ui ~= nil then
2025-09-28 14:46:24 -03:00
self:destroyUI()
2023-05-12 21:20:34 -03:00
end
self.ui = nil
self.attrs = nil
self.events = nil
self.dataUI = nil
self.extendedOpcodes = nil
2021-07-19 19:24:59 -03:00
self.opcodes = nil
self.keyboardEvents = nil
2024-07-03 22:27:40 -03:00
self.keyboardAnchor = nil
self.scheduledEvents = nil
2025-09-28 14:46:24 -03:00
self.htmlId = nil
2023-05-12 21:20:34 -03:00
self.__onGameStart = nil
self.__onGameEnd = nil
end
--[[
If you register an event in onInit() or in the general scope of the script,
the event will be automatically registered at startup and disconnected when the module is destroyed.
If it is inside onGameStart(), the events will be connected when entering the game map and disconnected when leaving and also when the module is destroyed.
]]
function Controller:registerEvents(actor, events)
2023-05-12 21:20:34 -03:00
if self.events[self.currentTypeEvent] == nil then
self.events[self.currentTypeEvent] = {}
end
local evt = EventController:new(actor, events)
table.insert(self.events[self.currentTypeEvent], evt)
2021-07-19 19:24:59 -03:00
return evt
end
function Controller:registerUIEvents(actor, events)
local evt = EventController:new(actor, events)
table.insert(self.uiEvents, evt)
-- fix html lazy loading
if actor and actor.isOnHtml and actor:isOnHtml() then
evt:connect()
end
return evt
end
function Controller:checkWidgetsDestroyed()
table.remove_if(self.uiEvents, function(i, e)
if e:actorIsDestroyed() then
e:disconnect()
return true
end
return false
end)
end
function Controller:registerExtendedOpcode(opcode, fnc)
2021-07-19 19:24:59 -03:00
ProtocolGame.registerExtendedOpcode(opcode, fnc)
table.insert(self.extendedOpcodes, opcode)
end
function Controller:registerOpcode(opcode, fnc)
ProtocolGame.registerOpcode(opcode, fnc)
2021-07-19 19:24:59 -03:00
table.insert(self.opcodes, opcode)
end
function Controller:sendExtendedOpcode(opcode, ...)
2021-07-19 19:24:59 -03:00
local protocol = g_game.getProtocolGame()
if protocol then
protocol:sendExtendedOpcode(opcode, ...)
end
end
local function registerScheduledEvent(controller, fncRef, fnc, delay, name)
local currentType = controller.currentTypeEvent
controller.scheduledEvents = controller.scheduledEvents or {}
if controller.scheduledEvents[currentType] == nil then
controller.scheduledEvents[currentType] = {}
end
local _rmvEvent = function()
if not controller.scheduledEvents then return end
local list = controller.scheduledEvents[currentType]
if not list then return end
if name and list[name] then
removeEvent(list[name])
list[name] = nil
end
end
_rmvEvent()
local evt = nil
local action = function()
local res = fnc()
if fncRef == scheduleEvent then
if name then
_rmvEvent()
else
if controller.scheduledEvents and controller.scheduledEvents[currentType] then
table.removevalue(controller.scheduledEvents[currentType], evt)
end
end
elseif res == false then
removeEvent(evt)
end
end
evt = fncRef(action, delay)
if name then
if controller.scheduledEvents then
controller.scheduledEvents[currentType][name] = evt
end
else
if controller.scheduledEvents and controller.scheduledEvents[currentType] then
table.insert(controller.scheduledEvents[currentType], evt)
end
end
return evt
end
function Controller:scheduleEvent(fnc, delay, name)
return registerScheduledEvent(self, scheduleEvent, fnc, delay, name)
end
function Controller:cycleEvent(fnc, delay, name)
return registerScheduledEvent(self, cycleEvent, fnc, delay, name)
end
function Controller:removeEvent(evt)
if self.scheduledEvents[TypeEvent.GAME_INIT] then
if table.removevalue(self.scheduledEvents[TypeEvent.GAME_INIT], evt) then
removeEvent(evt)
return
end
end
if self.scheduledEvents[TypeEvent.MODULE_INIT] then
if table.find(self.scheduledEvents[TypeEvent.MODULE_INIT], evt) then
error('It is not possible to remove events registered at controller init.')
return
end
end
error('The event was not registered by the controller.')
end
2024-07-03 22:27:40 -03:00
function Controller:bindKeyDown(...)
local args = { ... }
if args[3] == nil or type(args[3]) == 'boolean' then
args[4] = args[3]
args[3] = self.keyboardAnchor
2024-05-27 18:16:53 -03:00
end
2022-04-29 23:14:24 -03:00
table.insert(self.keyboardEvents, {
name = 'KeyDown',
2024-08-29 18:07:23 -03:00
args = args,
controllerEventType = self.currentTypeEvent
2022-04-29 23:14:24 -03:00
})
g_keyboard.bindKeyDown(args[1], args[2], args[3], args[4])
end
2024-05-27 18:16:53 -03:00
2024-07-03 22:27:40 -03:00
function Controller:bindKeyUp(...)
local args = { ... }
if args[3] == nil or type(args[3]) == 'boolean' then
args[4] = args[3]
args[3] = self.keyboardAnchor
end
table.insert(self.keyboardEvents, {
name = 'KeyUp',
2024-08-29 18:07:23 -03:00
args = args,
controllerEventType = self.currentTypeEvent
})
2024-07-03 22:27:40 -03:00
g_keyboard.bindKeyUp(args[1], args[2], args[3], args[4])
end
2024-07-03 22:27:40 -03:00
function Controller:bindKeyPress(...)
local args = { ... }
if args[3] == nil or type(args[3]) == 'boolean' then
args[4] = args[3]
args[3] = self.keyboardAnchor
end
2022-04-29 23:14:24 -03:00
table.insert(self.keyboardEvents, {
name = 'KeyPress',
2024-08-29 18:07:23 -03:00
args = args,
controllerEventType = self.currentTypeEvent
2022-04-29 23:14:24 -03:00
})
2024-07-03 22:27:40 -03:00
g_keyboard.bindKeyPress(args[1], args[2], args[3])
end