mirror of
https://github.com/OTCv8/otclientv8.git
synced 2026-01-21 01:46:22 +01:00
Version 0.993 BETA (added bot)
This commit is contained in:
@@ -12,6 +12,7 @@ $serverIp = "otclient.ovh";
|
|||||||
$serverPort = 7172; // GAME PORT (7172 usually)
|
$serverPort = 7172; // GAME PORT (7172 usually)
|
||||||
|
|
||||||
$version = 1099;
|
$version = 1099;
|
||||||
|
$otc_version = 1337; // APP_VERSION, from init.lua
|
||||||
|
|
||||||
$maxLogins = 10; // 0 or null to disable
|
$maxLogins = 10; // 0 or null to disable
|
||||||
$blockTime = 60; // after too many logins, in seconds
|
$blockTime = 60; // after too many logins, in seconds
|
||||||
@@ -98,6 +99,10 @@ if(empty($data)) {
|
|||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($data->version != $otc_version) {
|
||||||
|
die(json_encode(array("error" => "Outdated client, please update!")));
|
||||||
|
}
|
||||||
|
|
||||||
if($data->quick == 1) {
|
if($data->quick == 1) {
|
||||||
// under development
|
// under development
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
|
|||||||
370
modules/game_bot/bot.lua
Normal file
370
modules/game_bot/bot.lua
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
botWindow = nil
|
||||||
|
botButton = nil
|
||||||
|
botConfigFile = nil
|
||||||
|
botConfig = nil
|
||||||
|
contentsPanel = nil
|
||||||
|
configWindow = nil
|
||||||
|
configEditorText = nil
|
||||||
|
configList = nil
|
||||||
|
botPanel = nil
|
||||||
|
local botMessages = nil
|
||||||
|
local showingDocumentation = false
|
||||||
|
local configCopy = ""
|
||||||
|
local enableButton = nil
|
||||||
|
local executeEvent = nil
|
||||||
|
local checkMsgsEvent = nil
|
||||||
|
local errorOccured = false
|
||||||
|
local statusLabel = nil
|
||||||
|
local compiledConfig = nil
|
||||||
|
local configTab = nil
|
||||||
|
local tabs = {"macros", "hotkeys", "callbacks", "other"}
|
||||||
|
local mainTab = nil
|
||||||
|
local activeTab = nil
|
||||||
|
local editorText = {"", ""}
|
||||||
|
|
||||||
|
function init()
|
||||||
|
dofile("defaultconfig")
|
||||||
|
dofile("executor")
|
||||||
|
|
||||||
|
connect(g_game, { onGameStart = online, onGameEnd = offline, onTalk = botOnTalk})
|
||||||
|
|
||||||
|
connect(rootWidget, { onKeyDown = botKeyDown,
|
||||||
|
onKeyUp = botKeyUp,
|
||||||
|
onKeyPress = botKeyPress })
|
||||||
|
|
||||||
|
botConfigFile = g_configs.create("/bot.otml")
|
||||||
|
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
|
||||||
|
|
||||||
|
botButton = modules.client_topmenu.addRightGameToggleButton('botButton',
|
||||||
|
tr('Bot'), '/images/topbuttons/bot', toggle)
|
||||||
|
botButton:setOn(false)
|
||||||
|
botButton:hide()
|
||||||
|
|
||||||
|
botWindow = g_ui.loadUI('bot', modules.game_interface.getRightPanel())
|
||||||
|
botWindow:setup()
|
||||||
|
|
||||||
|
contentsPanel = botWindow:getChildById('contentsPanel')
|
||||||
|
configList = contentsPanel:getChildById('config')
|
||||||
|
enableButton = contentsPanel:getChildById('enableButton')
|
||||||
|
statusLabel = contentsPanel:getChildById('statusLabel')
|
||||||
|
botMessages = contentsPanel:getChildById('messages')
|
||||||
|
botPanel = contentsPanel:getChildById('botPanel')
|
||||||
|
|
||||||
|
configWindow = g_ui.displayUI('config')
|
||||||
|
configWindow:hide()
|
||||||
|
|
||||||
|
configEditorText = configWindow:getChildById('text')
|
||||||
|
configTab = configWindow:getChildById('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
|
||||||
|
|
||||||
|
if g_game.isOnline() then
|
||||||
|
online()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function saveConfig()
|
||||||
|
botConfigFile:set("config", json.encode(botConfig))
|
||||||
|
botConfigFile:save()
|
||||||
|
end
|
||||||
|
|
||||||
|
function terminate()
|
||||||
|
saveConfig()
|
||||||
|
clearConfig()
|
||||||
|
|
||||||
|
disconnect(rootWidget, { onKeyDown = botKeyDown,
|
||||||
|
onKeyUp = botKeyUp,
|
||||||
|
onKeyPress = botKeyPress })
|
||||||
|
|
||||||
|
disconnect(g_game, { onGameStart = online, onGameEnd = offline, onTalk = botOnTalk})
|
||||||
|
|
||||||
|
removeEvent(executeEvent)
|
||||||
|
removeEvent(checkMsgsEvent)
|
||||||
|
|
||||||
|
botWindow:destroy()
|
||||||
|
botButton:destroy()
|
||||||
|
configWindow:destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
function onMiniWindowClose()
|
||||||
|
botButton:setOn(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function toggle()
|
||||||
|
if botButton:isOn() then
|
||||||
|
botWindow:close()
|
||||||
|
botButton:setOn(false)
|
||||||
|
else
|
||||||
|
botWindow:open()
|
||||||
|
botButton:setOn(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function online()
|
||||||
|
botButton:show()
|
||||||
|
updateEnabled()
|
||||||
|
if botConfig.enabled then
|
||||||
|
refreshConfig()
|
||||||
|
else
|
||||||
|
clearConfig()
|
||||||
|
end
|
||||||
|
if executeEvent == nil then
|
||||||
|
executeEvent = scheduleEvent(executeConfig, 200)
|
||||||
|
checkMsgsEvent = scheduleEvent(checkMsgs, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function toggleBot()
|
||||||
|
botConfig.enabled = not botConfig.enabled
|
||||||
|
if botConfig.enabled then
|
||||||
|
refreshConfig()
|
||||||
|
else
|
||||||
|
clearConfig()
|
||||||
|
end
|
||||||
|
updateEnabled()
|
||||||
|
end
|
||||||
|
|
||||||
|
function updateEnabled()
|
||||||
|
if botConfig.enabled then
|
||||||
|
enableButton:setText(tr('On'))
|
||||||
|
enableButton:setColor('#00AA00FF')
|
||||||
|
else
|
||||||
|
enableButton:setText(tr('Off'))
|
||||||
|
enableButton:setColor('#FF0000FF')
|
||||||
|
statusLabel:setText(tr("Status: disabled"))
|
||||||
|
end
|
||||||
|
errorOccured = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function editConfig()
|
||||||
|
local config = configList.currentIndex
|
||||||
|
configWindow:show()
|
||||||
|
configWindow:raise()
|
||||||
|
configWindow:focus()
|
||||||
|
editorText = {botConfig.configs[config].script or "", ""}
|
||||||
|
configEditorText:setText(botConfig.configs[config].script)
|
||||||
|
configEditorText:setEditable(true)
|
||||||
|
activeTab = mainTab
|
||||||
|
configTab:selectTab(mainTab)
|
||||||
|
showingDocumentation = false
|
||||||
|
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
|
||||||
|
editorText = {editorText[1] .. "--#" .. activeTab:getText():lower() .. "\n" .. configEditorText:getText() .. 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
|
||||||
|
botPanel:destroyChildren()
|
||||||
|
botMessages:destroyChildren()
|
||||||
|
botMessages:updateLayout()
|
||||||
|
end
|
||||||
|
|
||||||
|
function refreshConfig()
|
||||||
|
clearConfig()
|
||||||
|
configWindow:hide()
|
||||||
|
|
||||||
|
botConfig.selectedConfig = configList.currentIndex
|
||||||
|
if not botConfig.enabled then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
saveConfig()
|
||||||
|
|
||||||
|
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
|
||||||
|
local status, result = pcall(function() return executeBot(config.script, config.storage, botPanel, botMsgCallback) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. tostring(result)))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
compiledConfig = result
|
||||||
|
statusLabel:setText(tr("Status: working"))
|
||||||
|
end
|
||||||
|
|
||||||
|
function executeConfig()
|
||||||
|
executeEvent = scheduleEvent(executeConfig, 20)
|
||||||
|
if compiledConfig == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not botConfig.enabled or errorOccured then
|
||||||
|
if not errorOccured then
|
||||||
|
statusLabel:setText(tr("Status: disabled"))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local status, result = pcall(function() return compiledConfig.script() end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function botMsgCallback(category, msg)
|
||||||
|
local widget = g_ui.createWidget('BotLabel', botMessages)
|
||||||
|
widget.added = g_clock.millis()
|
||||||
|
if category == 'error' then
|
||||||
|
widget:setText(msg)
|
||||||
|
widget:setColor("red")
|
||||||
|
elseif category == 'warn' then
|
||||||
|
widget:setText(msg)
|
||||||
|
widget:setColor("yellow")
|
||||||
|
elseif category == 'info' then
|
||||||
|
widget:setText(msg)
|
||||||
|
widget:setColor("white")
|
||||||
|
end
|
||||||
|
|
||||||
|
if botMessages:getChildCount() > 5 then
|
||||||
|
botMessages:getFirstChild():destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function checkMsgs()
|
||||||
|
checkMsgsEvent = scheduleEvent(checkMsgs, 200)
|
||||||
|
local widget = botMessages:getFirstChild()
|
||||||
|
if widget and widget.added + 5000 < g_clock.millis() then
|
||||||
|
widget:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function botKeyDown(widget, keyCode, keyboardModifiers)
|
||||||
|
if keyCode == KeyUnknown or compiledConfig == nil then return false end
|
||||||
|
local status, result = pcall(function() compiledConfig.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function botKeyUp(widget, keyCode, keyboardModifiers)
|
||||||
|
if keyCode == KeyUnknown or compiledConfig == nil then return false end
|
||||||
|
local status, result = pcall(function() compiledConfig.callbacks.onKeyUp(keyCode, keyboardModifiers) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function botKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
|
||||||
|
if keyCode == KeyUnknown or compiledConfig == nil then return false end
|
||||||
|
local status, result = pcall(function() compiledConfig.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function botOnTalk(name, level, mode, text, channelId, pos)
|
||||||
|
if compiledConfig == nil then return false end
|
||||||
|
local status, result = pcall(function() compiledConfig.callbacks.onTalk(name, level, mode, text, channelId, pos) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function botOnGet(oprationId, url, err, data)
|
||||||
|
if compiledConfig == nil then return false end
|
||||||
|
local status, result = pcall(function() compiledConfig.callbacks.onGet(oprationId, url, err, data) end)
|
||||||
|
if not status then
|
||||||
|
errorOccured = true
|
||||||
|
statusLabel:setText(tr("Error: " .. result))
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
8
modules/game_bot/bot.otmod
Normal file
8
modules/game_bot/bot.otmod
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Module
|
||||||
|
name: game_bot
|
||||||
|
description: Bot
|
||||||
|
author: otclient@otclient.ovh
|
||||||
|
sandboxed: true
|
||||||
|
scripts: [ bot ]
|
||||||
|
@onLoad: init()
|
||||||
|
@onUnload: terminate()
|
||||||
104
modules/game_bot/bot.otui
Normal file
104
modules/game_bot/bot.otui
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
BotButton < Button
|
||||||
|
margin-top: 2
|
||||||
|
|
||||||
|
BotSwitch < Button
|
||||||
|
margin-top: 2
|
||||||
|
image-color: green
|
||||||
|
$!on:
|
||||||
|
image-color: red
|
||||||
|
|
||||||
|
BotLabel < Label
|
||||||
|
margin-top: 2
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
|
||||||
|
BotPanel < Panel
|
||||||
|
margin-top: 2
|
||||||
|
layout:
|
||||||
|
type: verticalBox
|
||||||
|
fit-children: true
|
||||||
|
|
||||||
|
BotSeparator < HorizontalSeparator
|
||||||
|
margin-top: 5
|
||||||
|
margin-bottom: 3
|
||||||
|
|
||||||
|
MiniWindow
|
||||||
|
id: botWindow
|
||||||
|
!text: tr('Bot')
|
||||||
|
height: 200
|
||||||
|
icon: /images/topbuttons/bot
|
||||||
|
@onClose: modules.game_bot.onMiniWindowClose()
|
||||||
|
&save: true
|
||||||
|
|
||||||
|
MiniWindowContents
|
||||||
|
margin-left: 5
|
||||||
|
margin-right: 5
|
||||||
|
|
||||||
|
ComboBox
|
||||||
|
id: config
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-top: 5
|
||||||
|
margin-right: 90
|
||||||
|
text-offset: 3 0
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: editConfig
|
||||||
|
anchors.top: prev.top
|
||||||
|
anchors.left: prev.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
!text: tr('Edit')
|
||||||
|
@onClick: modules.game_bot.editConfig()
|
||||||
|
margin-left: 5
|
||||||
|
margin-right: 45
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: enableButton
|
||||||
|
anchors.top: prev.top
|
||||||
|
anchors.left: prev.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
@onClick: modules.game_bot.toggleBot()
|
||||||
|
margin-left: 5
|
||||||
|
|
||||||
|
Label
|
||||||
|
id: statusLabel
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-top: 5
|
||||||
|
text-auto-resize: true
|
||||||
|
!text: tr('Status: waiting')
|
||||||
|
text-align: center
|
||||||
|
text-wrap: true
|
||||||
|
|
||||||
|
HorizontalSeparator
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 5
|
||||||
|
|
||||||
|
Panel
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
id: messages
|
||||||
|
layout:
|
||||||
|
type: verticalBox
|
||||||
|
fit-children: true
|
||||||
|
|
||||||
|
HorizontalSeparator
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 5
|
||||||
|
|
||||||
|
Panel
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
id: botPanel
|
||||||
|
layout:
|
||||||
|
type: verticalBox
|
||||||
75
modules/game_bot/config.otui
Normal file
75
modules/game_bot/config.otui
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
ConfigTabBar < MoveableTabBar
|
||||||
|
height: 22
|
||||||
|
|
||||||
|
ConfigTabBarButton < MoveableTabBarButton
|
||||||
|
height: 22
|
||||||
|
padding: 15
|
||||||
|
|
||||||
|
ConfigTabBarPanel < MoveableTabBarPanel
|
||||||
|
|
||||||
|
MainWindow
|
||||||
|
id: configWindow
|
||||||
|
size: 650 500
|
||||||
|
!text: tr("Config editor")
|
||||||
|
|
||||||
|
Label
|
||||||
|
id: description
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text-auto-resize: true
|
||||||
|
text-align: left
|
||||||
|
text-wrap: true
|
||||||
|
!text: tr("For more informations how to edit config, click 'Documentation' button")
|
||||||
|
|
||||||
|
ConfigTabBar
|
||||||
|
id: configTab
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-top: 5
|
||||||
|
tab-spacing: 2
|
||||||
|
movable: false
|
||||||
|
|
||||||
|
MultilineTextEdit
|
||||||
|
id: text
|
||||||
|
anchors.top: textScroll.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: textScroll.left
|
||||||
|
anchors.bottom: textScroll.bottom
|
||||||
|
vertical-scrollbar: textScroll
|
||||||
|
text-wrap: true
|
||||||
|
|
||||||
|
VerticalScrollBar
|
||||||
|
id: textScroll
|
||||||
|
anchors.top: configTab.bottom
|
||||||
|
anchors.bottom: okButton.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
margin-bottom: 10
|
||||||
|
step: 16
|
||||||
|
pixels-scroll: true
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: documentationButton
|
||||||
|
!text: tr('Documentation')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: 150
|
||||||
|
@onClick: scheduleEvent(function() g_platform.openUrl("https://github.com/OTCv8/otclient_bot") end, 50)
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: okButton
|
||||||
|
!text: tr('Ok')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: next.left
|
||||||
|
margin-right: 10
|
||||||
|
width: 60
|
||||||
|
@onClick: modules.game_bot.saveEditedConfig()
|
||||||
|
|
||||||
|
Button
|
||||||
|
id: cancelButton
|
||||||
|
!text: tr('Cancel')
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
width: 60
|
||||||
|
@onClick: self:getParent():hide()
|
||||||
62
modules/game_bot/defaultconfig.lua
Normal file
62
modules/game_bot/defaultconfig.lua
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
botDefaultConfig = {
|
||||||
|
configs = {
|
||||||
|
{name = "Example", script = [[
|
||||||
|
--#Example config
|
||||||
|
|
||||||
|
--#macros
|
||||||
|
macro(5000, "macro send link", "f5", function()
|
||||||
|
g_game.talk("macro test - https://github.com/OTCv8/otclient_bot")
|
||||||
|
end)
|
||||||
|
|
||||||
|
macro(1000, "flag tiles", function()
|
||||||
|
local staticText = StaticText.create()
|
||||||
|
staticText:addMessage("t", 9, "xDDD")
|
||||||
|
local tile = player:getTile()
|
||||||
|
tile:clearTexts()
|
||||||
|
tile:addText(staticText)
|
||||||
|
for i = 1, 10 do
|
||||||
|
schedule(1000 * i, function()
|
||||||
|
staticText:setText(i)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
schedule(11000, function()
|
||||||
|
tile:clearTexts()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
addSeparator("spe0")
|
||||||
|
|
||||||
|
--#hotkeys
|
||||||
|
hotkey('y', 'test hotkey', function() g_game.talk('hotkey elo') end)
|
||||||
|
|
||||||
|
--#callbacks
|
||||||
|
|
||||||
|
--#other
|
||||||
|
addLabel("label1", "Test label 1")
|
||||||
|
addSeparator("sep1")
|
||||||
|
addLabel("label2", "Test label 2")
|
||||||
|
|
||||||
|
storage.clicks = 0
|
||||||
|
addButton("button1", "Click me", function()
|
||||||
|
storage.clicks = storage.clicks + 1
|
||||||
|
ui.button1:setText("Clicks: " .. storage.clicks)
|
||||||
|
end)
|
||||||
|
|
||||||
|
HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err)
|
||||||
|
if err then
|
||||||
|
warn("Whoops! Error occured: " .. err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
info("HTTP: My IP is: " .. tostring(data['ip']))
|
||||||
|
end)
|
||||||
|
|
||||||
|
info("Bot started")
|
||||||
|
|
||||||
|
|
||||||
|
]]},
|
||||||
|
{}, {}, {}, {}
|
||||||
|
},
|
||||||
|
enabled = false,
|
||||||
|
selectedConfig = 1
|
||||||
|
}
|
||||||
247
modules/game_bot/executor.lua
Normal file
247
modules/game_bot/executor.lua
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
function executeBot(config, storage, panel, msg)
|
||||||
|
local context = {}
|
||||||
|
context.panel = panel
|
||||||
|
context.storage = storage
|
||||||
|
if context.storage.macros == nil then
|
||||||
|
context.storage.macros = {} -- active macros
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
context.macros = {}
|
||||||
|
context.hotkeys = {}
|
||||||
|
context.scheduler = {}
|
||||||
|
context.callbacks = {
|
||||||
|
onKeyDown = {},
|
||||||
|
onKeyUp = {},
|
||||||
|
onKeyPress = {},
|
||||||
|
onTalk = {},
|
||||||
|
}
|
||||||
|
context.ui = {}
|
||||||
|
|
||||||
|
-- basic functions
|
||||||
|
context.print = print
|
||||||
|
context.pairs = pairs
|
||||||
|
context.ipairs = ipairs
|
||||||
|
context.tostring = tostring
|
||||||
|
context.math = math
|
||||||
|
context.table = table
|
||||||
|
context.string = string
|
||||||
|
context.tr = tr
|
||||||
|
context.json = json
|
||||||
|
context.regexMatch = regexMatch
|
||||||
|
|
||||||
|
-- game functions
|
||||||
|
context.say = g_game.talk
|
||||||
|
context.talk = g_game.talk
|
||||||
|
context.talkPrivate = context.talkPrivate
|
||||||
|
context.sayPrivate = context.talkPrivate
|
||||||
|
context.use = g_game.useInventoryItem
|
||||||
|
context.usewith = g_game.useInventoryItemWith
|
||||||
|
context.useWith = g_game.useInventoryItemWith
|
||||||
|
context.findItem = g_game.findItemInContainers
|
||||||
|
|
||||||
|
-- classes
|
||||||
|
context.g_game = g_game
|
||||||
|
context.g_map = g_map
|
||||||
|
context.StaticText = StaticText
|
||||||
|
context.HTTP = HTTP
|
||||||
|
|
||||||
|
-- log functions
|
||||||
|
context.info = function(text) return msg("info", text) end
|
||||||
|
context.warn = function(text) return msg("warn", text) end
|
||||||
|
context.error = function(text) return msg("error", text) end
|
||||||
|
context.warning = context.warn
|
||||||
|
|
||||||
|
-- UI
|
||||||
|
context.addSwitch = function(id, text, onClickCallback)
|
||||||
|
local switch = g_ui.createWidget('BotSwitch', context.panel)
|
||||||
|
switch:setId(id)
|
||||||
|
switch:setText(text)
|
||||||
|
switch.onClick = onClickCallback
|
||||||
|
context.ui[id] = switch
|
||||||
|
return switch
|
||||||
|
end
|
||||||
|
|
||||||
|
context.addButton = function(id, text, onClickCallback)
|
||||||
|
local button = g_ui.createWidget('BotButton', context.panel)
|
||||||
|
button:setId(id)
|
||||||
|
button:setText(text)
|
||||||
|
button.onClick = onClickCallback
|
||||||
|
context.ui[id] = button
|
||||||
|
return button
|
||||||
|
end
|
||||||
|
|
||||||
|
context.addLabel = function(id, text)
|
||||||
|
local label = g_ui.createWidget('BotLabel', context.panel)
|
||||||
|
label:setId(id)
|
||||||
|
label:setText(text)
|
||||||
|
context.ui[id] = label
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
|
||||||
|
context.addSeparator = function(id)
|
||||||
|
local separator = g_ui.createWidget('BotSeparator', context.panel)
|
||||||
|
separator:setId(id)
|
||||||
|
context.ui[id] = separator
|
||||||
|
return separator
|
||||||
|
end
|
||||||
|
|
||||||
|
context.addMacroSwitch = function(name, keys)
|
||||||
|
local text = name
|
||||||
|
if keys:len() > 0 then
|
||||||
|
text = name .. " [" .. keys .. "]"
|
||||||
|
end
|
||||||
|
local switch = context.addSwitch("macro_" .. #context.macros, text, function(widget)
|
||||||
|
context.storage.macros[name] = not context.storage.macros[name]
|
||||||
|
widget:setOn(context.storage.macros[name])
|
||||||
|
end)
|
||||||
|
switch:setOn(context.storage.macros[name])
|
||||||
|
return switch
|
||||||
|
end
|
||||||
|
|
||||||
|
context.addHotkeySwitch = function(name, keys)
|
||||||
|
local text = name
|
||||||
|
if keys:len() > 0 then
|
||||||
|
text = name .. " [" .. keys .. "]"
|
||||||
|
end
|
||||||
|
local switch = context.addSwitch("hotkey_" .. #context.hotkeys, text, nil)
|
||||||
|
switch:setOn(false)
|
||||||
|
return switch
|
||||||
|
end
|
||||||
|
|
||||||
|
-- MAIN BOT FUNCTION
|
||||||
|
-- macro(timeout, callback)
|
||||||
|
-- macro(timeout, name, callback)
|
||||||
|
-- macro(timeout, name, hotkey, callback)
|
||||||
|
context.macro = function(timeout, name, hotkey, callback)
|
||||||
|
if type(timeout) ~= 'number' or timeout < 1 then
|
||||||
|
error("Invalid timeout for macro: " .. tostring(timeout))
|
||||||
|
end
|
||||||
|
if type(name) == 'function' then
|
||||||
|
callback = name
|
||||||
|
name = ""
|
||||||
|
hotkey = ""
|
||||||
|
elseif type(hotkey) == 'function' then
|
||||||
|
callback = hotkey
|
||||||
|
hotkey = ""
|
||||||
|
elseif type(callback) ~= 'function' then
|
||||||
|
error("Invalid callback for macro: " .. tostring(callback))
|
||||||
|
end
|
||||||
|
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
|
||||||
|
error("Invalid name or hotkey for macro")
|
||||||
|
end
|
||||||
|
if hotkey:len() > 0 then
|
||||||
|
hotkey = retranslateKeyComboDesc(hotkey)
|
||||||
|
end
|
||||||
|
|
||||||
|
local switch = nil
|
||||||
|
if name:len() > 0 then
|
||||||
|
if context.storage.macros[name] == nil then
|
||||||
|
context.storage.macros[name] = true
|
||||||
|
end
|
||||||
|
switch = context.addMacroSwitch(name, hotkey)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(context.macros, {
|
||||||
|
timeout = timeout,
|
||||||
|
name = name,
|
||||||
|
callback = callback,
|
||||||
|
lastExecution = context.now,
|
||||||
|
hotkey = hotkey,
|
||||||
|
switch = switch
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- hotkey(keys, callback)
|
||||||
|
-- hotkey(keys, name, callback)
|
||||||
|
context.hotkey = function(keys, name, callback)
|
||||||
|
if type(name) == 'function' then
|
||||||
|
callback = name
|
||||||
|
name = ""
|
||||||
|
end
|
||||||
|
keys = retranslateKeyComboDesc(keys)
|
||||||
|
local switch = nil
|
||||||
|
if name:len() > 0 then
|
||||||
|
switch = context.addHotkeySwitch(name, keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
context.hotkeys[keys] = {
|
||||||
|
name = name,
|
||||||
|
callback = callback,
|
||||||
|
lastExecution = context.now,
|
||||||
|
switch = switch
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- schedule(timeout, callback)
|
||||||
|
context.schedule = function(timeout, callback)
|
||||||
|
local extecute_time = g_clock.millis() + timeout
|
||||||
|
table.insert(context.scheduler, {
|
||||||
|
execution = extecute_time,
|
||||||
|
callback = callback
|
||||||
|
})
|
||||||
|
table.sort(context.scheduler, function(a, b) return a.execution < b.execution end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- init context
|
||||||
|
context.now = g_clock.millis()
|
||||||
|
context.time = g_clock.millis()
|
||||||
|
context.player = g_game.getLocalPlayer()
|
||||||
|
|
||||||
|
-- run script
|
||||||
|
assert(load(config, nil, nil, context))()
|
||||||
|
|
||||||
|
return {
|
||||||
|
script = function()
|
||||||
|
context.now = g_clock.millis()
|
||||||
|
context.time = g_clock.millis()
|
||||||
|
|
||||||
|
for i, macro in ipairs(context.macros) do
|
||||||
|
if macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage.macros[macro.name]) then
|
||||||
|
macro.lastExecution = context.now
|
||||||
|
macro.callback()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while #context.scheduler > 0 and context.scheduler[1].execution <= g_clock.millis() do
|
||||||
|
context.scheduler[1].callback()
|
||||||
|
table.remove(context.scheduler, 1)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
callbacks = {
|
||||||
|
onKeyDown = function(keyCode, keyboardModifiers)
|
||||||
|
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||||
|
for i, macro in ipairs(context.macros) do
|
||||||
|
if macro.switch and macro.hotkey == keyDesc then
|
||||||
|
macro.switch:onClick()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local hotkey = context.hotkeys[keyDesc]
|
||||||
|
if hotkey and hotkey.switch then
|
||||||
|
hotkey.switch:setOn(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
end,
|
||||||
|
onKeyUp = function(keyCode, keyboardModifiers)
|
||||||
|
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||||
|
local hotkey = context.hotkeys[keyDesc]
|
||||||
|
if hotkey and hotkey.switch then
|
||||||
|
hotkey.switch:setOn(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
end,
|
||||||
|
onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks)
|
||||||
|
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
|
||||||
|
local hotkey = context.hotkeys[keyDesc]
|
||||||
|
if hotkey then
|
||||||
|
hotkey.lastExecution = context.now
|
||||||
|
hotkey.callback()
|
||||||
|
end
|
||||||
|
|
||||||
|
end,
|
||||||
|
onTalk = function(name, level, mode, text, channelId, pos)
|
||||||
|
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
@@ -9,6 +9,9 @@ end
|
|||||||
function updateFeatures(version)
|
function updateFeatures(version)
|
||||||
g_game.resetFeatures()
|
g_game.resetFeatures()
|
||||||
|
|
||||||
|
-- you can add custom features here, list of them in modules\gamelib\const.lua
|
||||||
|
g_game.getFeature(GameBot)
|
||||||
|
|
||||||
if(version >= 770) then
|
if(version >= 770) then
|
||||||
g_game.enableFeature(GameLooktypeU16);
|
g_game.enableFeature(GameLooktypeU16);
|
||||||
g_game.enableFeature(GameMessageStatements);
|
g_game.enableFeature(GameMessageStatements);
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ Module
|
|||||||
- game_unjustifiedpoints
|
- game_unjustifiedpoints
|
||||||
- game_walking
|
- game_walking
|
||||||
- game_shop
|
- game_shop
|
||||||
|
- game_bot
|
||||||
@onLoad: init()
|
@onLoad: init()
|
||||||
@onUnload: terminate()
|
@onUnload: terminate()
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ GameIngameStore = 73
|
|||||||
GameIngameStoreHighlights = 74
|
GameIngameStoreHighlights = 74
|
||||||
GameIngameStoreServiceType = 75
|
GameIngameStoreServiceType = 75
|
||||||
GameAdditionalSkills = 76
|
GameAdditionalSkills = 76
|
||||||
|
GameDistanceEffectU16 = 77
|
||||||
|
|
||||||
GameExtendedOpcode = 80
|
GameExtendedOpcode = 80
|
||||||
|
|
||||||
|
|||||||
BIN
otclient_dx.exe
BIN
otclient_dx.exe
Binary file not shown.
BIN
otclient_gl.exe
BIN
otclient_gl.exe
Binary file not shown.
@@ -1,21 +0,0 @@
|
|||||||
GPU ANGLE (Intel(R) UHD Graphics 620 Direct3D9Ex vs_3_0 ps_3_0)
|
|
||||||
OpenGL OpenGL ES 2.0 (ANGLE 2.1.a502c749b249)
|
|
||||||
== application started at Oct 03 2019 06:05:49
|
|
||||||
OTClientV8 0.95 beta rev 0 (alpha) made by otclient.ovh built on Oct 3 2019 for arch x86
|
|
||||||
GPU ANGLE (Intel(R) UHD Graphics 620 Direct3D9Ex vs_3_0 ps_3_0)
|
|
||||||
OpenGL OpenGL ES 2.0 (ANGLE 2.1.a502c749b249)
|
|
||||||
== application started at Oct 03 2019 06:18:44
|
|
||||||
OTClientV8 0.95 beta rev 0 (alpha) made by otclient.ovh built on Oct 3 2019 for arch x86
|
|
||||||
ERROR: protected lua call failed: attempt to call a boolean value
|
|
||||||
GPU Intel(R) UHD Graphics 620
|
|
||||||
OpenGL 4.5.0 - Build 24.20.100.6287
|
|
||||||
== application started at Oct 03 2019 06:22:12
|
|
||||||
OTClientV8 0.95 beta rev 0 (alpha) made by otclient.ovh built on Oct 2 2019 for arch x86
|
|
||||||
[Atlas] Texture size is: 4096x4096 (max: 16384x16384)
|
|
||||||
Exiting application..
|
|
||||||
GPU Intel(R) UHD Graphics 620
|
|
||||||
OpenGL 4.5.0 - Build 24.20.100.6287
|
|
||||||
== application started at Oct 03 2019 06:49:55
|
|
||||||
OTClientV8 0.95 beta rev 0 (alpha) made by otclient.ovh built on Oct 2 2019 for arch x86
|
|
||||||
[Atlas] Texture size is: 4096x4096 (max: 16384x16384)
|
|
||||||
Exiting application..
|
|
||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user