mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-04-29 18:59:20 +02:00
Version 1.7
This commit is contained in:
parent
1d2bdf855d
commit
2a10e65ec0
@ -1,6 +1,6 @@
|
|||||||
# OTClientV8
|
# OTClientV8
|
||||||
|
|
||||||
Tibia client design for versions 7.40 - 11.00
|
Tibia client designed for versions 7.40 - 11.00.
|
||||||
It's based on https://github.com/edubart/otclient and it's not backward compatible.
|
It's based on https://github.com/edubart/otclient and it's not backward compatible.
|
||||||
|
|
||||||
## DISCORD: https://discord.gg/feySup6
|
## DISCORD: https://discord.gg/feySup6
|
||||||
|
2
init.lua
2
init.lua
@ -5,7 +5,7 @@ APP_VERSION = 1337 -- client version for updater and login to identify outd
|
|||||||
-- If you don't use updater or other service, set it to updater = ""
|
-- If you don't use updater or other service, set it to updater = ""
|
||||||
Services = {
|
Services = {
|
||||||
website = "http://otclient.ovh", -- currently not used
|
website = "http://otclient.ovh", -- currently not used
|
||||||
updater = "http://otclient.ovh/api/updater.php",
|
updater = "",
|
||||||
news = "http://otclient.ovh/api/news.php",
|
news = "http://otclient.ovh/api/news.php",
|
||||||
stats = "",
|
stats = "",
|
||||||
crash = "http://otclient.ovh/api/crash.php",
|
crash = "http://otclient.ovh/api/crash.php",
|
||||||
|
@ -235,11 +235,8 @@ function CharacterList.terminate()
|
|||||||
CharacterList = nil
|
CharacterList = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function CharacterList.create(characters, account, otui, websocket)
|
function CharacterList.create(characters, account, otui)
|
||||||
if not otui then otui = 'characterlist' end
|
if not otui then otui = 'characterlist' end
|
||||||
if websocket then
|
|
||||||
websocket:close()
|
|
||||||
end
|
|
||||||
if charactersWindow then
|
if charactersWindow then
|
||||||
charactersWindow:destroy()
|
charactersWindow:destroy()
|
||||||
end
|
end
|
||||||
|
@ -17,9 +17,6 @@ local serverHostTextEdit
|
|||||||
local rememberPasswordBox
|
local rememberPasswordBox
|
||||||
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"}
|
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"}
|
||||||
|
|
||||||
local webSocket
|
|
||||||
local webSocketLoginPacket
|
|
||||||
|
|
||||||
-- private functions
|
-- private functions
|
||||||
local function onProtocolError(protocol, message, errorCode)
|
local function onProtocolError(protocol, message, errorCode)
|
||||||
if errorCode then
|
if errorCode then
|
||||||
@ -54,13 +51,9 @@ local function onCharacterList(protocol, characters, account, otui)
|
|||||||
loadBox = nil
|
loadBox = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
CharacterList.create(characters, account, otui, webSocket)
|
CharacterList.create(characters, account, otui)
|
||||||
CharacterList.show()
|
CharacterList.show()
|
||||||
|
|
||||||
if webSocket then
|
|
||||||
webSocket = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
g_settings.save()
|
g_settings.save()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -137,10 +130,6 @@ local function onHTTPResult(data, err)
|
|||||||
if #incorrectThings > 0 then
|
if #incorrectThings > 0 then
|
||||||
g_logger.info(incorrectThings)
|
g_logger.info(incorrectThings)
|
||||||
if Updater then
|
if Updater then
|
||||||
if webSocket then
|
|
||||||
webSocket:close()
|
|
||||||
webSocket = nil
|
|
||||||
end
|
|
||||||
return Updater.updateThings(things, incorrectThings)
|
return Updater.updateThings(things, incorrectThings)
|
||||||
else
|
else
|
||||||
return EnterGame.onError(incorrectThings)
|
return EnterGame.onError(incorrectThings)
|
||||||
@ -186,7 +175,7 @@ local function onHTTPResult(data, err)
|
|||||||
g_proxy.clear()
|
g_proxy.clear()
|
||||||
if proxies then
|
if proxies then
|
||||||
for i, proxy in ipairs(proxies) do
|
for i, proxy in ipairs(proxies) do
|
||||||
g_proxy.addProxy(tonumber(proxy["localPort"]), proxy["host"], tonumber(proxy["port"]), tonumber(proxy["priority"]))
|
g_proxy.addProxy(proxy["host"], tonumber(proxy["port"]), tonumber(proxy["priority"]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -198,7 +187,6 @@ end
|
|||||||
-- public functions
|
-- public functions
|
||||||
function EnterGame.init()
|
function EnterGame.init()
|
||||||
enterGame = g_ui.displayUI('entergame')
|
enterGame = g_ui.displayUI('entergame')
|
||||||
newLogin = g_ui.displayUI('entergame_new')
|
|
||||||
|
|
||||||
serverSelectorPanel = enterGame:getChildById('serverSelectorPanel')
|
serverSelectorPanel = enterGame:getChildById('serverSelectorPanel')
|
||||||
customServerSelectorPanel = enterGame:getChildById('customServerSelectorPanel')
|
customServerSelectorPanel = enterGame:getChildById('customServerSelectorPanel')
|
||||||
@ -260,15 +248,7 @@ end
|
|||||||
function EnterGame.terminate()
|
function EnterGame.terminate()
|
||||||
g_keyboard.unbindKeyDown('Ctrl+G')
|
g_keyboard.unbindKeyDown('Ctrl+G')
|
||||||
|
|
||||||
if webSocket then
|
|
||||||
webSocket.close()
|
|
||||||
webSocket = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
enterGame:destroy()
|
enterGame:destroy()
|
||||||
if newLogin then
|
|
||||||
newLogin:destroy()
|
|
||||||
end
|
|
||||||
if loadBox then
|
if loadBox then
|
||||||
loadBox:destroy()
|
loadBox:destroy()
|
||||||
loadBox = nil
|
loadBox = nil
|
||||||
@ -288,12 +268,10 @@ function EnterGame.show()
|
|||||||
enterGame:raise()
|
enterGame:raise()
|
||||||
enterGame:focus()
|
enterGame:focus()
|
||||||
enterGame:getChildById('accountNameTextEdit'):focus()
|
enterGame:getChildById('accountNameTextEdit'):focus()
|
||||||
EnterGame.checkWebsocket()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function EnterGame.hide()
|
function EnterGame.hide()
|
||||||
enterGame:hide()
|
enterGame:hide()
|
||||||
newLogin:hide()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function EnterGame.openWindow()
|
function EnterGame.openWindow()
|
||||||
@ -313,80 +291,8 @@ function EnterGame.clearAccountFields()
|
|||||||
g_settings.remove('password')
|
g_settings.remove('password')
|
||||||
end
|
end
|
||||||
|
|
||||||
function EnterGame.checkWebsocket()
|
|
||||||
if enterGame:isHidden() then return end
|
|
||||||
local url = serverHostTextEdit:getText()
|
|
||||||
if url:find("ws://") == nil and url:find("wss://") == nil then
|
|
||||||
if webSocket then
|
|
||||||
webSocket:close()
|
|
||||||
webSocket = nil
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if webSocket then
|
|
||||||
if webSocket.url == url then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
webSocket:close()
|
|
||||||
webSocket = nil
|
|
||||||
end
|
|
||||||
webSocket = HTTP.WebSocketJSON(url, {
|
|
||||||
onOpen = function(message, webSocketId)
|
|
||||||
if webSocket and webSocket.id == webSocketId then
|
|
||||||
webSocket.send({type="init", uid=G.UUID, version=APP_VERSION})
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
onMessage = function(message, webSocketId)
|
|
||||||
if webSocket and webSocket.id == webSocketId then
|
|
||||||
if message.type == "login" then
|
|
||||||
webSocketLoginPacket = nil
|
|
||||||
EnterGame.hide()
|
|
||||||
onHTTPResult(message, nil)
|
|
||||||
elseif message.type == "quick_login" and message.qrcode then
|
|
||||||
EnterGame.showNewLogin(message.qrcode)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
onClose = function(message, webSocketId)
|
|
||||||
if webSocket and webSocket.id == webSocketId then
|
|
||||||
webSocket = nil
|
|
||||||
if webSocketLoginPacket then
|
|
||||||
webSocketLoginPacket = nil
|
|
||||||
onHTTPResult(nil, "WebSocket disconnected")
|
|
||||||
end
|
|
||||||
EnterGame.checkWebsocket() -- reconnect
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
onError = function(message, webSocketId)
|
|
||||||
if webSocket and webSocket.id == webSocketId then
|
|
||||||
-- handle error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function EnterGame.hideNewLogin()
|
|
||||||
newLogin:hide()
|
|
||||||
end
|
|
||||||
|
|
||||||
function EnterGame.showNewLogin(qrcode)
|
|
||||||
if enterGame:isHidden() then return end
|
|
||||||
newLogin.qrcode:setQRCode("https://quath.co/0/" .. qrcode, 1)
|
|
||||||
newLogin.qrcode:setEnabled(true)
|
|
||||||
local clickFunction = function()
|
|
||||||
g_platform.openUrl("qauth://" .. qrcode)
|
|
||||||
end
|
|
||||||
newLogin.qrcode.onClick = clickFunction
|
|
||||||
newLogin.quathlogo.onClick = clickFunction
|
|
||||||
if newLogin:isHidden() then
|
|
||||||
newLogin:show()
|
|
||||||
newLogin:raise()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function EnterGame.onServerChange()
|
function EnterGame.onServerChange()
|
||||||
server = serverSelector:getText()
|
server = serverSelector:getText()
|
||||||
EnterGame.hideNewLogin()
|
|
||||||
if server == tr("Another") then
|
if server == tr("Another") then
|
||||||
if not customServerSelectorPanel:isOn() then
|
if not customServerSelectorPanel:isOn() then
|
||||||
serverHostTextEdit:setText("")
|
serverHostTextEdit:setText("")
|
||||||
@ -399,7 +305,6 @@ function EnterGame.onServerChange()
|
|||||||
end
|
end
|
||||||
if Servers and Servers[server] ~= nil then
|
if Servers and Servers[server] ~= nil then
|
||||||
serverHostTextEdit:setText(Servers[server])
|
serverHostTextEdit:setText(Servers[server])
|
||||||
EnterGame.checkWebsocket()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -509,36 +414,6 @@ function EnterGame.doLogin()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function EnterGame.doLoginWs()
|
|
||||||
-- PREVIEW, need to implement websocket reconnect and error handling
|
|
||||||
if G.host == nil or G.host:len() < 10 then
|
|
||||||
return EnterGame.onError("Invalid server url: " .. G.host)
|
|
||||||
end
|
|
||||||
if not webSocket then
|
|
||||||
return EnterGame.onError("There's no websocket connection to: " .. G.host)
|
|
||||||
end
|
|
||||||
|
|
||||||
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to login server...'))
|
|
||||||
connect(loadBox, { onCancel = function(msgbox)
|
|
||||||
loadBox = nil
|
|
||||||
webSocketLoginPacket = nil
|
|
||||||
EnterGame.show()
|
|
||||||
end })
|
|
||||||
|
|
||||||
local data = {
|
|
||||||
type = "login",
|
|
||||||
account = G.account,
|
|
||||||
password = G.password,
|
|
||||||
token = G.authenticatorToken,
|
|
||||||
version = APP_VERSION,
|
|
||||||
uid = G.UUID
|
|
||||||
}
|
|
||||||
webSocketLoginPacket = data
|
|
||||||
webSocket.send(data)
|
|
||||||
EnterGame.hide()
|
|
||||||
end
|
|
||||||
|
|
||||||
function EnterGame.doLoginHttp()
|
function EnterGame.doLoginHttp()
|
||||||
if G.host == nil or G.host:len() < 10 then
|
if G.host == nil or G.host:len() < 10 then
|
||||||
return EnterGame.onError("Invalid server url: " .. G.host)
|
return EnterGame.onError("Invalid server url: " .. G.host)
|
||||||
|
@ -6,6 +6,10 @@ local installedLocales
|
|||||||
local currentLocale
|
local currentLocale
|
||||||
|
|
||||||
function sendLocale(localeName)
|
function sendLocale(localeName)
|
||||||
|
if not g_game.getFeature(GameExtendedOpcode) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local protocolGame = g_game.getProtocolGame()
|
local protocolGame = g_game.getProtocolGame()
|
||||||
if protocolGame then
|
if protocolGame then
|
||||||
protocolGame:sendExtendedOpcode(ExtendedIds.Locale, localeName)
|
protocolGame:sendExtendedOpcode(ExtendedIds.Locale, localeName)
|
||||||
|
@ -84,6 +84,25 @@ Panel
|
|||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 300
|
maximum: 300
|
||||||
|
|
||||||
|
Label
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
id: walkCtrlTurnDelayLabel
|
||||||
|
margin-top: 10
|
||||||
|
@onSetup: |
|
||||||
|
local value = modules.client_options.getOption('walkTurnDelay')
|
||||||
|
self:setText(tr('Walk delay after ctrl turn: %s ms', value))
|
||||||
|
|
||||||
|
OptionScrollbar
|
||||||
|
id: walkCtrlTurnDelay
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-top: 3
|
||||||
|
minimum: 0
|
||||||
|
maximum: 300
|
||||||
|
|
||||||
Label
|
Label
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -48,7 +48,8 @@ local defaultOptions = {
|
|||||||
walkFirstStepDelay = 200,
|
walkFirstStepDelay = 200,
|
||||||
walkTurnDelay = 100,
|
walkTurnDelay = 100,
|
||||||
walkStairsDelay = 50,
|
walkStairsDelay = 50,
|
||||||
walkTeleportDelay = 200
|
walkTeleportDelay = 200,
|
||||||
|
walkCtrlTurnDelay = 150
|
||||||
}
|
}
|
||||||
|
|
||||||
local optionsWindow
|
local optionsWindow
|
||||||
@ -315,6 +316,8 @@ function setOption(key, value, force)
|
|||||||
generalPanel:getChildById('walkStairsDelayLabel'):setText(tr('Walk delay after floor change: %s ms', value))
|
generalPanel:getChildById('walkStairsDelayLabel'):setText(tr('Walk delay after floor change: %s ms', value))
|
||||||
elseif key == 'walkTeleportDelay' then
|
elseif key == 'walkTeleportDelay' then
|
||||||
generalPanel:getChildById('walkTeleportDelayLabel'):setText(tr('Walk delay after teleport: %s ms', value))
|
generalPanel:getChildById('walkTeleportDelayLabel'):setText(tr('Walk delay after teleport: %s ms', value))
|
||||||
|
elseif key == 'walkCtrlTurnDelay' then
|
||||||
|
generalPanel:getChildById('walkCtrlTurnDelayLabel'):setText(tr('Walk delay after ctrl turn: %s ms', value))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- change value for keybind updates
|
-- change value for keybind updates
|
||||||
|
@ -134,6 +134,7 @@ function sendStats()
|
|||||||
mem = g_platform.getTotalSystemMemory(),
|
mem = g_platform.getTotalSystemMemory(),
|
||||||
mem_usage = g_platform.getMemoryUsage(),
|
mem_usage = g_platform.getMemoryUsage(),
|
||||||
os_name = g_platform.getOSName(),
|
os_name = g_platform.getOSName(),
|
||||||
|
platform = g_window.getPlatformType(),
|
||||||
uptime = g_clock.seconds()
|
uptime = g_clock.seconds()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ function init()
|
|||||||
|
|
||||||
terminalWindow.onDoubleClick = popWindow
|
terminalWindow.onDoubleClick = popWindow
|
||||||
|
|
||||||
--terminalButton = modules.client_topmenu.addLeftButton('terminalButton', tr('Terminal') .. ' (Ctrl + T)', '/images/topbuttons/terminal', toggle)
|
terminalButton = modules.client_topmenu.addLeftButton('terminalButton', tr('Terminal') .. ' (Ctrl + T)', '/images/topbuttons/terminal', toggle)
|
||||||
g_keyboard.bindKeyDown('Ctrl+T', toggle)
|
g_keyboard.bindKeyDown('Ctrl+T', toggle)
|
||||||
|
|
||||||
commandHistory = g_settings.getList('terminal-history')
|
commandHistory = g_settings.getList('terminal-history')
|
||||||
|
@ -7,18 +7,27 @@ HTTP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function HTTP.get(url, callback)
|
function HTTP.get(url, callback)
|
||||||
|
if not g_http or not g_http.get then
|
||||||
|
return error("HTTP.get is not supported")
|
||||||
|
end
|
||||||
local operation = g_http.get(url, HTTP.timeout)
|
local operation = g_http.get(url, HTTP.timeout)
|
||||||
HTTP.operations[operation] = {type="get", url=url, callback=callback}
|
HTTP.operations[operation] = {type="get", url=url, callback=callback}
|
||||||
return operation
|
return operation
|
||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.getJSON(url, callback)
|
function HTTP.getJSON(url, callback)
|
||||||
|
if not g_http or not g_http.get then
|
||||||
|
return error("HTTP.getJSON is not supported")
|
||||||
|
end
|
||||||
local operation = g_http.get(url, HTTP.timeout)
|
local operation = g_http.get(url, HTTP.timeout)
|
||||||
HTTP.operations[operation] = {type="get", json=true, url=url, callback=callback}
|
HTTP.operations[operation] = {type="get", json=true, url=url, callback=callback}
|
||||||
return operation
|
return operation
|
||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.post(url, data, callback)
|
function HTTP.post(url, data, callback)
|
||||||
|
if not g_http or not g_http.post then
|
||||||
|
return error("HTTP.post is not supported")
|
||||||
|
end
|
||||||
if type(data) == "table" then
|
if type(data) == "table" then
|
||||||
data = json.encode(data)
|
data = json.encode(data)
|
||||||
end
|
end
|
||||||
@ -28,6 +37,9 @@ function HTTP.post(url, data, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.postJSON(url, data, callback)
|
function HTTP.postJSON(url, data, callback)
|
||||||
|
if not g_http or not g_http.post then
|
||||||
|
return error("HTTP.postJSON is not supported")
|
||||||
|
end
|
||||||
if type(data) == "table" then
|
if type(data) == "table" then
|
||||||
data = json.encode(data)
|
data = json.encode(data)
|
||||||
end
|
end
|
||||||
@ -37,12 +49,18 @@ function HTTP.postJSON(url, data, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.download(url, file, callback, progressCallback)
|
function HTTP.download(url, file, callback, progressCallback)
|
||||||
|
if not g_http or not g_http.download then
|
||||||
|
return error("HTTP.download is not supported")
|
||||||
|
end
|
||||||
local operation = g_http.download(url, file, HTTP.timeout)
|
local operation = g_http.download(url, file, HTTP.timeout)
|
||||||
HTTP.operations[operation] = {type="download", url=url, file=file, callback=callback, progressCallback=progressCallback}
|
HTTP.operations[operation] = {type="download", url=url, file=file, callback=callback, progressCallback=progressCallback}
|
||||||
return operation
|
return operation
|
||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.downloadImage(url, callback)
|
function HTTP.downloadImage(url, callback)
|
||||||
|
if not g_http or not g_http.download then
|
||||||
|
return error("HTTP.downloadImage is not supported")
|
||||||
|
end
|
||||||
if HTTP.images[url] ~= nil then
|
if HTTP.images[url] ~= nil then
|
||||||
if callback then
|
if callback then
|
||||||
callback('/downloads/' .. HTTP.images[url], nil)
|
callback('/downloads/' .. HTTP.images[url], nil)
|
||||||
@ -57,6 +75,9 @@ function HTTP.downloadImage(url, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function HTTP.webSocket(url, callbacks, timeout, jsonWebsocket)
|
function HTTP.webSocket(url, callbacks, timeout, jsonWebsocket)
|
||||||
|
if not g_http or not g_http.ws then
|
||||||
|
return error("WebSocket is not supported")
|
||||||
|
end
|
||||||
if not timeout or timeout < 1 then
|
if not timeout or timeout < 1 then
|
||||||
timeout = HTTP.websocketTimeout
|
timeout = HTTP.websocketTimeout
|
||||||
end
|
end
|
||||||
@ -84,6 +105,9 @@ end
|
|||||||
HTTP.WebSocketJSON = HTTP.webSocketJSON
|
HTTP.WebSocketJSON = HTTP.webSocketJSON
|
||||||
|
|
||||||
function HTTP.cancel(operationId)
|
function HTTP.cancel(operationId)
|
||||||
|
if not g_http or not g_http.cancel then
|
||||||
|
return
|
||||||
|
end
|
||||||
return g_http.cancel(operationId)
|
return g_http.cancel(operationId)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ function UIComboBox:clearOptions()
|
|||||||
self:clearText()
|
self:clearText()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function UIComboBox:clear()
|
||||||
|
return self:clearOptions()
|
||||||
|
end
|
||||||
|
|
||||||
function UIComboBox:getOptionsCount()
|
function UIComboBox:getOptionsCount()
|
||||||
return #self.options
|
return #self.options
|
||||||
end
|
end
|
||||||
@ -108,12 +112,6 @@ function UIComboBox:removeOption(text)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function UIComboBox:clear()
|
|
||||||
self.options = {}
|
|
||||||
self.currentIndex = -1
|
|
||||||
self:setText("")
|
|
||||||
end
|
|
||||||
|
|
||||||
function UIComboBox:onMousePress(mousePos, mouseButton)
|
function UIComboBox:onMousePress(mousePos, mouseButton)
|
||||||
local menu
|
local menu
|
||||||
if self.menuScroll then
|
if self.menuScroll then
|
||||||
|
@ -1,86 +1,37 @@
|
|||||||
botWindow = nil
|
botWindow = nil
|
||||||
botButton = nil
|
botButton = nil
|
||||||
botConfigFile = nil
|
|
||||||
botConfig = nil
|
|
||||||
contentsPanel = nil
|
contentsPanel = nil
|
||||||
configWindow = nil
|
editWindow = nil
|
||||||
configEditorText = nil
|
|
||||||
configList = nil
|
local checkEvent = nil
|
||||||
botTabs = nil
|
|
||||||
botPanel = nil
|
local botStorage = {}
|
||||||
|
local botStorageFile = nil
|
||||||
local botWebSockets = {}
|
local botWebSockets = {}
|
||||||
local botMessages = nil
|
local botMessages = nil
|
||||||
local configCopy = ""
|
local botTabs = nil
|
||||||
|
local botExecutor = nil
|
||||||
|
|
||||||
|
local configList = nil
|
||||||
local enableButton = nil
|
local enableButton = nil
|
||||||
local executeEvent = nil
|
local executeEvent = nil
|
||||||
local checkMsgsEvent = nil
|
|
||||||
local errorOccured = false
|
|
||||||
local statusLabel = nil
|
local statusLabel = nil
|
||||||
local compiledConfig = nil
|
|
||||||
local configTab = nil
|
|
||||||
local tabs = {"main", "panels", "macros", "hotkeys", "callbacks", "other"}
|
|
||||||
local mainTab = nil
|
|
||||||
local activeTab = nil
|
|
||||||
local editorText = {"", ""}
|
|
||||||
|
|
||||||
function init()
|
function init()
|
||||||
dofile("defaultconfig")
|
|
||||||
dofile("executor")
|
dofile("executor")
|
||||||
|
|
||||||
g_ui.importStyle("ui/basic.otui")
|
g_ui.importStyle("ui/basic.otui")
|
||||||
g_ui.importStyle("ui/panels.otui")
|
g_ui.importStyle("ui/panels.otui")
|
||||||
|
g_ui.importStyle("ui/config.otui")
|
||||||
|
|
||||||
connect(g_game, {
|
connect(g_game, {
|
||||||
onGameStart = online,
|
onGameStart = online,
|
||||||
onGameEnd = offline,
|
onGameEnd = offline,
|
||||||
onTalk = botOnTalk,
|
|
||||||
onTextMessage = botOnTextMessage,
|
|
||||||
onUse = botOnUse,
|
|
||||||
onUseWith = botOnUseWith,
|
|
||||||
onChannelList = botChannelList,
|
|
||||||
onOpenChannel = botOpenChannel,
|
|
||||||
onCloseChannel = botCloseChannel,
|
|
||||||
onChannelEvent = botChannelEvent
|
|
||||||
})
|
})
|
||||||
|
|
||||||
connect(rootWidget, { onKeyDown = botKeyDown,
|
|
||||||
onKeyUp = botKeyUp,
|
|
||||||
onKeyPress = botKeyPress })
|
|
||||||
|
|
||||||
connect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
|
|
||||||
|
|
||||||
connect(Creature, {
|
|
||||||
onAppear = botCreatureAppear,
|
|
||||||
onDisappear = botCreatureDisappear,
|
|
||||||
onPositionChange = botCreaturePositionChange,
|
|
||||||
onHealthPercentChange = botCraetureHealthPercentChange
|
|
||||||
})
|
|
||||||
connect(LocalPlayer, {
|
|
||||||
onPositionChange = botCreaturePositionChange,
|
|
||||||
onHealthPercentChange = botCraetureHealthPercentChange
|
|
||||||
})
|
|
||||||
connect(Container, { onOpen = botContainerOpen,
|
|
||||||
onClose = botContainerClose,
|
|
||||||
onUpdateItem = botContainerUpdateItem })
|
|
||||||
connect(g_map, { onMissle = botOnMissle })
|
|
||||||
|
|
||||||
botConfigFile = g_configs.create("/bot.otml")
|
initCallbacks()
|
||||||
local config = botConfigFile:get("config")
|
|
||||||
if config ~= nil and config:len() > 10 then
|
|
||||||
local status, result = pcall(function() return json.decode(config) end)
|
|
||||||
if not status then
|
|
||||||
g_logger.error("Error: bot config parse error: " .. result .. "\n" .. config)
|
|
||||||
end
|
|
||||||
botConfig = result
|
|
||||||
else
|
|
||||||
botConfig = botDefaultConfig
|
|
||||||
end
|
|
||||||
|
|
||||||
botConfig.configs[1].name = botDefaultConfig.configs[1].name
|
botButton = modules.client_topmenu.addRightGameToggleButton('botButton', tr('Bot'), '/images/topbuttons/bot', toggle)
|
||||||
botConfig.configs[1].script = botDefaultConfig.configs[1].script
|
|
||||||
|
|
||||||
botButton = modules.client_topmenu.addRightGameToggleButton('botButton',
|
|
||||||
tr('Bot'), '/images/topbuttons/bot', toggle)
|
|
||||||
botButton:setOn(false)
|
botButton:setOn(false)
|
||||||
botButton:hide()
|
botButton:hide()
|
||||||
|
|
||||||
@ -93,107 +44,188 @@ function init()
|
|||||||
statusLabel = contentsPanel.statusLabel
|
statusLabel = contentsPanel.statusLabel
|
||||||
botMessages = contentsPanel.messages
|
botMessages = contentsPanel.messages
|
||||||
botTabs = contentsPanel.botTabs
|
botTabs = contentsPanel.botTabs
|
||||||
botPanel = contentsPanel.botPanel
|
botTabs:setContentWidget(contentsPanel.botPanel)
|
||||||
botTabs:setContentWidget(botPanel)
|
|
||||||
|
|
||||||
configWindow = g_ui.displayUI('config')
|
|
||||||
configWindow:hide()
|
|
||||||
|
|
||||||
configEditorText = configWindow.text
|
|
||||||
configTab = configWindow.configTab
|
|
||||||
|
|
||||||
configTab.onTabChange = editorTabChanged
|
|
||||||
|
|
||||||
for i=1,#botConfig.configs do
|
|
||||||
if botConfig.configs[i].name ~= nil then
|
|
||||||
configList:addOption(botConfig.configs[i].name)
|
|
||||||
else
|
|
||||||
configList:addOption("Config #" .. i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if type(botConfig.selectedConfig) == 'number' then
|
|
||||||
configList:setCurrentIndex(botConfig.selectedConfig)
|
|
||||||
end
|
|
||||||
configList.onOptionChange = modules.game_bot.refreshConfig
|
|
||||||
|
|
||||||
mainTab = configTab:addTab("all")
|
|
||||||
for k, v in ipairs(tabs) do
|
|
||||||
configTab:addTab(v, nil, nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
editWindow = g_ui.displayUI('edit')
|
||||||
|
editWindow:hide()
|
||||||
|
|
||||||
if g_game.isOnline() then
|
if g_game.isOnline() then
|
||||||
|
clear()
|
||||||
online()
|
online()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function saveConfig()
|
|
||||||
local status, result = pcall(function()
|
|
||||||
botConfigFile:set("config", json.encode(botConfig))
|
|
||||||
botConfigFile:save()
|
|
||||||
end)
|
|
||||||
if not status then
|
|
||||||
errorOccured = true
|
|
||||||
-- try to fix it
|
|
||||||
local extraInfo = ""
|
|
||||||
for i = 1, #botConfig.configs do
|
|
||||||
if botConfig.configs[i].storage then
|
|
||||||
local status, result = pcall(function() json.encode(botConfig.configs[i].storage) end)
|
|
||||||
if not status then
|
|
||||||
botConfig.configs[i].storage = nil
|
|
||||||
extraInfo = extraInfo .. "\nLocal storage of config " .. i .. " has been erased due to invalid data"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
statusLabel:setText("Error while saving config and user storage:\n" .. result .. extraInfo .. "\n\n" .. "Try to restart bot")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function terminate()
|
function terminate()
|
||||||
saveConfig()
|
save()
|
||||||
clearConfig()
|
clear()
|
||||||
|
|
||||||
disconnect(rootWidget, { onKeyDown = botKeyDown,
|
|
||||||
onKeyUp = botKeyUp,
|
|
||||||
onKeyPress = botKeyPress })
|
|
||||||
|
|
||||||
disconnect(g_game, {
|
disconnect(g_game, {
|
||||||
onGameStart = online,
|
onGameStart = online,
|
||||||
onGameEnd = offline,
|
onGameEnd = offline,
|
||||||
onTalk = botOnTalk,
|
|
||||||
onTextMessage = botOnTextMessage,
|
|
||||||
onUse = botOnUse,
|
|
||||||
onUseWith = botOnUseWith,
|
|
||||||
onChannelList = botChannelList,
|
|
||||||
onOpenChannel = botOpenChannel,
|
|
||||||
onCloseChannel = botCloseChannel,
|
|
||||||
onChannelEvent = botChannelEvent
|
|
||||||
})
|
})
|
||||||
|
|
||||||
disconnect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
|
terminateCallbacks()
|
||||||
|
|
||||||
disconnect(Creature, {
|
removeEvent(checkEvent)
|
||||||
onAppear = botCreatureAppear,
|
|
||||||
onDisappear =botCreatureDisappear,
|
|
||||||
onPositionChange = botCreaturePositionChange,
|
|
||||||
onHealthPercentChange = botCraetureHealthPercentChange
|
|
||||||
})
|
|
||||||
disconnect(LocalPlayer, {
|
|
||||||
onPositionChange = botCreaturePositionChange,
|
|
||||||
onHealthPercentChange = botCraetureHealthPercentChange
|
|
||||||
})
|
|
||||||
disconnect(Container, { onOpen = botContainerOpen,
|
|
||||||
onClose = botContainerClose,
|
|
||||||
onUpdateItem = botContainerUpdateItem })
|
|
||||||
disconnect(g_map, { onMissle = botOnMissle })
|
|
||||||
|
|
||||||
removeEvent(executeEvent)
|
editWindow:destroy()
|
||||||
removeEvent(checkMsgsEvent)
|
|
||||||
|
|
||||||
botWindow:destroy()
|
botWindow:destroy()
|
||||||
botButton:destroy()
|
botButton:destroy()
|
||||||
configWindow:destroy()
|
end
|
||||||
|
|
||||||
|
function clear()
|
||||||
|
botExecutor = nil
|
||||||
|
removeEvent(checkEvent)
|
||||||
|
|
||||||
|
-- optimization, callback is not used when not needed
|
||||||
|
g_game.enableTileThingLuaCallback(false)
|
||||||
|
|
||||||
|
botTabs:clearTabs()
|
||||||
|
botTabs:setOn(false)
|
||||||
|
|
||||||
|
botMessages:destroyChildren()
|
||||||
|
botMessages:updateLayout()
|
||||||
|
|
||||||
|
for i, socket in pairs(botWebSockets) do
|
||||||
|
g_http.cancel(socket)
|
||||||
|
end
|
||||||
|
botWebSockets = {}
|
||||||
|
|
||||||
|
for i, widget in pairs(g_ui.getRootWidget():getChildren()) do
|
||||||
|
if widget.botWidget then
|
||||||
|
widget:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local gameMapPanel = modules.game_interface.getMapPanel()
|
||||||
|
if gameMapPanel then
|
||||||
|
gameMapPanel:unlockVisibleFloor()
|
||||||
|
end
|
||||||
|
|
||||||
|
if g_sounds then
|
||||||
|
g_sounds.getChannel(SoundChannels.Bot):stop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function refresh()
|
||||||
|
save()
|
||||||
|
clear()
|
||||||
|
|
||||||
|
-- create bot dir
|
||||||
|
if not g_resources.directoryExists("/bot") then
|
||||||
|
g_resources.makeDir("/bot")
|
||||||
|
if not g_resources.directoryExists("/bot") then
|
||||||
|
return onError("Can't create bot directory in " .. g_resources.getWriteDir())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get list of configs
|
||||||
|
local configs = g_resources.listDirectoryFiles("/bot", false, false)
|
||||||
|
if #configs == 0 then
|
||||||
|
createDefaultConfig()
|
||||||
|
configs = g_resources.listDirectoryFiles("/bot", false, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- clean
|
||||||
|
configList.onOptionChange = nil
|
||||||
|
enableButton.onClick = nil
|
||||||
|
configList:clearOptions()
|
||||||
|
|
||||||
|
-- select active config based on settings
|
||||||
|
local settings = g_settings.getNode('bot') or {}
|
||||||
|
local index = g_game.getCharacterName() .. "_" .. g_game.getClientVersion()
|
||||||
|
if settings[index] == nil then
|
||||||
|
settings[index] = {
|
||||||
|
enabled=false,
|
||||||
|
config=""
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- init list and buttons
|
||||||
|
for i=1,#configs do
|
||||||
|
configList:addOption(configs[i])
|
||||||
|
end
|
||||||
|
configList:setCurrentOption(settings[index].config)
|
||||||
|
if configList:getCurrentOption().text ~= settings[index].config then
|
||||||
|
settings[index].enabled = false
|
||||||
|
end
|
||||||
|
|
||||||
|
enableButton:setOn(settings[index].enabled)
|
||||||
|
|
||||||
|
configList.onOptionChange = function(widget)
|
||||||
|
settings[index].config = widget:getCurrentOption().text
|
||||||
|
settings[index].enabled = false
|
||||||
|
g_settings.setNode('bot', settings)
|
||||||
|
g_settings.save()
|
||||||
|
refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
enableButton.onClick = function(widget)
|
||||||
|
settings[index].enabled = not settings[index].enabled
|
||||||
|
g_settings.setNode('bot', settings)
|
||||||
|
g_settings.save()
|
||||||
|
refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not g_game.isOnline() or not settings[index].enabled then
|
||||||
|
statusLabel:setOn(true)
|
||||||
|
statusLabel:setText("Status: disabled")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local configName = settings[index].config
|
||||||
|
|
||||||
|
-- storage
|
||||||
|
botStorage = {}
|
||||||
|
botStorageFile = "/bot/" .. configName .. "/storage.json"
|
||||||
|
if g_resources.fileExists(botStorageFile) then
|
||||||
|
local status, result = pcall(function()
|
||||||
|
return json.decode(g_resources.readFileContents(botStorageFile))
|
||||||
|
end)
|
||||||
|
if not status then
|
||||||
|
return onError("Error while reading storage (" .. botStorageFile .. "). To fix this problem you can delete storage.json. Details: " .. result)
|
||||||
|
end
|
||||||
|
botStorage = result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- run script
|
||||||
|
local status, result = pcall(function()
|
||||||
|
return executeBot(configName, botStorage, botTabs, message, save, botWebSockets) end
|
||||||
|
)
|
||||||
|
if not status then
|
||||||
|
return onError(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
statusLabel:setOn(false)
|
||||||
|
botExecutor = result
|
||||||
|
check()
|
||||||
|
end
|
||||||
|
|
||||||
|
function save()
|
||||||
|
if not botExecutor then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local settings = g_settings.getNode('bot') or {}
|
||||||
|
local index = g_game.getCharacterName() .. "_" .. g_game.getClientVersion()
|
||||||
|
if settings[index] == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, result = pcall(function()
|
||||||
|
return json.encode(botStorage)
|
||||||
|
end)
|
||||||
|
if not status then
|
||||||
|
return onError("Error while saving bot storage. Storage won't be saved. Details: " .. result)
|
||||||
|
end
|
||||||
|
|
||||||
|
if result:len() > 100 * 1024 * 1024 then
|
||||||
|
return onError("Storage file is too big, above 100MB, it won't be saved")
|
||||||
|
end
|
||||||
|
|
||||||
|
g_resources.writeFileContents(botStorageFile, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
function onMiniWindowClose()
|
function onMiniWindowClose()
|
||||||
@ -212,228 +244,63 @@ end
|
|||||||
|
|
||||||
function online()
|
function online()
|
||||||
botButton:show()
|
botButton:show()
|
||||||
updateEnabled()
|
scheduleEvent(refresh, 20)
|
||||||
if botConfig.enabled then
|
|
||||||
scheduleEvent(refreshConfig, 20)
|
|
||||||
else
|
|
||||||
clearConfig()
|
|
||||||
end
|
|
||||||
if executeEvent == nil then
|
|
||||||
executeEvent = scheduleEvent(executeConfig, 200)
|
|
||||||
checkMsgsEvent = scheduleEvent(checkMsgs, 200)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function offline()
|
function offline()
|
||||||
|
save()
|
||||||
|
clear()
|
||||||
botButton:hide()
|
botButton:hide()
|
||||||
configWindow:hide()
|
editWindow:hide()
|
||||||
clearConfig()
|
|
||||||
removeEvent(executeEvent)
|
|
||||||
removeEvent(checkMsgsEvent)
|
|
||||||
executeEvent = nil
|
|
||||||
checkMsgsEvent = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function toggleBot()
|
function onError(message)
|
||||||
botConfig.enabled = not botConfig.enabled
|
statusLabel:setOn(true)
|
||||||
if botConfig.enabled then
|
statusLabel:setText("Error:\n" .. message)
|
||||||
refreshConfig()
|
g_logger.error("[BOT] " .. message)
|
||||||
else
|
|
||||||
clearConfig()
|
|
||||||
end
|
|
||||||
updateEnabled()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function updateEnabled()
|
function edit()
|
||||||
if botConfig.enabled then
|
editWindow:show()
|
||||||
enableButton:setText(tr('On'))
|
editWindow:focus()
|
||||||
enableButton:setColor('#00AA00FF')
|
editWindow:raise()
|
||||||
else
|
|
||||||
enableButton:setText(tr('Off'))
|
|
||||||
enableButton:setColor('#FF0000FF')
|
|
||||||
statusLabel:setText(tr("Status: disabled"))
|
|
||||||
end
|
|
||||||
errorOccured = false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function editConfig()
|
function createDefaultConfig()
|
||||||
local config = configList.currentIndex
|
if not g_resources.directoryExists("/bot/default_config") then
|
||||||
configWindow:show()
|
g_resources.makeDir("/bot/default_config")
|
||||||
configWindow:raise()
|
if not g_resources.directoryExists("/bot/default_config") then
|
||||||
configWindow:focus()
|
return onError("Can't create default_config directory in " .. g_resources.getWriteDir())
|
||||||
editorText = {botConfig.configs[config].script or "", ""}
|
|
||||||
if #editorText[1] <= 2 then
|
|
||||||
editorText[1] = "--config name\n\n"
|
|
||||||
for k, v in ipairs(tabs) do
|
|
||||||
editorText[1] = editorText[1] .. "--#" .. v .. "\n\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
configEditorText:setText(editorText[1])
|
|
||||||
configEditorText:setEditable(true)
|
|
||||||
activeTab = mainTab
|
|
||||||
configTab:selectTab(mainTab)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function split2(str, delimiter)
|
|
||||||
local result = { }
|
|
||||||
local from = 1
|
|
||||||
local delim_from, delim_to = string.find( str, delimiter, from, true)
|
|
||||||
if delim_from then
|
|
||||||
table.insert( result, string.sub( str, from , delim_from - 1 ) )
|
|
||||||
from = delim_to + 1
|
|
||||||
delim_from, delim_to = string.find( str, delimiter, from )
|
|
||||||
table.insert( result, string.sub( str, from ) )
|
|
||||||
else
|
|
||||||
table.insert(result, str)
|
|
||||||
table.insert(result, "")
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function restoreMainTab()
|
|
||||||
if activeTab == mainTab then
|
|
||||||
editorText = {configEditorText:getText(), ""}
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local currentText = configEditorText:getText()
|
|
||||||
if #currentText > 0 and currentText:sub(#currentText, #currentText) ~= '\n' then
|
|
||||||
currentText = currentText .. '\n'
|
|
||||||
end
|
|
||||||
editorText = {editorText[1] .. "--#" .. activeTab:getText():lower() .. "\n" .. currentText .. editorText[2], ""}
|
|
||||||
configEditorText:setText(editorText[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
function editorTabChanged(holder, tab)
|
|
||||||
if activeTab == tab then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
restoreMainTab()
|
|
||||||
activeTab = tab
|
|
||||||
if tab == mainTab then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local splitted = split2(editorText[1], "--#" .. activeTab:getText():lower() .. "\n")
|
|
||||||
local splitted2 = split2(splitted[2], "--#")
|
|
||||||
if splitted2[2]:len() > 1 then
|
|
||||||
splitted2[2] = "--#" .. splitted2[2]
|
|
||||||
end
|
|
||||||
editorText = {splitted[1], splitted2[2]}
|
|
||||||
configEditorText:setText(splitted2[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
function saveEditedConfig()
|
|
||||||
restoreMainTab()
|
|
||||||
local config = configList.currentIndex
|
|
||||||
local text = configEditorText:getText()
|
|
||||||
configWindow:hide()
|
|
||||||
botConfig.configs[config].script = text
|
|
||||||
if text:len() > 3 and text:sub(1,2) == '--' and text:sub(3,3) ~= '#' then
|
|
||||||
local delim_from, delim_to = string.find( text, "\n", 3, true)
|
|
||||||
if delim_from then
|
|
||||||
botConfig.configs[config].name = string.sub( text, 3 , delim_from - 1 ):trim()
|
|
||||||
configList:updateCurrentOption(botConfig.configs[config].name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
refreshConfig()
|
|
||||||
end
|
|
||||||
|
|
||||||
function clearConfig()
|
|
||||||
compiledConfig = nil
|
|
||||||
|
|
||||||
botTabs:clearTabs()
|
|
||||||
botTabs:setOn(false)
|
|
||||||
|
|
||||||
botMessages:destroyChildren()
|
|
||||||
botMessages:updateLayout()
|
|
||||||
|
|
||||||
for socket in pairs(botWebSockets) do
|
|
||||||
g_http.cancel(socket)
|
|
||||||
botWebSockets[socket] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, widget in pairs(g_ui.getRootWidget():getChildren()) do
|
|
||||||
if widget.botWidget then
|
|
||||||
widget:destroy()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local gameMapPanel = modules.game_interface.getMapPanel()
|
|
||||||
if gameMapPanel then
|
|
||||||
gameMapPanel:unlockVisibleFloor()
|
|
||||||
end
|
|
||||||
if g_sounds then
|
|
||||||
local botSoundChannel = g_sounds.getChannel(SoundChannels.Bot)
|
|
||||||
botSoundChannel:stop()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function refreshConfig()
|
local defaultConfigFiles = g_resources.listDirectoryFiles("default_config", true, false)
|
||||||
configWindow:hide()
|
for i, file in ipairs(defaultConfigFiles) do
|
||||||
|
local baseName = file:split("/")
|
||||||
botConfig.selectedConfig = configList.currentIndex
|
baseName = baseName[#baseName]
|
||||||
if not botConfig.enabled then
|
local contents = g_resources.readFileContents(file)
|
||||||
return
|
if contents:len() > 0 then
|
||||||
end
|
g_resources.writeFileContents("/bot/default_config/" .. baseName, contents)
|
||||||
|
|
||||||
if not saveConfig() then
|
|
||||||
clearConfig()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
clearConfig()
|
|
||||||
|
|
||||||
local config = botConfig.configs[configList.currentIndex]
|
|
||||||
if not config.storage then
|
|
||||||
config.storage = {}
|
|
||||||
end
|
|
||||||
if config.script == nil or config.script:len() < 5 then
|
|
||||||
errorOccured = true
|
|
||||||
statusLabel:setText(tr("Error: empty config"))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
errorOccured = false
|
|
||||||
g_game.enableTileThingLuaCallback(false)
|
|
||||||
local status, result = pcall(function() return executeBot(config.script, config.storage, botTabs, botMsgCallback, saveConfig, botWebSockets) end)
|
|
||||||
if not status then
|
|
||||||
errorOccured = true
|
|
||||||
statusLabel:setText("Error: " .. tostring(result))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
compiledConfig = result
|
|
||||||
statusLabel:setText(tr("Status: working"))
|
|
||||||
end
|
|
||||||
|
|
||||||
function executeConfig()
|
|
||||||
executeEvent = scheduleEvent(executeConfig, 25)
|
|
||||||
if compiledConfig == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not botConfig.enabled or errorOccured then
|
|
||||||
if not errorOccured then
|
|
||||||
statusLabel:setText(tr("Status: disabled"))
|
|
||||||
end
|
end
|
||||||
return
|
|
||||||
end
|
end
|
||||||
local status, result = pcall(function() return compiledConfig.script() end)
|
|
||||||
if not status then
|
|
||||||
errorOccured = true
|
|
||||||
statusLabel:setText("Error: " .. result)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function botMsgCallback(category, msg)
|
-- Executor
|
||||||
|
function message(category, msg)
|
||||||
local widget = g_ui.createWidget('BotLabel', botMessages)
|
local widget = g_ui.createWidget('BotLabel', botMessages)
|
||||||
widget.added = g_clock.millis()
|
widget.added = g_clock.millis()
|
||||||
if category == 'error' then
|
if category == 'error' then
|
||||||
widget:setText(msg)
|
widget:setText(msg)
|
||||||
widget:setColor("red")
|
widget:setColor("red")
|
||||||
|
g_logger.error("[BOT] " .. msg)
|
||||||
elseif category == 'warn' then
|
elseif category == 'warn' then
|
||||||
widget:setText(msg)
|
widget:setText(msg)
|
||||||
widget:setColor("yellow")
|
widget:setColor("yellow")
|
||||||
|
g_logger.warning("[BOT] " .. msg)
|
||||||
elseif category == 'info' then
|
elseif category == 'info' then
|
||||||
widget:setText(msg)
|
widget:setText(msg)
|
||||||
widget:setColor("white")
|
widget:setColor("white")
|
||||||
|
g_logger.info("[BOT] " .. msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
if botMessages:getChildCount() > 5 then
|
if botMessages:getChildCount() > 5 then
|
||||||
@ -441,132 +308,233 @@ function botMsgCallback(category, msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function checkMsgs()
|
function check()
|
||||||
checkMsgsEvent = scheduleEvent(checkMsgs, 200)
|
removeEvent(checkEvent)
|
||||||
|
if not botExecutor then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
checkEvent = scheduleEvent(check, 25)
|
||||||
|
|
||||||
|
local status, result = pcall(function()
|
||||||
|
return botExecutor.script()
|
||||||
|
end)
|
||||||
|
if not status then
|
||||||
|
botExecutor = nil -- critical
|
||||||
|
return onError(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove old messages
|
||||||
local widget = botMessages:getFirstChild()
|
local widget = botMessages:getFirstChild()
|
||||||
if widget and widget.added + 5000 < g_clock.millis() then
|
if widget and widget.added + 5000 < g_clock.millis() then
|
||||||
widget:destroy()
|
widget:destroy()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Callbacks
|
||||||
|
function initCallbacks()
|
||||||
|
connect(rootWidget, {
|
||||||
|
onKeyDown = botKeyDown,
|
||||||
|
onKeyUp = botKeyUp,
|
||||||
|
onKeyPress = botKeyPress
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(g_game, {
|
||||||
|
onTalk = botOnTalk,
|
||||||
|
onTextMessage = botOnTextMessage,
|
||||||
|
onUse = botOnUse,
|
||||||
|
onUseWith = botOnUseWith,
|
||||||
|
onChannelList = botChannelList,
|
||||||
|
onOpenChannel = botOpenChannel,
|
||||||
|
onCloseChannel = botCloseChannel,
|
||||||
|
onChannelEvent = botChannelEvent
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(Tile, {
|
||||||
|
onAddThing = botAddThing,
|
||||||
|
onRemoveThing = botRemoveThing
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(Creature, {
|
||||||
|
onAppear = botCreatureAppear,
|
||||||
|
onDisappear = botCreatureDisappear,
|
||||||
|
onPositionChange = botCreaturePositionChange,
|
||||||
|
onHealthPercentChange = botCraetureHealthPercentChange
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(LocalPlayer, {
|
||||||
|
onPositionChange = botCreaturePositionChange,
|
||||||
|
onHealthPercentChange = botCraetureHealthPercentChange
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(Container, {
|
||||||
|
onOpen = botContainerOpen,
|
||||||
|
onClose = botContainerClose,
|
||||||
|
onUpdateItem = botContainerUpdateItem
|
||||||
|
})
|
||||||
|
|
||||||
|
connect(g_map, {
|
||||||
|
onMissle = botOnMissle
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function terminateCallbacks()
|
||||||
|
disconnect(rootWidget, {
|
||||||
|
onKeyDown = botKeyDown,
|
||||||
|
onKeyUp = botKeyUp,
|
||||||
|
onKeyPress = botKeyPress
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(g_game, {
|
||||||
|
onTalk = botOnTalk,
|
||||||
|
onTextMessage = botOnTextMessage,
|
||||||
|
onUse = botOnUse,
|
||||||
|
onUseWith = botOnUseWith,
|
||||||
|
onChannelList = botChannelList,
|
||||||
|
onOpenChannel = botOpenChannel,
|
||||||
|
onCloseChannel = botCloseChannel,
|
||||||
|
onChannelEvent = botChannelEvent
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(Tile, {
|
||||||
|
onAddThing = botAddThing,
|
||||||
|
onRemoveThing = botRemoveThing
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(Creature, {
|
||||||
|
onAppear = botCreatureAppear,
|
||||||
|
onDisappear = botCreatureDisappear,
|
||||||
|
onPositionChange = botCreaturePositionChange,
|
||||||
|
onHealthPercentChange = botCraetureHealthPercentChange
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(LocalPlayer, {
|
||||||
|
onPositionChange = botCreaturePositionChange,
|
||||||
|
onHealthPercentChange = botCraetureHealthPercentChange
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(Container, {
|
||||||
|
onOpen = botContainerOpen,
|
||||||
|
onClose = botContainerClose,
|
||||||
|
onUpdateItem = botContainerUpdateItem
|
||||||
|
})
|
||||||
|
|
||||||
|
disconnect(g_map, {
|
||||||
|
onMissle = botOnMissle
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
function safeBotCall(func)
|
function safeBotCall(func)
|
||||||
local status, result = pcall(func)
|
local status, result = pcall(func)
|
||||||
if not status then
|
if not status then
|
||||||
errorOccured = true
|
onError(result)
|
||||||
statusLabel:setText("Error: " .. result)
|
|
||||||
end
|
end
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function botKeyDown(widget, keyCode, keyboardModifiers)
|
function botKeyDown(widget, keyCode, keyboardModifiers)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
if keyCode == KeyUnknown then return end
|
if keyCode == KeyUnknown then return end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
|
safeBotCall(function() botExecutor.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botKeyUp(widget, keyCode, keyboardModifiers)
|
function botKeyUp(widget, keyCode, keyboardModifiers)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
if keyCode == KeyUnknown then return end
|
if keyCode == KeyUnknown then return end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onKeyUp(keyCode, keyboardModifiers) end)
|
safeBotCall(function() botExecutor.callbacks.onKeyUp(keyCode, keyboardModifiers) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
|
function botKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
if keyCode == KeyUnknown then return end
|
if keyCode == KeyUnknown then return end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end)
|
safeBotCall(function() botExecutor.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOnTalk(name, level, mode, text, channelId, pos)
|
function botOnTalk(name, level, mode, text, channelId, pos)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onTalk(name, level, mode, text, channelId, pos) end)
|
safeBotCall(function() botExecutor.callbacks.onTalk(name, level, mode, text, channelId, pos) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOnTextMessage(mode, text)
|
function botOnTextMessage(mode, text)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onTextMessage(mode, text) end)
|
safeBotCall(function() botExecutor.callbacks.onTextMessage(mode, text) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botAddThing(tile, thing)
|
function botAddThing(tile, thing)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onAddThing(tile, thing) end)
|
safeBotCall(function() botExecutor.callbacks.onAddThing(tile, thing) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botRemoveThing(tile, thing)
|
function botRemoveThing(tile, thing)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end)
|
safeBotCall(function() botExecutor.callbacks.onRemoveThing(tile, thing) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botCreatureAppear(creature)
|
function botCreatureAppear(creature)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onCreatureAppear(creature) end)
|
safeBotCall(function() botExecutor.callbacks.onCreatureAppear(creature) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botCreatureDisappear(creature)
|
function botCreatureDisappear(creature)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onCreatureDisappear(creature) end)
|
safeBotCall(function() botExecutor.callbacks.onCreatureDisappear(creature) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botCreaturePositionChange(creature, newPos, oldPos)
|
function botCreaturePositionChange(creature, newPos, oldPos)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onCreaturePositionChange(creature, newPos, oldPos) end)
|
safeBotCall(function() botExecutor.callbacks.onCreaturePositionChange(creature, newPos, oldPos) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botCraetureHealthPercentChange(creature, healthPercent)
|
function botCraetureHealthPercentChange(creature, healthPercent)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onCreatureHealthPercentChange(creature, healthPercent) end)
|
safeBotCall(function() botExecutor.callbacks.onCreatureHealthPercentChange(creature, healthPercent) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOnUse(pos, itemId, stackPos, subType)
|
function botOnUse(pos, itemId, stackPos, subType)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onUse(pos, itemId, stackPos, subType) end)
|
safeBotCall(function() botExecutor.callbacks.onUse(pos, itemId, stackPos, subType) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOnUseWith(pos, itemId, target, subType)
|
function botOnUseWith(pos, itemId, target, subType)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onUseWith(pos, itemId, target, subType) end)
|
safeBotCall(function() botExecutor.callbacks.onUseWith(pos, itemId, target, subType) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botContainerOpen(container, previousContainer)
|
function botContainerOpen(container, previousContainer)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onContainerOpen(container, previousContainer) end)
|
safeBotCall(function() botExecutor.callbacks.onContainerOpen(container, previousContainer) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botContainerClose(container)
|
function botContainerClose(container)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onContainerClose(container) end)
|
safeBotCall(function() botExecutor.callbacks.onContainerClose(container) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botContainerUpdateItem(container, slot, item)
|
function botContainerUpdateItem(container, slot, item)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onContainerUpdateItem(container, slot, item) end)
|
safeBotCall(function() botExecutor.callbacks.onContainerUpdateItem(container, slot, item) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOnMissle(missle)
|
function botOnMissle(missle)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onMissle(missle) end)
|
safeBotCall(function() botExecutor.callbacks.onMissle(missle) end)
|
||||||
end
|
|
||||||
|
|
||||||
function botOnMissle(missle)
|
|
||||||
if compiledConfig == nil then return false end
|
|
||||||
safeBotCall(function() compiledConfig.callbacks.onMissle(missle) end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function botChannelList(channels)
|
function botChannelList(channels)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onChannelList(channels) end)
|
safeBotCall(function() botExecutor.callbacks.onChannelList(channels) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botOpenChannel(channelId, name)
|
function botOpenChannel(channelId, name)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onOpenChannel(channelId, name) end)
|
safeBotCall(function() botExecutor.callbacks.onOpenChannel(channelId, name) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botCloseChannel(channelId)
|
function botCloseChannel(channelId)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onCloseChannel(channelId) end)
|
safeBotCall(function() botExecutor.callbacks.onCloseChannel(channelId) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function botChannelEvent(channelId, name, event)
|
function botChannelEvent(channelId, name, event)
|
||||||
if compiledConfig == nil then return false end
|
if botExecutor == nil then return false end
|
||||||
safeBotCall(function() compiledConfig.callbacks.onChannelEvent(channelId, name, event) end)
|
safeBotCall(function() botExecutor.callbacks.onChannelEvent(channelId, name, event) end)
|
||||||
end
|
end
|
||||||
|
@ -8,16 +8,14 @@ MiniWindow
|
|||||||
&autoOpen: 10
|
&autoOpen: 10
|
||||||
|
|
||||||
MiniWindowContents
|
MiniWindowContents
|
||||||
margin-left: 5
|
|
||||||
margin-right: 3
|
|
||||||
|
|
||||||
ComboBox
|
ComboBox
|
||||||
id: config
|
id: config
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
margin-top: 2
|
margin-top: 2
|
||||||
margin-right: 90
|
margin-left: 2
|
||||||
|
margin-right: 75
|
||||||
text-offset: 3 0
|
text-offset: 3 0
|
||||||
|
|
||||||
Button
|
Button
|
||||||
@ -26,34 +24,52 @@ MiniWindow
|
|||||||
anchors.left: prev.right
|
anchors.left: prev.right
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
!text: tr('Edit')
|
!text: tr('Edit')
|
||||||
@onClick: modules.game_bot.editConfig()
|
@onClick: modules.game_bot.edit()
|
||||||
margin-left: 5
|
margin-left: 3
|
||||||
margin-right: 45
|
margin-right: 37
|
||||||
|
|
||||||
Button
|
Button
|
||||||
id: enableButton
|
id: enableButton
|
||||||
anchors.top: prev.top
|
anchors.top: prev.top
|
||||||
anchors.left: prev.right
|
anchors.left: prev.right
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@onClick: modules.game_bot.toggleBot()
|
margin-left: 3
|
||||||
margin-left: 5
|
margin-right: 2
|
||||||
|
|
||||||
|
$on:
|
||||||
|
text: On
|
||||||
|
color: #00AA00
|
||||||
|
|
||||||
|
$!on:
|
||||||
|
text: Off
|
||||||
|
color: #FF0000
|
||||||
|
|
||||||
Label
|
Label
|
||||||
id: statusLabel
|
id: statusLabel
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
margin-top: 3
|
|
||||||
text-auto-resize: true
|
|
||||||
!text: tr('Status: waiting')
|
|
||||||
text-align: center
|
|
||||||
text-wrap: true
|
text-wrap: true
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
!text: tr('Status: waiting')
|
||||||
|
margin-left: 3
|
||||||
|
margin-right: 3
|
||||||
|
|
||||||
|
$on:
|
||||||
|
margin-top: 3
|
||||||
|
|
||||||
|
$!on:
|
||||||
|
text:
|
||||||
|
margin-top: -13
|
||||||
|
|
||||||
HorizontalSeparator
|
HorizontalSeparator
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
margin-top: 3
|
margin-top: 3
|
||||||
|
margin-left: 2
|
||||||
|
margin-right: 2
|
||||||
|
|
||||||
Panel
|
Panel
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
@ -69,6 +85,8 @@ MiniWindow
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
margin-top: 5
|
margin-top: 5
|
||||||
|
margin-left: 2
|
||||||
|
margin-right: 2
|
||||||
|
|
||||||
MoveableTabBar
|
MoveableTabBar
|
||||||
id: botTabs
|
id: botTabs
|
||||||
@ -76,6 +94,8 @@ MiniWindow
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
tab-spacing: 1
|
tab-spacing: 1
|
||||||
|
margin-left: 1
|
||||||
|
margin-right: 1
|
||||||
height: 20
|
height: 20
|
||||||
movable: false
|
movable: false
|
||||||
|
|
||||||
|
BIN
modules/game_bot/configs.png
Normal file
BIN
modules/game_bot/configs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
8
modules/game_bot/default_config/battle.lua
Normal file
8
modules/game_bot/default_config/battle.lua
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
local batTab = addTab("Batt")
|
||||||
|
|
||||||
|
Panels.AttackSpell(batTab)
|
||||||
|
Panels.AttackItem(batTab)
|
||||||
|
|
||||||
|
Panels.AttackLeaderTarget(batTab)
|
||||||
|
Panels.LimitFloor(batTab)
|
||||||
|
Panels.AntiPush(batTab)
|
8
modules/game_bot/default_config/cavebot.lua
Normal file
8
modules/game_bot/default_config/cavebot.lua
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
local caveTab = addTab("Cave")
|
||||||
|
|
||||||
|
local waypoints = Panels.Waypoints(caveTab)
|
||||||
|
local attacking = Panels.Attacking(caveTab)
|
||||||
|
local looting = Panels.Looting(caveTab)
|
||||||
|
addButton("tutorial", "Help & Tutorials", function()
|
||||||
|
g_platform.openUrl("https://github.com/OTCv8/otclientv8_bot")
|
||||||
|
end, caveTab)
|
5
modules/game_bot/default_config/example.otui
Normal file
5
modules/game_bot/default_config/example.otui
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
ExampleLabel2 < Label
|
||||||
|
text: LOL
|
||||||
|
height: 200
|
||||||
|
width: 50
|
||||||
|
|
16
modules/game_bot/default_config/hp.lua
Normal file
16
modules/game_bot/default_config/hp.lua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
local healTab = addTab("HP")
|
||||||
|
|
||||||
|
Panels.Haste(healTab)
|
||||||
|
Panels.ManaShield(healTab)
|
||||||
|
Panels.AntiParalyze(healTab)
|
||||||
|
Panels.Health(healTab)
|
||||||
|
Panels.Health(healTab)
|
||||||
|
Panels.HealthItem(healTab)
|
||||||
|
Panels.HealthItem(healTab)
|
||||||
|
Panels.ManaItem(healTab)
|
||||||
|
Panels.ManaItem(healTab)
|
||||||
|
Panels.Equip(healTab)
|
||||||
|
Panels.Equip(healTab)
|
||||||
|
Panels.Equip(healTab)
|
||||||
|
Panels.Eating(healTab)
|
||||||
|
|
16
modules/game_bot/default_config/main.lua
Normal file
16
modules/game_bot/default_config/main.lua
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Panels.TradeMessage()
|
||||||
|
Panels.AutoStackItems()
|
||||||
|
|
||||||
|
addButton("discord", "Discord & Help", function()
|
||||||
|
g_platform.openUrl("https://discord.gg/yhqBE4A")
|
||||||
|
end)
|
||||||
|
|
||||||
|
addButton("forum", "Forum", function()
|
||||||
|
g_platform.openUrl("https://otland.net/forums/otclient.494/")
|
||||||
|
end)
|
||||||
|
|
||||||
|
addButton("github", "Documentation", function()
|
||||||
|
g_platform.openUrl("https://github.com/OTCv8/otclientv8_bot")
|
||||||
|
end)
|
||||||
|
|
||||||
|
addSeparator("sep")
|
7
modules/game_bot/default_config/npc.lua
Normal file
7
modules/game_bot/default_config/npc.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
singlehotkey("f10", "npc buy and sell", function()
|
||||||
|
NPC.say("hi")
|
||||||
|
NPC.say("trade")
|
||||||
|
NPC.buy(3074, 2) -- wand of vortex
|
||||||
|
NPC.sell(3074, 1)
|
||||||
|
NPC.closeTrade()
|
||||||
|
end)
|
84
modules/game_bot/default_config/tools.lua
Normal file
84
modules/game_bot/default_config/tools.lua
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
local toolsTab = addTab("Tools")
|
||||||
|
|
||||||
|
macro(1000, "exchange money", function()
|
||||||
|
local containers = getContainers()
|
||||||
|
for i, container in pairs(containers) do
|
||||||
|
for j, item in ipairs(container:getItems()) do
|
||||||
|
if item:isStackable() and (item:getId() == 3035 or item:getId() == 3031) and item:getCount() == 100 then
|
||||||
|
g_game.use(item)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
macro(1000, "this macro does nothing", "f7", function()
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
macro(100, "debug pathfinding", nil, function()
|
||||||
|
for i, tile in ipairs(g_map.getTiles(posz())) do
|
||||||
|
tile:setText("")
|
||||||
|
end
|
||||||
|
local path = findEveryPath(pos(), 20, {
|
||||||
|
ignoreNonPathable = false
|
||||||
|
})
|
||||||
|
local total = 0
|
||||||
|
for i, p in pairs(path) do
|
||||||
|
local s = i:split(",")
|
||||||
|
local pos = {x=tonumber(s[1]), y=tonumber(s[2]), z=tonumber(s[3])}
|
||||||
|
local tile = g_map.getTile(pos)
|
||||||
|
if tile then
|
||||||
|
tile:setText(p[2])
|
||||||
|
end
|
||||||
|
total = total + 1
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
macro(1000, "speed hack", nil, function()
|
||||||
|
player:setSpeed(1000)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hotkey("f5", "example hotkey", function()
|
||||||
|
info("Wow, you clicked f5 hotkey")
|
||||||
|
end)
|
||||||
|
|
||||||
|
singlehotkey("ctrl+f6", "singlehotkey", function()
|
||||||
|
info("Wow, you clicked f6 singlehotkey")
|
||||||
|
usewith(268, player)
|
||||||
|
end)
|
||||||
|
|
||||||
|
singlehotkey("ctrl+f8", "play alarm", function()
|
||||||
|
playAlarm()
|
||||||
|
end)
|
||||||
|
|
||||||
|
singlehotkey("ctrl+f9", "stop alarm", function()
|
||||||
|
stopSound()
|
||||||
|
end)
|
||||||
|
|
||||||
|
local positionLabel = addLabel("positionLabel", "")
|
||||||
|
onPlayerPositionChange(function()
|
||||||
|
positionLabel:setText("Pos: " .. posx() .. "," .. posy() .. "," .. posz())
|
||||||
|
end)
|
||||||
|
|
||||||
|
local s = addSwitch("sdSound", "Play sound when using sd", function(widget)
|
||||||
|
storage.sdSound = not storage.sdSound
|
||||||
|
widget:setOn(storage.sdSound)
|
||||||
|
end)
|
||||||
|
s:setOn(storage.sdSound)
|
||||||
|
|
||||||
|
onUseWith(function(pos, itemId)
|
||||||
|
if storage.sdSound and itemId == 3155 then
|
||||||
|
playSound("/sounds/magnum.ogg")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
macro(100, "hide useless tiles", "", function()
|
||||||
|
for i, tile in ipairs(g_map.getTiles(posz())) do
|
||||||
|
if not tile:isWalkable(true) then
|
||||||
|
tile:setFill('black')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
addLabel("mapinfo", "You can use ctrl + plus and ctrl + minus to zoom in / zoom out map")
|
121
modules/game_bot/edit.otui
Normal file
121
modules/game_bot/edit.otui
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
MainWindow
|
||||||
|
id: editWindow
|
||||||
|
size: 550 580
|
||||||
|
!text: tr("Config editor")
|
||||||
|
@onEscape: self:hide()
|
||||||
|
@onEnter: self:hide()
|
||||||
|
|
||||||
|
Label
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
!text: tr("Bot configs are stored in:")
|
||||||
|
|
||||||
|
TextEdit
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
height: 20
|
||||||
|
width: 400
|
||||||
|
margin-top: 5
|
||||||
|
editable: false
|
||||||
|
!text: g_resources.getWriteDir() .. "bot"
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: documentationButton
|
||||||
|
!text: tr('Click here to open bot directory')
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 5
|
||||||
|
width: 250
|
||||||
|
@onClick: g_platform.openDir(g_resources.getWriteDir() .. "bot")
|
||||||
|
|
||||||
|
Label
|
||||||
|
margin-top: 5
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
!text: tr("Every directory in bot directory is treated as different config.\nTo create new config just create new directory.")
|
||||||
|
|
||||||
|
Label
|
||||||
|
margin-top: 5
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 175
|
||||||
|
image-source: configs.png
|
||||||
|
image-fixed-ratio: true
|
||||||
|
image-size: 500 175
|
||||||
|
|
||||||
|
Label
|
||||||
|
margin-top: 5
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
!text: tr("Inside config directory put .lua and .otui files.\nEvery file will be loaded and executed in alphabetical order, .otui first and then .lua.")
|
||||||
|
|
||||||
|
Label
|
||||||
|
margin-top: 5
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: 150
|
||||||
|
image-source: scripts.png
|
||||||
|
image-fixed-ratio: true
|
||||||
|
image-size: 500 150
|
||||||
|
|
||||||
|
Label
|
||||||
|
margin-top: 5
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
!text: tr("To reload configs just press On and Off in bot window.\nTo learn more about bot click Tutorials button.")
|
||||||
|
|
||||||
|
Button
|
||||||
|
!text: tr('Tutorials')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: 100
|
||||||
|
@onClick: g_platform.openUrl("http://otclient.ovh/bot.php?tutorials")
|
||||||
|
|
||||||
|
Button
|
||||||
|
!text: tr('Scripts')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: prev.right
|
||||||
|
margin-left: 10
|
||||||
|
width: 100
|
||||||
|
@onClick: g_platform.openUrl("http://otclient.ovh/bot.php?scripts")
|
||||||
|
|
||||||
|
Button
|
||||||
|
!text: tr('Forum')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: prev.right
|
||||||
|
margin-left: 10
|
||||||
|
width: 100
|
||||||
|
@onClick: g_platform.openUrl("http://otclient.ovh/bot.php?forum")
|
||||||
|
|
||||||
|
Button
|
||||||
|
!text: tr('Discord')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: prev.right
|
||||||
|
margin-left: 10
|
||||||
|
width: 100
|
||||||
|
@onClick: g_platform.openUrl("http://otclient.ovh/bot.php?discord")
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: cancelButton
|
||||||
|
!text: tr('Close')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
width: 60
|
||||||
|
@onClick: self:getParent():hide()
|
@ -1,7 +1,27 @@
|
|||||||
function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, websockets)
|
function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, websockets)
|
||||||
|
-- load lua and otui files
|
||||||
|
local configFiles = g_resources.listDirectoryFiles("/bot/" .. config, true, false)
|
||||||
|
local luaFiles = {}
|
||||||
|
local uiFiles = {}
|
||||||
|
for i, file in ipairs(configFiles) do
|
||||||
|
local ext = file:split(".")
|
||||||
|
if ext[#ext]:lower() == "lua" then
|
||||||
|
table.insert(luaFiles, file)
|
||||||
|
end
|
||||||
|
if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then
|
||||||
|
table.insert(uiFiles, file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #luaFiles == 0 then
|
||||||
|
return error("Config (/bot/" .. config .. ") doesn't have lua files")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- init bot variables
|
||||||
local context = {}
|
local context = {}
|
||||||
|
context.configDir = "/bot/".. config
|
||||||
context.tabs = tabs
|
context.tabs = tabs
|
||||||
context.panel = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel
|
context.panel = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel.content
|
||||||
context.saveConfig = saveConfigCallback
|
context.saveConfig = saveConfigCallback
|
||||||
context._websockets = websockets
|
context._websockets = websockets
|
||||||
|
|
||||||
@ -10,7 +30,7 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, webs
|
|||||||
context.storage._macros = {} -- active macros
|
context.storage._macros = {} -- active macros
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
-- macros, hotkeys, scheduler, callbacks
|
||||||
context._macros = {}
|
context._macros = {}
|
||||||
context._hotkeys = {}
|
context._hotkeys = {}
|
||||||
context._scheduler = {}
|
context._scheduler = {}
|
||||||
@ -87,8 +107,15 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, webs
|
|||||||
dofiles("panels")
|
dofiles("panels")
|
||||||
G.botContext = nil
|
G.botContext = nil
|
||||||
|
|
||||||
-- run script
|
-- run ui scripts
|
||||||
assert(load(config, nil, nil, context))()
|
for i, file in ipairs(uiFiles) do
|
||||||
|
g_ui.importStyle(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- run lua script
|
||||||
|
for i, file in ipairs(luaFiles) do
|
||||||
|
assert(load(g_resources.readFileContents(file), file, nil, context))()
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
script = function()
|
script = function()
|
||||||
|
113
modules/game_bot/functions/config.lua
Normal file
113
modules/game_bot/functions/config.lua
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
--[[
|
||||||
|
Config. create. load and save config file (.json)
|
||||||
|
Used by cavebot and other things
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local context = G.botContext
|
||||||
|
context.Config = {}
|
||||||
|
local Config = context.Config
|
||||||
|
|
||||||
|
Config.exist = function(dir)
|
||||||
|
return g_resources.directoryExists(context.configDir .. "/" .. dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.create = function(dir)
|
||||||
|
g_resources.makeDir(context.configDir .. "/" .. dir)
|
||||||
|
return Config.exist(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.list = function(dir)
|
||||||
|
if not Config.exist(dir) then
|
||||||
|
if not Config.create(dir) then
|
||||||
|
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return g_resources.listDirectoryFiles(context.configDir .. "/" .. dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.load = function(dir, name)
|
||||||
|
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
|
||||||
|
if g_resources.fileExists(file) then -- load json
|
||||||
|
return json.decode(g_resources.readFileContents(file))
|
||||||
|
end
|
||||||
|
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
|
||||||
|
if g_resources.fileExists(file) then -- load cfg
|
||||||
|
return g_resources.readFileContents(file)
|
||||||
|
end
|
||||||
|
return context.error("Config " .. file .. " doesn't exist")
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.save = function(dir, name, value)
|
||||||
|
if not Config.exist(dir) then
|
||||||
|
if not Config.create(dir) then
|
||||||
|
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local file = context.configDir .. "/" .. dir .. "/" .. name
|
||||||
|
if type(value) == 'string' then -- cfg
|
||||||
|
g_resources.writeFileContents(file .. ".cfg", value)
|
||||||
|
elseif type(value) == 'table' then -- json
|
||||||
|
g_resources.writeFileContents(file .. ".json", json.encode(value))
|
||||||
|
end
|
||||||
|
return context.error("Invalid config value type: " .. type(value))
|
||||||
|
end
|
||||||
|
|
||||||
|
Config.remove = function(dir, name)
|
||||||
|
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
|
||||||
|
if g_resources.fileExists(file) then
|
||||||
|
return g_resources.deleteFile(file)
|
||||||
|
end
|
||||||
|
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
|
||||||
|
if g_resources.fileExists(file) then
|
||||||
|
return g_resources.deleteFile(file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- setup is used for BotConfig widget
|
||||||
|
-- not done yet
|
||||||
|
Config.setup = function(dir, widget, callback)
|
||||||
|
local refresh = function()
|
||||||
|
--
|
||||||
|
end
|
||||||
|
|
||||||
|
widget.switch.onClick = function()
|
||||||
|
widget.switch:setOn(not widget.switch:isOn())
|
||||||
|
end
|
||||||
|
|
||||||
|
widget.add = function()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
widget.edit = function()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
widget.remove = function()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--local configs = Config.list(dir)
|
||||||
|
--widget.list.
|
||||||
|
|
||||||
|
return {
|
||||||
|
isOn = function()
|
||||||
|
return widget.switch:isOn()
|
||||||
|
end,
|
||||||
|
isOff = function()
|
||||||
|
return not widget.switch:isOn()
|
||||||
|
end,
|
||||||
|
enable = function()
|
||||||
|
if not widget.switch:isOn() then
|
||||||
|
widget.switch:onClick()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
disable = function()
|
||||||
|
if widget.switch:isOn() then
|
||||||
|
widget.switch:onClick()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
save = function()
|
||||||
|
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
111
modules/game_bot/functions/npc.lua
Normal file
111
modules/game_bot/functions/npc.lua
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
local context = G.botContext
|
||||||
|
|
||||||
|
context.NPC = {}
|
||||||
|
|
||||||
|
context.NPC.talk = function(text)
|
||||||
|
if g_game.getClientVersion() >= 810 then
|
||||||
|
g_game.talkChannel(11, 0, text)
|
||||||
|
else
|
||||||
|
return context.say(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
context.NPC.say = context.NPC.talk
|
||||||
|
|
||||||
|
context.NPC.isTrading = function()
|
||||||
|
return modules.game_npctrade.npcWindow and modules.game_npctrade.npcWindow:isVisible()
|
||||||
|
end
|
||||||
|
context.NPC.hasTrade = context.NPC.isTrading
|
||||||
|
context.NPC.hasTradeWindow = context.NPC.isTrading
|
||||||
|
|
||||||
|
|
||||||
|
context.NPC.getSellItems = function()
|
||||||
|
if not context.NPC.isTrading() then return {} end
|
||||||
|
local items = {}
|
||||||
|
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.SELL]) do
|
||||||
|
table.insert(items, {
|
||||||
|
id = item.ptr:getId(),
|
||||||
|
name = item.name,
|
||||||
|
count = item.ptr:getCount(),
|
||||||
|
subType = item.ptr:getSubType(),
|
||||||
|
weight = item.weight / 100,
|
||||||
|
price = item.price
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.getBuyItems = function()
|
||||||
|
if not context.NPC.isTrading() then return {} end
|
||||||
|
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.BUY]) do
|
||||||
|
table.insert(items, {
|
||||||
|
id = item.ptr:getId(),
|
||||||
|
name = item.name,
|
||||||
|
count = item.ptr:getCount(),
|
||||||
|
subType = item.ptr:getSubType(),
|
||||||
|
weight = item.weight / 100,
|
||||||
|
price = item.price
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.getSellQuantity = function(item)
|
||||||
|
if not context.NPC.isTrading() then return 0 end
|
||||||
|
if type(item) == 'number' then
|
||||||
|
item = Item.create(item)
|
||||||
|
end
|
||||||
|
return modules.game_npctrade.getSellQuantity(item)
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.canTradeItem = function(item)
|
||||||
|
if not context.NPC.isTrading() then return false end
|
||||||
|
if type(item) == 'number' then
|
||||||
|
item = Item.create(item)
|
||||||
|
end
|
||||||
|
return modules.game_npctrade.canTradeItem(item)
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.sell = function(item, count, ignoreEquipped)
|
||||||
|
if type(item) == 'number' then
|
||||||
|
item = Item.create(item)
|
||||||
|
end
|
||||||
|
if count == 0 then
|
||||||
|
count = 1
|
||||||
|
end
|
||||||
|
if count == nil or count == -1 then
|
||||||
|
count = context.NPC.getSellQuantity(item)
|
||||||
|
end
|
||||||
|
if ignoreEquipped == nil then
|
||||||
|
ignoreEquipped = true
|
||||||
|
end
|
||||||
|
g_game.sellItem(item, count, ignoreEquipped)
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.buy = function(item, count, ignoreCapacity, withBackpack)
|
||||||
|
if type(item) == 'number' then
|
||||||
|
item = Item.create(item)
|
||||||
|
end
|
||||||
|
if count == nil or count <= 0 then
|
||||||
|
count = 1
|
||||||
|
end
|
||||||
|
if ignoreCapacity == nil then
|
||||||
|
ignoreCapacity = false
|
||||||
|
end
|
||||||
|
if withBackpack == nil then
|
||||||
|
withBackpack = false
|
||||||
|
end
|
||||||
|
g_game.buyItem(item, count, ignoreCapacity, withBackpack)
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.sellAll = function()
|
||||||
|
if not context.NPC.isTrading() then return false end
|
||||||
|
modules.game_npctrade.sellAll()
|
||||||
|
end
|
||||||
|
|
||||||
|
context.NPC.closeTrade = function()
|
||||||
|
modules.game_npctrade.closeNpcTrade()
|
||||||
|
end
|
||||||
|
context.NPC.close = context.NPC.closeTrade
|
||||||
|
context.NPC.finish = context.NPC.closeTrade
|
||||||
|
context.NPC.endTrade = context.NPC.closeTrade
|
||||||
|
context.NPC.finishTrade = context.NPC.closeTrade
|
@ -11,7 +11,7 @@ end
|
|||||||
|
|
||||||
context.addTab = function(name)
|
context.addTab = function(name)
|
||||||
context.tabs:setOn(true)
|
context.tabs:setOn(true)
|
||||||
return context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel
|
return context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel.content
|
||||||
end
|
end
|
||||||
|
|
||||||
context.addSwitch = function(id, text, onClickCallback, parent)
|
context.addSwitch = function(id, text, onClickCallback, parent)
|
||||||
|
BIN
modules/game_bot/scripts.png
Normal file
BIN
modules/game_bot/scripts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -39,8 +39,80 @@ BotSeparator < HorizontalSeparator
|
|||||||
margin-bottom: 3
|
margin-bottom: 3
|
||||||
|
|
||||||
BotPanel < Panel
|
BotPanel < Panel
|
||||||
layout:
|
ScrollablePanel
|
||||||
type: verticalBox
|
id: content
|
||||||
|
anchors.fill: parent
|
||||||
|
margin-right: 8
|
||||||
|
margin-left: 1
|
||||||
|
margin-bottom: 5
|
||||||
|
vertical-scrollbar: botPanelScroll
|
||||||
|
layout:
|
||||||
|
type: verticalBox
|
||||||
|
|
||||||
|
UIScrollBar
|
||||||
|
id: botPanelScroll
|
||||||
|
orientation: vertical
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-bottom: 1
|
||||||
|
step: 20
|
||||||
|
width: 8
|
||||||
|
image-source: /images/ui/scrollbar
|
||||||
|
image-clip: 39 0 13 65
|
||||||
|
image-border: 1
|
||||||
|
pixels-scroll: true
|
||||||
|
|
||||||
|
UIButton
|
||||||
|
id: decrementButton
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
image-source: /images/ui/scrollbar
|
||||||
|
image-clip: 0 0 13 13
|
||||||
|
image-color: #ffffffff
|
||||||
|
size: 8 8
|
||||||
|
$hover:
|
||||||
|
image-clip: 13 0 13 13
|
||||||
|
$pressed:
|
||||||
|
image-clip: 26 0 13 13
|
||||||
|
$disabled:
|
||||||
|
image-color: #ffffff66
|
||||||
|
|
||||||
|
UIButton
|
||||||
|
id: incrementButton
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
size: 8 8
|
||||||
|
image-source: /images/ui/scrollbar
|
||||||
|
image-clip: 0 13 13 13
|
||||||
|
image-color: #ffffffff
|
||||||
|
$hover:
|
||||||
|
image-clip: 13 13 13 13
|
||||||
|
$pressed:
|
||||||
|
image-clip: 26 13 13 13
|
||||||
|
$disabled:
|
||||||
|
image-color: #ffffff66
|
||||||
|
|
||||||
|
UIButton
|
||||||
|
id: sliderButton
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: 8 11
|
||||||
|
image-source: /images/ui/scrollbar
|
||||||
|
image-clip: 0 26 13 13
|
||||||
|
image-border: 2
|
||||||
|
image-color: #ffffffff
|
||||||
|
$hover:
|
||||||
|
image-clip: 13 26 13 13
|
||||||
|
$pressed:
|
||||||
|
image-clip: 26 26 13 13
|
||||||
|
$disabled:
|
||||||
|
image-color: #ffffff66
|
||||||
|
|
||||||
|
Label
|
||||||
|
id: valueLabel
|
||||||
|
anchors.fill: parent
|
||||||
|
color: white
|
||||||
|
text-align: center
|
||||||
|
|
||||||
CaveBotLabel < Label
|
CaveBotLabel < Label
|
||||||
background-color: alpha
|
background-color: alpha
|
||||||
|
51
modules/game_bot/ui/config.otui
Normal file
51
modules/game_bot/ui/config.otui
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
BotConfig < Panel
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
ComboBox
|
||||||
|
id: list
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
text-offset: 3 0
|
||||||
|
width: 130
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: switch
|
||||||
|
anchors.top: prev.top
|
||||||
|
anchors.left: prev.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-left: 5
|
||||||
|
$on:
|
||||||
|
text: On
|
||||||
|
color: #00AA00
|
||||||
|
|
||||||
|
$!on:
|
||||||
|
text: Off
|
||||||
|
color: #FF0000
|
||||||
|
|
||||||
|
Button
|
||||||
|
margin-top: 1
|
||||||
|
id: add
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: Add
|
||||||
|
width: 58
|
||||||
|
height: 17
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: edit
|
||||||
|
anchors.top: prev.top
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: Edit
|
||||||
|
width: 58
|
||||||
|
height: 17
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: remove
|
||||||
|
anchors.top: prev.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: Remove
|
||||||
|
width: 58
|
||||||
|
height: 17
|
@ -112,7 +112,6 @@ ItemsRow < Panel
|
|||||||
id: item1
|
id: item1
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
margin-left: 3
|
|
||||||
|
|
||||||
BotItem
|
BotItem
|
||||||
id: item2
|
id: item2
|
||||||
|
@ -898,6 +898,7 @@ end
|
|||||||
function sendCurrentMessage()
|
function sendCurrentMessage()
|
||||||
local message = consoleTextEdit:getText()
|
local message = consoleTextEdit:getText()
|
||||||
if #message == 0 then return end
|
if #message == 0 then return end
|
||||||
|
if not isChatEnabled() then return end
|
||||||
consoleTextEdit:clearText()
|
consoleTextEdit:clearText()
|
||||||
|
|
||||||
-- send message
|
-- send message
|
||||||
@ -1071,6 +1072,10 @@ function setIgnoreNpcMessages(ignore)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function navigateMessageHistory(step)
|
function navigateMessageHistory(step)
|
||||||
|
if not isChatEnabled() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local numCommands = #messageHistory
|
local numCommands = #messageHistory
|
||||||
if numCommands > 0 then
|
if numCommands > 0 then
|
||||||
currentMessageIndex = math.min(math.max(currentMessageIndex + step, 0), numCommands)
|
currentMessageIndex = math.min(math.max(currentMessageIndex + step, 0), numCommands)
|
||||||
|
@ -95,6 +95,9 @@ function onContainerOpen(container, previousContainer)
|
|||||||
containerWindow:hide()
|
containerWindow:hide()
|
||||||
end
|
end
|
||||||
containerWindow.onDrop = function(container, widget, mousePos)
|
containerWindow.onDrop = function(container, widget, mousePos)
|
||||||
|
if containerPanel:getChildByPos(mousePos) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
local child = containerPanel:getChildByIndex(-1)
|
local child = containerPanel:getChildByIndex(-1)
|
||||||
if child then
|
if child then
|
||||||
child:onDrop(widget, mousePos, true)
|
child:onDrop(widget, mousePos, true)
|
||||||
|
@ -285,10 +285,14 @@ function onUseWith(clickedWidget, mousePosition)
|
|||||||
if clickedWidget:getClassName() == 'UIGameMap' then
|
if clickedWidget:getClassName() == 'UIGameMap' then
|
||||||
local tile = clickedWidget:getTile(mousePosition)
|
local tile = clickedWidget:getTile(mousePosition)
|
||||||
if tile then
|
if tile then
|
||||||
if selectedThing:isFluidContainer() or selectedThing:isMultiUse() then
|
if selectedThing:isFluidContainer() or selectedThing:isMultiUse() then
|
||||||
g_game.useWith(selectedThing, tile:getTopMultiUseThing(), selectedSubtype)
|
if selectedThing:getId() == 3180 or selectedThing:getId() == 3156 then
|
||||||
|
-- special version for mwall
|
||||||
|
g_game.useWith(selectedThing, tile:getTopUseThing(), selectedSubtype)
|
||||||
|
else
|
||||||
|
g_game.useWith(selectedThing, tile:getTopMultiUseThingEx(clickedWidget:getPositionOffset(mousePosition)), selectedSubtype)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
print("normal")
|
|
||||||
g_game.useWith(selectedThing, tile:getTopUseThing(), selectedSubtype)
|
g_game.useWith(selectedThing, tile:getTopUseThing(), selectedSubtype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -13,6 +13,7 @@ searchText = nil
|
|||||||
setupPanel = nil
|
setupPanel = nil
|
||||||
quantity = nil
|
quantity = nil
|
||||||
quantityScroll = nil
|
quantityScroll = nil
|
||||||
|
idLabel = nil
|
||||||
nameLabel = nil
|
nameLabel = nil
|
||||||
priceLabel = nil
|
priceLabel = nil
|
||||||
moneyLabel = nil
|
moneyLabel = nil
|
||||||
@ -49,6 +50,7 @@ function init()
|
|||||||
|
|
||||||
setupPanel = npcWindow:recursiveGetChildById('setupPanel')
|
setupPanel = npcWindow:recursiveGetChildById('setupPanel')
|
||||||
quantityScroll = setupPanel:getChildById('quantityScroll')
|
quantityScroll = setupPanel:getChildById('quantityScroll')
|
||||||
|
idLabel = setupPanel:getChildById('id')
|
||||||
nameLabel = setupPanel:getChildById('name')
|
nameLabel = setupPanel:getChildById('name')
|
||||||
priceLabel = setupPanel:getChildById('price')
|
priceLabel = setupPanel:getChildById('price')
|
||||||
moneyLabel = setupPanel:getChildById('money')
|
moneyLabel = setupPanel:getChildById('money')
|
||||||
@ -119,6 +121,23 @@ end
|
|||||||
|
|
||||||
function hide()
|
function hide()
|
||||||
npcWindow:hide()
|
npcWindow:hide()
|
||||||
|
|
||||||
|
local layout = itemsPanel:getLayout()
|
||||||
|
layout:disableUpdates()
|
||||||
|
|
||||||
|
clearSelectedItem()
|
||||||
|
|
||||||
|
searchText:clearText()
|
||||||
|
setupPanel:disable()
|
||||||
|
itemsPanel:destroyChildren()
|
||||||
|
|
||||||
|
if radioItems then
|
||||||
|
radioItems:destroy()
|
||||||
|
radioItems = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
layout:enableUpdates()
|
||||||
|
layout:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function onItemBoxChecked(widget)
|
function onItemBoxChecked(widget)
|
||||||
@ -226,6 +245,7 @@ function setShowYourCapacity(state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function clearSelectedItem()
|
function clearSelectedItem()
|
||||||
|
idLabel:clearText()
|
||||||
nameLabel:clearText()
|
nameLabel:clearText()
|
||||||
weightLabel:clearText()
|
weightLabel:clearText()
|
||||||
priceLabel:clearText()
|
priceLabel:clearText()
|
||||||
@ -288,6 +308,7 @@ function canTradeItem(item)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function refreshItem(item)
|
function refreshItem(item)
|
||||||
|
idLabel:setText(item.ptr:getId())
|
||||||
nameLabel:setText(item.name)
|
nameLabel:setText(item.name)
|
||||||
weightLabel:setText(string.format('%.2f', item.weight) .. ' ' .. WEIGHT_UNIT)
|
weightLabel:setText(string.format('%.2f', item.weight) .. ' ' .. WEIGHT_UNIT)
|
||||||
priceLabel:setText(formatCurrency(getItemPrice(item)))
|
priceLabel:setText(formatCurrency(getItemPrice(item)))
|
||||||
@ -420,11 +441,11 @@ end
|
|||||||
|
|
||||||
function closeNpcTrade()
|
function closeNpcTrade()
|
||||||
g_game.closeNpcTrade()
|
g_game.closeNpcTrade()
|
||||||
hide()
|
addEvent(hide)
|
||||||
end
|
end
|
||||||
|
|
||||||
function onCloseNpcTrade()
|
function onCloseNpcTrade()
|
||||||
hide()
|
addEvent(hide)
|
||||||
end
|
end
|
||||||
|
|
||||||
function onPlayerGoods(money, items)
|
function onPlayerGoods(money, items)
|
||||||
|
@ -101,13 +101,24 @@ MainWindow
|
|||||||
image-color: #ffffff88
|
image-color: #ffffff88
|
||||||
|
|
||||||
Label
|
Label
|
||||||
!text: tr('Name') .. ':'
|
!text: tr('Id') .. ':'
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
margin-top: 5
|
margin-top: 5
|
||||||
margin-left: 5
|
margin-left: 5
|
||||||
width: 85
|
width: 85
|
||||||
|
|
||||||
|
NPCOfferLabel
|
||||||
|
id: id
|
||||||
|
|
||||||
|
Label
|
||||||
|
!text: tr('Name') .. ':'
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 5
|
||||||
|
margin-left: 5
|
||||||
|
width: 85
|
||||||
|
|
||||||
NPCOfferLabel
|
NPCOfferLabel
|
||||||
id: name
|
id: name
|
||||||
|
|
||||||
|
@ -243,9 +243,12 @@ function sortBy(state)
|
|||||||
refresh()
|
refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
function onAddVip(id, name, state, description, iconId, notify)
|
function onAddVip(id, name, state, description, iconId, notify)
|
||||||
local vipList = vipWindow:getChildById('contentsPanel')
|
if not name or name:len() == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local vipList = vipWindow:getChildById('contentsPanel')
|
||||||
local childrenCount = vipList:getChildCount()
|
local childrenCount = vipList:getChildCount()
|
||||||
for i=1,childrenCount do
|
for i=1,childrenCount do
|
||||||
local child = vipList:getChildByIndex(i)
|
local child = vipList:getChildByIndex(i)
|
||||||
|
@ -383,7 +383,7 @@ function turn(dir, repeated)
|
|||||||
end
|
end
|
||||||
lastTurnDirection = dir
|
lastTurnDirection = dir
|
||||||
nextWalkDir = nil
|
nextWalkDir = nil
|
||||||
player:lockWalk(150)
|
player:lockWalk(g_settings.getNumber('walkCtrlTurnDelay'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
BIN
otclient_dx.exe
BIN
otclient_dx.exe
Binary file not shown.
BIN
otclient_gl.exe
BIN
otclient_gl.exe
Binary file not shown.
BIN
otclient_linux
BIN
otclient_linux
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user