controller.lua

main 399 lines · 10.2 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1local TypeEvent = {
2 MODULE_INIT = 1,
3 GAME_INIT = 2
4}
5
6local function onGameStart(self)
7 if self.__onGameStart ~= nil then
8 self.currentTypeEvent = TypeEvent.GAME_INIT
9 addEvent(function()
10 self:__onGameStart()
11
12 local eventList = self.events[TypeEvent.GAME_INIT]
13 if eventList ~= nil then
14 for _, event in pairs(eventList) do
15 event:connect()
16 end
17 end
18 end)
19 end
20end
21
22local function onGameEnd(self)
23 if self.__onGameEnd ~= nil then
24 self:__onGameEnd()
25 end
26
27 local eventList = self.events[TypeEvent.GAME_INIT]
28 if eventList ~= nil then
29 for _, event in pairs(eventList) do
30 event:destroy()
31 end
32
33 self.events[TypeEvent.GAME_INIT] = nil
34 end
35
36 local scheduledEventsList = self.scheduledEvents[TypeEvent.GAME_INIT]
37 if scheduledEventsList then
38 for _, eventId in pairs(scheduledEventsList) do
39 removeEvent(eventId)
40 end
41
42 self.scheduledEvents[TypeEvent.GAME_INIT] = nil
43 end
44
45 if self.dataUI ~= nil and self.dataUI.onGameStart and self.ui then
46 self:destroyUI()
47 end
48
49 table.remove_if(self.keyboardEvents, function(i, event)
50 local destroy = event.controllerEventType == TypeEvent.GAME_INIT
51 if destroy then
52 g_keyboard['unbind' .. event.name](event.args[1], event.args[2], event.args[3])
53 end
54 return destroy
55 end)
56end
57
58Controller = {
59 ui = nil,
60 name = nil,
61 attrs = nil,
62 extendedOpcodes = nil,
63 opcodes = nil,
64 events = nil,
65 htmlRoot = nil,
66 keyboardAnchor = nil,
67 scheduledEvents = nil,
68 keyboardEvents = nil
69}
70
71function Controller:new()
72 local module = g_modules.getCurrentModule()
73 local obj = {
74 name = module and module:getName() or nil,
75 currentTypeEvent = TypeEvent.MODULE_INIT,
76 events = {},
77 scheduledEvents = {},
78 keyboardEvents = {},
79 attrs = {},
80 extendedOpcodes = {},
81 opcodes = {},
82 }
83 setmetatable(obj, self)
84 self.__index = self
85 return obj
86end
87
88function Controller:init()
89 if self.dataUI ~= nil then
90 self:loadUI()
91 end
92
93 if self.onInit then
94 self.currentTypeEvent = TypeEvent.MODULE_INIT
95 self:onInit()
96 end
97
98 self.__onGameStart = self.onGameStart
99 self.onGameStart = function()
100 onGameStart(self)
101 end
102
103 connect(g_game, {
104 onGameStart = self.onGameStart
105 })
106
107 if g_game.isOnline() then
108 self:onGameStart()
109 end
110
111 self.__onGameEnd = self.onGameEnd
112 self.onGameEnd = function()
113 onGameEnd(self)
114 end
115
116 connect(g_game, {
117 onGameEnd = self.onGameEnd
118 })
119
120 local eventList = self.events[TypeEvent.MODULE_INIT]
121 if eventList then
122 for _, event in pairs(eventList) do
123 event:connect()
124 end
125 end
126end
127
128function Controller:loadHtml(path, parent)
129 local suffix = ".html"
130 if path:sub(- #suffix) ~= suffix then
131 path = path .. suffix
132 end
133
134 self:setUI(path, parent)
135 self.htmlRoot = HtmlLoader('/' .. self.name .. '/' .. path, parent, self)
136 self.ui = self.htmlRoot.widget
137end
138
139function Controller:destroyUI()
140 if self.htmlRoot ~= nil then
141 self.htmlRoot.widget = nil
142 self.htmlRoot = nil
143 end
144
145 if self.ui then
146 self.ui:destroy()
147 self.ui = nil
148 end
149
150 for type, events in pairs(self.events) do
151 table.remove_if(events, function(i, event)
152 local canRemove = event:actorIsDestroyed()
153 if canRemove then
154 event:destroy() -- force destroy
155 end
156 return canRemove
157 end)
158 end
159end
160
161function Controller:findElements(query)
162 return self.htmlRoot and self.htmlRoot:find(query:trim()) or {}
163end
164
165function Controller:findWidgets(query)
166 return self.htmlRoot and self.htmlRoot:findWidgets(query:trim()) or {}
167end
168
169function Controller:findElement(query)
170 local els = self:findElements(query)
171 return #els > 0 and els[1] or nil
172end
173
174function Controller:findWidget(query)
175 local els = self:findWidgets(query)
176 return #els > 0 and els[1] or nil
177end
178
179function Controller:loadUI(name, parent)
180 if self.ui then
181 return
182 end
183
184 if not self.dataUI then
185 self:setUI(name, parent)
186 end
187
188 self.ui = g_ui.loadUI('/' .. self.name .. '/' .. self.dataUI.name, self.dataUI.parent or g_ui.getRootWidget())
189end
190
191function Controller:setKeyboardAnchor(widget)
192 self.keyboardAnchor = widget
193end
194
195function Controller:setUI(name, parent)
196 self.dataUI = { name = name, parent = parent, onGameStart = self.currentTypeEvent == TypeEvent.GAME_INIT }
197end
198
199function Controller:terminate()
200 if self.onGameStart then
201 disconnect(g_game, { onGameStart = self.onGameStart })
202 end
203
204 if self.onGameEnd then
205 if g_game.isOnline() then self:onGameEnd() end
206 disconnect(g_game, { onGameEnd = self.onGameEnd })
207 end
208
209 if self.onTerminate then
210 self:onTerminate()
211 end
212
213 for i, event in pairs(self.keyboardEvents) do
214 g_keyboard['unbind' .. event.name](event.args[1], event.args[2], event.args[3])
215 end
216
217 for i, opcode in pairs(self.extendedOpcodes) do
218 ProtocolGame.unregisterExtendedOpcode(opcode)
219 end
220
221 for _, opcode in ipairs(self.opcodes) do
222 ProtocolGame.unregisterOpcode(opcode)
223 end
224
225 for type, events in pairs(self.events) do
226 if events ~= nil then
227 for _, event in pairs(events) do
228 event:destroy()
229 end
230 end
231 end
232
233 for type, events in pairs(self.scheduledEvents) do
234 if events ~= nil then
235 for _, eventId in pairs(events) do
236 removeEvent(eventId)
237 end
238 end
239 end
240
241 if self.ui ~= nil then
242 self.ui:destroy()
243 end
244
245 self.ui = nil
246 self.attrs = nil
247 self.events = nil
248 self.dataUI = nil
249 self.extendedOpcodes = nil
250 self.opcodes = nil
251 self.keyboardEvents = nil
252 self.keyboardAnchor = nil
253 self.scheduledEvents = nil
254 self.htmlRoot = nil
255
256 self.__onGameStart = nil
257 self.__onGameEnd = nil
258end
259
260--[[
261 If you register an event in onInit() or in the general scope of the script,
262 the event will be automatically registered at startup and disconnected when the module is destroyed.
263 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.
264]]
265function Controller:registerEvents(actor, events)
266 if self.events[self.currentTypeEvent] == nil then
267 self.events[self.currentTypeEvent] = {}
268 end
269
270 local evt = EventController:new(actor, events)
271 table.insert(self.events[self.currentTypeEvent], evt)
272
273 return evt
274end
275
276function Controller:registerExtendedOpcode(opcode, fnc)
277 ProtocolGame.registerExtendedOpcode(opcode, fnc)
278 table.insert(self.extendedOpcodes, opcode)
279end
280
281function Controller:registerOpcode(opcode, fnc)
282 ProtocolGame.registerOpcode(opcode, fnc)
283 table.insert(self.opcodes, opcode)
284end
285
286function Controller:sendExtendedOpcode(opcode, ...)
287 local protocol = g_game.getProtocolGame()
288 if protocol then
289 protocol:sendExtendedOpcode(opcode, ...)
290 end
291end
292
293local function registerScheduledEvent(controller, fncRef, fnc, delay, name)
294 local currentType = controller.currentTypeEvent
295 if controller.scheduledEvents[currentType] == nil then
296 controller.scheduledEvents[currentType] = {}
297 end
298
299 local _rmvEvent = function()
300 if controller.scheduledEvents[currentType][name] then
301 removeEvent(controller.scheduledEvents[currentType][name])
302 controller.scheduledEvents[currentType][name] = nil
303 end
304 end
305 _rmvEvent()
306
307 local evt = nil
308 local action = function()
309 fnc()
310
311 if fncRef == scheduleEvent then
312 if name then
313 _rmvEvent()
314 else
315 table.removevalue(controller.scheduledEvents[currentType], evt)
316 end
317 end
318 end
319
320 evt = fncRef(action, delay)
321
322 if name then
323 controller.scheduledEvents[currentType][name] = evt
324 else
325 table.insert(controller.scheduledEvents[currentType], evt)
326 end
327
328 return evt
329end
330
331function Controller:scheduleEvent(fnc, delay, name)
332 return registerScheduledEvent(self, scheduleEvent, fnc, delay, name)
333end
334
335function Controller:cycleEvent(fnc, delay, name)
336 return registerScheduledEvent(self, cycleEvent, fnc, delay, name)
337end
338
339function Controller:removeEvent(evt)
340 if self.scheduledEvents[TypeEvent.GAME_INIT] then
341 if table.removevalue(self.scheduledEvents[TypeEvent.GAME_INIT], evt) then
342 removeEvent(evt)
343 return
344 end
345 end
346
347 if self.scheduledEvents[TypeEvent.MODULE_INIT] then
348 if table.find(self.scheduledEvents[TypeEvent.MODULE_INIT], evt) then
349 error('It is not possible to remove events registered at controller init.')
350 return
351 end
352 end
353
354 error('The event was not registered by the controller.')
355end
356
357function Controller:bindKeyDown(...)
358 local args = { ... }
359 if args[3] == nil or type(args[3]) == 'boolean' then
360 args[4] = args[3]
361 args[3] = self.keyboardAnchor
362 end
363 table.insert(self.keyboardEvents, {
364 name = 'KeyDown',
365 args = args,
366 controllerEventType = self.currentTypeEvent
367 })
368 g_keyboard.bindKeyDown(args[1], args[2], args[3], args[4])
369end
370
371function Controller:bindKeyUp(...)
372 local args = { ... }
373 if args[3] == nil or type(args[3]) == 'boolean' then
374 args[4] = args[3]
375 args[3] = self.keyboardAnchor
376 end
377 table.insert(self.keyboardEvents, {
378 name = 'KeyUp',
379 args = args,
380 controllerEventType = self.currentTypeEvent
381 })
382
383 g_keyboard.bindKeyUp(args[1], args[2], args[3], args[4])
384end
385
386function Controller:bindKeyPress(...)
387 local args = { ... }
388 if args[3] == nil or type(args[3]) == 'boolean' then
389 args[4] = args[3]
390 args[3] = self.keyboardAnchor
391 end
392 table.insert(self.keyboardEvents, {
393 name = 'KeyPress',
394 args = args,
395 controllerEventType = self.currentTypeEvent
396 })
397 g_keyboard.bindKeyPress(args[1], args[2], args[3])
398end
399
Top