1function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, reloadCallback, websockets)
2 -- load lua and otui files
3 local configFiles = g_resources.listDirectoryFiles("/bot/" .. config, true, false)
4 local luaFiles = {}
5 local uiFiles = {}
6 for i, file in ipairs(configFiles) do
7 local ext = file:split(".")
8 if ext[#ext]:lower() == "lua" then
9 table.insert(luaFiles, file)
10 end
11 if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then
12 table.insert(uiFiles, file)
13 end
14 end
15
16 if #luaFiles == 0 then
17 return error("Config (/bot/" .. config .. ") doesn't have lua files")
18 end
19
20 -- init bot variables
21 local context = {}
22 context.configDir = "/bot/".. config
23 context.tabs = tabs
24 context.mainTab = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel.content
25 context.panel = context.mainTab
26 context.saveConfig = saveConfigCallback
27 context.reload = reloadCallback
28
29 context.storage = storage
30 if context.storage._macros == nil then
31 context.storage._macros = {} -- active macros
32 end
33
34 -- websockets, macros, hotkeys, scheduler, icons, callbacks
35 context._websockets = websockets
36 context._macros = {}
37 context._hotkeys = {}
38 context._scheduler = {}
39 context._callbacks = {
40 onKeyDown = {},
41 onKeyUp = {},
42 onKeyPress = {},
43 onTalk = {},
44 onTextMessage = {},
45 onAddThing = {},
46 onRemoveThing = {},
47 onCreatureAppear = {},
48 onCreatureDisappear = {},
49 onCreaturePositionChange = {},
50 onCreatureHealthPercentChange = {},
51 onUse = {},
52 onUseWith = {},
53 onContainerOpen = {},
54 onContainerClose = {},
55 onContainerUpdateItem = {},
56 onMissle = {},
57 onAnimatedText = {},
58 onStaticText = {},
59 onChannelList = {},
60 onOpenChannel = {},
61 onCloseChannel = {},
62 onChannelEvent = {},
63 onTurn = {}
64 }
65
66 -- basic functions & classes
67 context.print = print
68 context.pairs = pairs
69 context.ipairs = ipairs
70 context.tostring = tostring
71 context.math = math
72 context.table = table
73 context.string = string
74 context.tonumber = tonumber
75 context.type = type
76 context.pcall = pcall
77 context.os = {
78 time = os.time,
79 date = os.date,
80 difftime = os.difftime,
81 date = os.date,
82 clock = os.clock
83 }
84 context.load = function(str) return assert(load(str, nil, nil, context)) end
85 context.loadstring = context.load
86 context.assert = assert
87 context.dofile = function(file) assert(load(g_resources.readFileContents("/bot/" .. config .. "/" .. file), file, nil, context))() end
88 context.gcinfo = gcinfo
89 context.tr = tr
90 context.json = json
91 context.base64 = base64
92 context.regexMatch = regexMatch
93 context.getDistanceBetween = function(p1, p2)
94 return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
95 end
96 context.isMobile = g_app.isMobile
97 context.getVersion = g_app.getVersion
98
99 -- classes
100 context.g_resources = g_resources
101 context.g_game = g_game
102 context.g_map = g_map
103 context.g_ui = g_ui
104 context.g_sounds = g_sounds
105 context.g_window = g_window
106 context.g_mouse = g_mouse
107 context.g_things = g_things
108 context.g_platform = {
109 openUrl = g_platform.openUrl,
110 openDir = g_platform.openDir,
111 }
112
113 context.Item = Item
114 context.Creature = Creature
115 context.ThingType = ThingType
116 context.Effect = Effect
117 context.Missile = Missile
118 context.Player = Player
119 context.Monster = Monster
120 context.StaticText = StaticText
121 context.HTTP = HTTP
122 context.OutputMessage = OutputMessage
123 context.modules = modules
124
125 -- log functions
126 context.info = function(text) return msgCallback("info", tostring(text)) end
127 context.warn = function(text) return msgCallback("warn", tostring(text)) end
128 context.error = function(text) return msgCallback("error", tostring(text)) end
129 context.warning = context.warn
130
131 -- init context
132 context.now = g_clock.millis()
133 context.time = g_clock.millis()
134 context.player = g_game.getLocalPlayer()
135
136 -- init functions
137 G.botContext = context
138 dofiles("functions")
139 context.Panels = {}
140 dofiles("panels")
141 G.botContext = nil
142
143 -- run ui scripts
144 for i, file in ipairs(uiFiles) do
145 g_ui.importStyle(file)
146 end
147
148 -- run lua script
149 for i, file in ipairs(luaFiles) do
150 assert(load(g_resources.readFileContents(file), file, nil, context))()
151 context.panel = context.mainTab -- reset default tab
152 end
153
154 return {
155 script = function()
156 context.now = g_clock.millis()
157 context.time = g_clock.millis()
158
159 for i, macro in ipairs(context._macros) do
160 if macro.lastExecution + macro.timeout <= context.now and macro.enabled then
161 local status, result = pcall(function()
162 if macro.callback(macro) then
163 macro.lastExecution = context.now
164 end
165 end)
166 if not status then
167 context.error("Macro: " .. macro.name .. " execution error: " .. result)
168 end
169 end
170 end
171
172 while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
173 local status, result = pcall(function()
174 context._scheduler[1].callback()
175 end)
176 if not status then
177 context.error("Schedule execution error: " .. result)
178 end
179 table.remove(context._scheduler, 1)
180 end
181 end,
182 callbacks = {
183 onKeyDown = function(keyCode, keyboardModifiers)
184 local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
185 for i, macro in ipairs(context._macros) do
186 if macro.switch and macro.hotkey == keyDesc then
187 macro.switch:onClick()
188 end
189 end
190 local hotkey = context._hotkeys[keyDesc]
191 if hotkey then
192 if hotkey.single then
193 if hotkey.callback() then
194 hotkey.lastExecution = context.now
195 end
196 end
197 if hotkey.switch then
198 hotkey.switch:setOn(true)
199 end
200 end
201 for i, callback in ipairs(context._callbacks.onKeyDown) do
202 callback(keyDesc)
203 end
204 end,
205 onKeyUp = function(keyCode, keyboardModifiers)
206 local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
207 local hotkey = context._hotkeys[keyDesc]
208 if hotkey then
209 if hotkey.switch then
210 hotkey.switch:setOn(false)
211 end
212 end
213 for i, callback in ipairs(context._callbacks.onKeyUp) do
214 callback(keyDesc)
215 end
216 end,
217 onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks)
218 local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
219 local hotkey = context._hotkeys[keyDesc]
220 if hotkey and not hotkey.single then
221 if hotkey.callback() then
222 hotkey.lastExecution = context.now
223 end
224 end
225 for i, callback in ipairs(context._callbacks.onKeyPress) do
226 callback(keyDesc, autoRepeatTicks)
227 end
228 end,
229 onTalk = function(name, level, mode, text, channelId, pos)
230 for i, callback in ipairs(context._callbacks.onTalk) do
231 callback(name, level, mode, text, channelId, pos)
232 end
233 end,
234 onTextMessage = function(mode, text)
235 for i, callback in ipairs(context._callbacks.onTextMessage) do
236 callback(mode, text)
237 end
238 end,
239 onAddThing = function(tile, thing)
240 for i, callback in ipairs(context._callbacks.onAddThing) do
241 callback(tile, thing)
242 end
243 end,
244 onRemoveThing = function(tile, thing)
245 for i, callback in ipairs(context._callbacks.onRemoveThing) do
246 callback(tile, thing)
247 end
248 end,
249 onCreatureAppear = function(creature)
250 for i, callback in ipairs(context._callbacks.onCreatureAppear) do
251 callback(creature)
252 end
253 end,
254 onCreatureDisappear = function(creature)
255 for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
256 callback(creature)
257 end
258 end,
259 onCreaturePositionChange = function(creature, newPos, oldPos)
260 for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
261 callback(creature, newPos, oldPos)
262 end
263 end,
264 onCreatureHealthPercentChange = function(creature, healthPercent)
265 for i, callback in ipairs(context._callbacks.onCreatureHealthPercentChange) do
266 callback(creature, healthPercent)
267 end
268 end,
269 onUse = function(pos, itemId, stackPos, subType)
270 for i, callback in ipairs(context._callbacks.onUse) do
271 callback(pos, itemId, stackPos, subType)
272 end
273 end,
274 onUseWith = function(pos, itemId, target, subType)
275 for i, callback in ipairs(context._callbacks.onUseWith) do
276 callback(pos, itemId, target, subType)
277 end
278 end,
279 onContainerOpen = function(container, previousContainer)
280 for i, callback in ipairs(context._callbacks.onContainerOpen) do
281 callback(container, previousContainer)
282 end
283 end,
284 onContainerClose = function(container)
285 for i, callback in ipairs(context._callbacks.onContainerClose) do
286 callback(container)
287 end
288 end,
289 onContainerUpdateItem = function(container, slot, item)
290 for i, callback in ipairs(context._callbacks.onContainerUpdateItem) do
291 callback(container, slot, item)
292 end
293 end,
294 onMissle = function(missle)
295 for i, callback in ipairs(context._callbacks.onMissle) do
296 callback(missle)
297 end
298 end,
299 onAnimatedText = function(thing, text)
300 for i, callback in ipairs(context._callbacks.onAnimatedText) do
301 callback(thing, text)
302 end
303 end,
304 onStaticText = function(thing, text)
305 for i, callback in ipairs(context._callbacks.onStaticText) do
306 callback(thing, text)
307 end
308 end,
309 onChannelList = function(channels)
310 for i, callback in ipairs(context._callbacks.onChannelList) do
311 callback(channels)
312 end
313 end,
314 onOpenChannel = function(channelId, channelName)
315 for i, callback in ipairs(context._callbacks.onOpenChannel) do
316 callback(channels)
317 end
318 end,
319 onCloseChannel = function(channelId)
320 for i, callback in ipairs(context._callbacks.onCloseChannel) do
321 callback(channelId)
322 end
323 end,
324 onChannelEvent = function(channelId, name, event)
325 for i, callback in ipairs(context._callbacks.onChannelEvent) do
326 callback(channelId, name, event)
327 end
328 end,
329 onTurn = function(creature, direction)
330 for i, callback in ipairs(context._callbacks.onTurn) do
331 callback(creature, direction)
332 end
333 end,
334 }
335 }
336end