otclientv8/modules/game_bot/executor.lua
2019-10-26 17:38:55 +02:00

399 lines
12 KiB
Lua

function executeBot(config, storage, panel, msgCallback)
local context = {}
context.panel = panel
context.storage = storage
if context.storage._macros == nil then
context.storage._macros = {} -- active macros
end
--
context._macros = {}
context._hotkeys = {}
context._scheduler = {}
context._callbacks = {
onKeyDown = {},
onKeyUp = {},
onKeyPress = {},
onTalk = {},
onAddThing = {},
onRemoveThing = {}
}
context.ui = {}
-- basic functions & classes
context.print = print
context.pairs = pairs
context.ipairs = ipairs
context.tostring = tostring
context.math = math
context.table = table
context.string = string
context.tr = tr
context.json = json
context.regexMatch = regexMatch
-- classes
context.g_game = g_game
context.g_map = g_map
context.g_ui = g_ui
context.StaticText = StaticText
context.Position = Position
context.HTTP = HTTP
-- log functions
context.info = function(text) return msgCallback("info", tostring(text)) end
context.warn = function(text) return msgCallback("warn", tostring(text)) end
context.error = function(text) return msgCallback("error", tostring(text)) end
context.warning = context.warn
-- UI
context.setupUI = function(otml, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.loadUIFromString(otml, parent)
if parent == context.panel and widget:getId() then
context.ui[widget:getId()] = widget
end
return widget
end
context.addSwitch = function(id, text, onClickCallback)
local switch = g_ui.createWidget('BotSwitch', context.panel)
switch:setId(id)
switch:setText(text)
switch.onClick = onClickCallback
context.ui[id] = switch
return switch
end
context.addButton = function(id, text, onClickCallback)
local button = g_ui.createWidget('BotButton', context.panel)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
context.ui[id] = button
return button
end
context.addLabel = function(id, text)
local label = g_ui.createWidget('BotLabel', context.panel)
label:setId(id)
label:setText(text)
context.ui[id] = label
return label
end
context.addSeparator = function(id)
local separator = g_ui.createWidget('BotSeparator', context.panel)
separator:setId(id)
context.ui[id] = separator
return separator
end
context._addMacroSwitch = function(name, keys)
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("macro_" .. #context._macros, text, function(widget)
context.storage._macros[name] = not context.storage._macros[name]
widget:setOn(context.storage._macros[name])
end)
switch:setOn(context.storage._macros[name])
return switch
end
context._addHotkeySwitch = function(name, keys)
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil)
switch:setOn(false)
return switch
end
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, hotkey, callback)
context.macro = function(timeout, name, hotkey, callback)
if type(timeout) ~= 'number' or timeout < 1 then
error("Invalid timeout for macro: " .. tostring(timeout))
end
if type(name) == 'function' then
callback = name
name = ""
hotkey = ""
elseif type(hotkey) == 'function' then
callback = hotkey
hotkey = ""
elseif type(callback) ~= 'function' then
error("Invalid callback for macro: " .. tostring(callback))
end
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
error("Invalid name or hotkey for macro")
end
if hotkey:len() > 0 then
hotkey = retranslateKeyComboDesc(hotkey)
end
local switch = nil
if name:len() > 0 then
if context.storage._macros[name] == nil then
context.storage._macros[name] = true
end
switch = context._addMacroSwitch(name, hotkey)
end
table.insert(context._macros, {
timeout = timeout,
name = name,
callback = callback,
lastExecution = context.now,
hotkey = hotkey,
switch = switch
})
return context._macros[#context._macros]
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
context.hotkey = function(keys, name, callback)
if type(name) == 'function' then
callback = name
name = ""
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys)
end
context._hotkeys[keys] = {
name = name,
callback = callback,
lastExecution = context.now,
switch = switch,
single = false
}
return context._hotkeys[keys]
end
-- singlehotkey(keys, callback)
-- singlehotkey(keys, name, callback)
context.singlehotkey = function(keys, name, callback)
if type(name) == 'function' then
callback = name
name = ""
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys)
end
context._hotkeys[keys] = {
name = name,
callback = callback,
lastExecution = context.now,
switch = switch,
single = true
}
return context._hotkeys[keys]
end
-- schedule(timeout, callback)
context.schedule = function(timeout, callback)
local extecute_time = g_clock.millis() + timeout
table.insert(context._scheduler, {
execution = extecute_time,
callback = callback
})
table.sort(context._scheduler, function(a, b) return a.execution < b.execution end)
end
-- callback(callbackType, callback)
context.callback = function(callbackType, callback)
if not context._callbacks[callbackType] then
return error("Wrong callback type: " .. callbackType)
end
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
g_game.enableTileThingLuaCallback(true)
end
local callbackData = {}
table.insert(context._callbacks[callbackType], function(...)
if not callbackData.delay or callbackData.delay < context.now then
context._currentExecution = callbackData
callback(...)
context._currentExecution = nil
end
end)
end
-- onKeyDown(callback) -- callback = function(keys)
context.onKeyDown = function(callback)
return context.callback("onKeyDown", callback)
end
-- onKeyPress(callback) -- callback = function(keys)
context.onKeyPress = function(callback)
return context.callback("onKeyPress", callback)
end
-- onKeyUp(callback) -- callback = function(keys)
context.onKeyUp = function(callback)
return context.callback("onKeyUp", callback)
end
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
context.onTalk = function(callback)
return context.callback("onTalk", callback)
end
-- onAddThing(callback) -- callback = function(tile, thing)
context.onAddThing = function(callback)
return context.callback("onAddThing", callback)
end
-- onRemoveThing(callback) -- callback = function(tile, thing)
context.onRemoveThing = function(callback)
return context.callback("onRemoveThing", callback)
end
-- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback)
name = name:lower()
context.onTalk(function(name2, level, mode, text, channelId, pos)
if name == name2:lower() then
callback(text, channelId, pos)
end
end)
end
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
context.delay = function(duration)
if not context._currentExecution then
return context.error("Invalid usage of delay function, it should be used inside callbacks")
end
context._currentExecution.delay = context.now + duration
end
-- init context
context.now = g_clock.millis()
context.time = g_clock.millis()
context.player = g_game.getLocalPlayer()
require("functions.lua")
setupFunctions(context)
-- run script
assert(load(config, nil, nil, context))()
return {
script = function()
context.now = g_clock.millis()
context.time = g_clock.millis()
for i, macro in ipairs(context._macros) do
if macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage._macros[macro.name]) then
if not macro.delay or macro.delay < context.now then
macro.lastExecution = context.now
context._currentExecution = context._macros[i]
macro.callback()
context._currentExecution = nil
end
end
end
while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
context._scheduler[1].callback()
table.remove(context._scheduler, 1)
end
end,
callbacks = {
onKeyDown = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
for i, macro in ipairs(context._macros) do
if macro.switch and macro.hotkey == keyDesc then
macro.switch:onClick()
end
end
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then
hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
end
end
if hotkey.switch then
hotkey.switch:setOn(true)
end
end
for i, callback in ipairs(context._callbacks.onKeyDown) do
callback(keyDesc)
end
end,
onKeyUp = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.switch then
hotkey.switch:setOn(false)
end
end
for i, callback in ipairs(context._callbacks.onKeyUp) do
callback(keyDesc)
end
end,
onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey and not hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then
hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
end
end
for i, callback in ipairs(context._callbacks.onKeyPress) do
callback(keyDesc, autoRepeatTicks)
end
end,
onTalk = function(name, level, mode, text, channelId, pos)
for i, callback in ipairs(context._callbacks.onTalk) do
callback(name, level, mode, text, channelId, pos)
end
end,
onAddThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onAddThing) do
callback(tile, thing)
end
end,
onRemoveThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onRemoveThing) do
callback(tile, thing)
end
end
}
}
end