Version 0.999 BETA - a lot of bug fixes, improvments and more advanced bot

This commit is contained in:
OTCv8
2019-10-31 05:46:22 +01:00
parent 017fa290b4
commit 06b08af1df
36 changed files with 1513 additions and 415 deletions

View File

@@ -9,6 +9,7 @@ local render = nil
local atlas = nil
local adaptiveRender = nil
local slowMain = nil
local widgetsInfo = nil
local updateEvent = nil
local monitorEvent = nil
@@ -36,6 +37,7 @@ function init()
atlas = statsWindow:recursiveGetChildById('atlas')
adaptiveRender = statsWindow:recursiveGetChildById('adaptiveRender')
slowMain = statsWindow:recursiveGetChildById('slowMain')
widgetsInfo = statsWindow:recursiveGetChildById('widgetsInfo')
lastSend = os.time()
g_stats.resetSleepTime()
@@ -143,6 +145,7 @@ function sendStats()
g_stats.clear(i - 1)
g_stats.clearSlow(i - 1)
end
data.widgets = g_stats.getWidgetsInfo(10, false)
data = json.encode(data)
if Services.stats ~= nil and Services.stats:len() > 3 then
g_http.post(Services.stats, data)
@@ -171,7 +174,8 @@ function update()
dispatcherStats:setText(g_stats.get(3, 5, true))
luaStats:setText(g_stats.get(4, 5, true))
luaCallback:setText(g_stats.get(5, 5, true))
slowMain:setText(g_stats.getSlow(3, 10, 10, true) .. "\n\n\n" .. g_stats.getSlow(1, 20, 20, true))
slowMain:setText(g_stats.getSlow(3, 10, 10, true) .. "\n\n\n" .. g_stats.getSlow(1, 20, 20, true))
widgetsInfo:setText(g_stats.getWidgetsInfo(10, true))
if g_proxy then
local text = ""

View File

@@ -87,6 +87,13 @@ MainWindow
DebugText
id: luaCallback
text: -
DebugLabel
!text: tr('Widgets')
DebugText
id: widgetsInfo
text: -
DebugLabel
!text: tr('Slow main functions')

View File

@@ -317,6 +317,12 @@ function UIMoveableTabBar:onStyleApply(styleName, styleNode)
end
end
function UIMoveableTabBar:clearTabs()
while #self.tabs > 0 do
self:removeTab(self.tabs[#self.tabs])
end
end
function UIMoveableTabBar:removeTab(tab)
local tabTables = {self.tabs, self.preTabs, self.postTabs}
local index = nil

View File

@@ -80,8 +80,10 @@ end
function UISpinBox:onStyleApply(styleName, styleNode)
for name, value in pairs(styleNode) do
if name == 'maximum' then
self.maximum = value
addEvent(function() self:setMaximum(value) end)
elseif name == 'minimum' then
self.minimum = value
addEvent(function() self:setMinimum(value) end)
elseif name == 'mouse-scroll' then
addEvent(function() self:setMouseScroll(value) end)
@@ -118,6 +120,9 @@ function UISpinBox:down()
end
function UISpinBox:setValue(value, dontSignal)
if type(value) == "string" then
value = tonumber(value)
end
value = value or 0
value = math.max(math.min(self.maximum, value), self.minimum)

View File

@@ -6,6 +6,7 @@ contentsPanel = nil
configWindow = nil
configEditorText = nil
configList = nil
botTabs = nil
botPanel = nil
local botMessages = nil
local configCopy = ""
@@ -32,6 +33,17 @@ function init()
onKeyPress = botKeyPress })
connect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
connect(Creature, {
onAppear = botCreatureAppear,
onDisappear =botCreatureDisappear,
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
connect(LocalPlayer, {
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
botConfigFile = g_configs.create("/bot.otml")
local config = botConfigFile:get("config")
@@ -53,18 +65,20 @@ function init()
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')
contentsPanel = botWindow.contentsPanel
configList = contentsPanel.config
enableButton = contentsPanel.enableButton
statusLabel = contentsPanel.statusLabel
botMessages = contentsPanel.messages
botTabs = contentsPanel.botTabs
botPanel = contentsPanel.botPanel
botTabs:setContentWidget(botPanel)
configWindow = g_ui.displayUI('config')
configWindow:hide()
configEditorText = configWindow:getChildById('text')
configTab = configWindow:getChildById('configTab')
configEditorText = configWindow.text
configTab = configWindow.configTab
configTab.onTabChange = editorTabChanged
@@ -91,8 +105,27 @@ function init()
end
function saveConfig()
botConfigFile:set("config", json.encode(botConfig))
botConfigFile:save()
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()
@@ -107,6 +140,17 @@ function terminate()
disconnect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
disconnect(Creature, {
onAppear = botCreatureAppear,
onDisappear =botCreatureDisappear,
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
disconnect(LocalPlayer, {
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
removeEvent(executeEvent)
removeEvent(checkMsgsEvent)
@@ -259,13 +303,15 @@ end
function clearConfig()
compiledConfig = nil
botPanel:destroyChildren()
botTabs:clearTabs()
botTabs:setOn(false)
botMessages:destroyChildren()
botMessages:updateLayout()
end
function refreshConfig()
clearConfig()
configWindow:hide()
botConfig.selectedConfig = configList.currentIndex
@@ -273,8 +319,13 @@ function refreshConfig()
return
end
saveConfig()
if not saveConfig() then
clearConfig()
return
end
clearConfig()
local config = botConfig.configs[configList.currentIndex]
if not config.storage then
config.storage = {}
@@ -286,7 +337,7 @@ function refreshConfig()
end
errorOccured = false
g_game.enableTileThingLuaCallback(false)
local status, result = pcall(function() return executeBot(config.script, config.storage, botPanel, botMsgCallback) end)
local status, result = pcall(function() return executeBot(config.script, config.storage, botTabs, botMsgCallback) end)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. tostring(result))
@@ -342,9 +393,8 @@ function checkMsgs()
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)
function safeBotCall(func)
local status, result = pcall(func)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. result)
@@ -352,52 +402,55 @@ function botKeyDown(widget, keyCode, keyboardModifiers)
return false
end
function botKeyDown(widget, keyCode, keyboardModifiers)
if compiledConfig == nil then return false end
if keyCode == KeyUnknown then return end
safeBotCall(function() compiledConfig.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
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("Error: " .. result)
end
return false
if compiledConfig == nil then return false end
if keyCode == KeyUnknown then return end
safeBotCall(function() compiledConfig.callbacks.onKeyUp(keyCode, keyboardModifiers) end)
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("Error: " .. result)
end
return false
if compiledConfig == nil then return false end
if keyCode == KeyUnknown then return end
safeBotCall(function() compiledConfig.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end)
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("Error: " .. result)
end
return false
safeBotCall(function() compiledConfig.callbacks.onTalk(name, level, mode, text, channelId, pos) end)
end
function botAddThing(tile, thing, asd)
function botAddThing(tile, thing)
if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onAddThing(tile, thing) end)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
safeBotCall(function() compiledConfig.callbacks.onAddThing(tile, thing) end)
end
function botRemoveThing(tile, thing)
if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
end
safeBotCall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end)
end
function botCreatureAppear(creature)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreatureAppear(creature) end)
end
function botCreatureDisappear(creature)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreatureDisappear(creature) end)
end
function botCreaturePositionChange(creature, newPos, oldPos)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreaturePositionChange(creature, newPos, oldPos) end)
end
function botCraetureHealthPercentChange(creature, healthPercent)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreatureHealthPercentChange(creature, healthPercent) end)
end

View File

@@ -3,12 +3,14 @@ BotButton < Button
BotSwitch < Button
margin-top: 2
height: 20
image-color: green
$!on:
image-color: red
BotLabel < Label
margin-top: 2
height: 20
text-auto-resize: true
text-align: center
text-wrap: true
@@ -18,17 +20,20 @@ BotItem < UIItem
size: 32 32
border: 1 black
&selectable: true
BotTextEdit < TextEdit
@onClick: modules.game_textedit.show(self)
text-align: center
multiline: false
BotSeparator < HorizontalSeparator
margin-top: 5
margin-bottom: 3
BotPanel < Panel
margin-top: 2
layout:
type: verticalBox
fit-children: true
MiniWindow
id: botWindow
!text: tr('Bot')
@@ -99,12 +104,28 @@ MiniWindow
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
MoveableTabBar
id: botTabs
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
tab-spacing: 2
height: 20
movable: false
$on:
visible: true
margin-top: 2
$!on:
visible: false
margin-top: -20
Panel
id: botPanel
margin-top: 2
anchors.top: prev.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
id: botPanel
layout:
type: verticalBox

View File

@@ -1,82 +1,52 @@
botDefaultConfig = {
configs = {
{name = "Example", script = [=[
--#Example
info("Tested on 10.99")
--Example
--#main
local widget = setupUI([[
Panel
id: redPanel
background: red
margin-top: 10
margin-bottom: 10
height: 100
Label
anchors.fill: parent
text: custom ui, otml based
text-align: center
]])
Panels.Haste()
Panels.ManaShield()
Panels.Health()
Panels.HealthItem()
Panels.ManaItem()
Panels.ManaItem()
Panels.AntiParalyze()
local tab2 = addTab("Another Tab")
addButton("button1", "test button on 2nd tab", nil, tab2)
local tab3 = addTab("3th tab")
addLabel("label1", "Label on 3th tab", tab3)
Panels.Turning(tab3)
--#macros
macro(5000, "macro send link", "f5", function()
g_game.talk("macro test - https://github.com/OTCv8/otclient_bot")
g_game.talk("bot is hiding 50% of effects as example, say exevo gran mas vis")
end)
macro(1000, "flag tiles", function()
player:getTile():setText("Hello =)", "red")
end)
macro(25, "auto healing", function()
if hppercent() < 80 then
say("exura")
delay(1000) -- not calling this macro for next 1s
end
end)
addSeparator("spe0")
local helloLabel = addLabel("helloLabel", "", tab2)
macro(1000, "example macro (time)", nil, function()
helloLabel:setText("Time from start: " .. now)
end, tab2)
--#hotkeys
hotkey('y', 'test hotkey', function() g_game.talk('hotkey elo') end)
singlehotkey('x', 'single hotkey', function() g_game.talk('single hotkey') end)
singlehotkey('=', "Zoom in map", function () zoomIn() end)
singlehotkey('-', "Zoom out map", function () zoomOut() end)
--#callbacks
onAddThing(function(tile, thing)
if thing:isItem() and thing:getId() == 2129 then
local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
if not storage[pos] or storage[pos] < now then
storage[pos] = now + 20000
end
tile:setTimer(storage[pos] - now)
end
hotkey("f5", "example hotkey", function()
info("Wow, you clicked f5 hotkey")
end)
-- hide 50% of effects
onAddThing(function(tile, thing)
if thing:isEffect() and math.random(1, 2) == 1 then
thing:hide()
end
singlehotkey("f6", "example hotkey2", function()
info("Wow, you clicked f6 singlehotkey")
end)
--#callbacks
local positionLabel = addLabel("positionLabel", "")
onPlayerPositionChange(function()
positionLabel:setText("Pos: " .. posx() .. "," .. posy() .. "," .. posz())
end)
listen(player:getName(), function(text)
info("you said: " .. text)
end)
--#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
@@ -85,6 +55,7 @@ HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err)
end
info("HTTP: My IP is: " .. tostring(data['ip']))
end)
]=]},

View File

@@ -1,6 +1,8 @@
function executeBot(config, storage, panel, msgCallback)
function executeBot(config, storage, tabs, msgCallback)
local context = {}
context.panel = panel
context.tabs = tabs
context.panel = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel
context.storage = storage
if context.storage._macros == nil then
context.storage._macros = {} -- active macros
@@ -16,9 +18,12 @@ function executeBot(config, storage, panel, msgCallback)
onKeyPress = {},
onTalk = {},
onAddThing = {},
onRemoveThing = {}
onRemoveThing = {},
onCreatureAppear = {},
onCreatureDisappear = {},
onCreaturePositionChange = {},
onCreatureHealthPercentChange = {}
}
context.ui = {}
-- basic functions & classes
context.print = print
@@ -44,265 +49,20 @@ function executeBot(config, storage, panel, msgCallback)
context.info = function(text) return msgCallback("info", tostring(text)) end
context.warn = function(text) return msgCallback("warn", tostring(text)) end
context.error = function(text) return msgCallback("error", tostring(text)) end
context.warning = context.warn
-- UI
context.setupUI = function(otml, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.loadUIFromString(otml, parent)
if parent == context.panel and widget:getId() then
context.ui[widget:getId()] = widget
end
return widget
end
context.addSwitch = function(id, text, onClickCallback)
local switch = g_ui.createWidget('BotSwitch', context.panel)
switch:setId(id)
switch:setText(text)
switch.onClick = onClickCallback
context.ui[id] = switch
return switch
end
context.addButton = function(id, text, onClickCallback)
local button = g_ui.createWidget('BotButton', context.panel)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
context.ui[id] = button
return button
end
context.addLabel = function(id, text)
local label = g_ui.createWidget('BotLabel', context.panel)
label:setId(id)
label:setText(text)
context.ui[id] = label
return label
end
context.addSeparator = function(id)
local separator = g_ui.createWidget('BotSeparator', context.panel)
separator:setId(id)
context.ui[id] = separator
return separator
end
context._addMacroSwitch = function(name, keys)
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("macro_" .. #context._macros, text, function(widget)
context.storage._macros[name] = not context.storage._macros[name]
widget:setOn(context.storage._macros[name])
end)
switch:setOn(context.storage._macros[name])
return switch
end
context._addHotkeySwitch = function(name, keys)
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil)
switch:setOn(false)
return switch
end
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, hotkey, callback)
context.macro = function(timeout, name, hotkey, callback)
if type(timeout) ~= 'number' or timeout < 1 then
error("Invalid timeout for macro: " .. tostring(timeout))
end
if type(name) == 'function' then
callback = name
name = ""
hotkey = ""
elseif type(hotkey) == 'function' then
callback = hotkey
hotkey = ""
elseif type(callback) ~= 'function' then
error("Invalid callback for macro: " .. tostring(callback))
end
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
error("Invalid name or hotkey for macro")
end
if hotkey:len() > 0 then
hotkey = retranslateKeyComboDesc(hotkey)
end
local switch = nil
if name:len() > 0 then
if context.storage._macros[name] == nil then
context.storage._macros[name] = true
end
switch = context._addMacroSwitch(name, hotkey)
end
table.insert(context._macros, {
timeout = timeout,
name = name,
callback = callback,
lastExecution = context.now,
hotkey = hotkey,
switch = switch
})
return context._macros[#context._macros]
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
context.hotkey = function(keys, name, callback)
if type(name) == 'function' then
callback = name
name = ""
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys)
end
context._hotkeys[keys] = {
name = name,
callback = callback,
lastExecution = context.now,
switch = switch,
single = false
}
return context._hotkeys[keys]
end
-- singlehotkey(keys, callback)
-- singlehotkey(keys, name, callback)
context.singlehotkey = function(keys, name, callback)
if type(name) == 'function' then
callback = name
name = ""
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys)
end
context._hotkeys[keys] = {
name = name,
callback = callback,
lastExecution = context.now,
switch = switch,
single = true
}
return context._hotkeys[keys]
end
-- schedule(timeout, callback)
context.schedule = function(timeout, callback)
local extecute_time = g_clock.millis() + timeout
table.insert(context._scheduler, {
execution = extecute_time,
callback = callback
})
table.sort(context._scheduler, function(a, b) return a.execution < b.execution end)
end
-- callback(callbackType, callback)
context.callback = function(callbackType, callback)
if not context._callbacks[callbackType] then
return error("Wrong callback type: " .. callbackType)
end
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
g_game.enableTileThingLuaCallback(true)
end
local callbackData = {}
table.insert(context._callbacks[callbackType], function(...)
if not callbackData.delay or callbackData.delay < context.now then
context._currentExecution = callbackData
callback(...)
context._currentExecution = nil
end
end)
end
-- onKeyDown(callback) -- callback = function(keys)
context.onKeyDown = function(callback)
return context.callback("onKeyDown", callback)
end
-- onKeyPress(callback) -- callback = function(keys)
context.onKeyPress = function(callback)
return context.callback("onKeyPress", callback)
end
-- onKeyUp(callback) -- callback = function(keys)
context.onKeyUp = function(callback)
return context.callback("onKeyUp", callback)
end
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
context.onTalk = function(callback)
return context.callback("onTalk", callback)
end
-- onAddThing(callback) -- callback = function(tile, thing)
context.onAddThing = function(callback)
return context.callback("onAddThing", callback)
end
-- onRemoveThing(callback) -- callback = function(tile, thing)
context.onRemoveThing = function(callback)
return context.callback("onRemoveThing", callback)
end
-- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback)
if not name then return context.error("listen: invalid name") end
name = name:lower()
context.onTalk(function(name2, level, mode, text, channelId, pos)
if name == name2:lower() then
callback(text, channelId, pos)
end
end)
end
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
context.delay = function(duration)
if not context._currentExecution then
return context.error("Invalid usage of delay function, it should be used inside callbacks")
end
context._currentExecution.delay = context.now + duration
end
context.warning = context.warn
-- init context
context.now = g_clock.millis()
context.time = g_clock.millis()
context.player = g_game.getLocalPlayer()
require("functions.lua")
setupFunctions(context)
-- init functions
G.botContext = context
dofiles("functions")
context.Panels = {}
dofiles("panels")
G.botContext = nil
-- run script
assert(load(config, nil, nil, context))()
@@ -313,11 +73,8 @@ function executeBot(config, storage, panel, msgCallback)
for i, macro in ipairs(context._macros) do
if macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage._macros[macro.name]) then
if not macro.delay or macro.delay < context.now then
macro.lastExecution = context.now
context._currentExecution = context._macros[i]
macro.callback()
context._currentExecution = nil
if macro.callback() then
macro.lastExecution = context.now
end
end
end
@@ -338,11 +95,8 @@ function executeBot(config, storage, panel, msgCallback)
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then
hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
if hotkey.switch then
@@ -369,11 +123,8 @@ function executeBot(config, storage, panel, msgCallback)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey and not hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then
hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
for i, callback in ipairs(context._callbacks.onKeyPress) do
@@ -394,6 +145,26 @@ function executeBot(config, storage, panel, msgCallback)
for i, callback in ipairs(context._callbacks.onRemoveThing) do
callback(tile, thing)
end
end,
onCreatureAppear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureAppear) do
callback(creature)
end
end,
onCreatureDisappear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
callback(creature)
end
end,
onCreaturePositionChange = function(creature, newPos, oldPos)
for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
callback(creature, newPos, oldPos)
end
end,
onCreatureHealthPercentChange = function(creature, healthPercent)
for i, callback in ipairs(context._callbacks.onCreatureHealthPercentChange) do
callback(creature, healthPercent)
end
end
}
}

View File

@@ -0,0 +1,101 @@
local context = G.botContext
-- callback(callbackType, callback)
context.callback = function(callbackType, callback)
if not context._callbacks[callbackType] then
return error("Wrong callback type: " .. callbackType)
end
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
g_game.enableTileThingLuaCallback(true)
end
local callbackData = {}
table.insert(context._callbacks[callbackType], function(...)
if not callbackData.delay or callbackData.delay < context.now then
context._currentExecution = callbackData
callback(...)
context._currentExecution = nil
end
end)
end
-- onKeyDown(callback) -- callback = function(keys)
context.onKeyDown = function(callback)
return context.callback("onKeyDown", callback)
end
-- onKeyPress(callback) -- callback = function(keys)
context.onKeyPress = function(callback)
return context.callback("onKeyPress", callback)
end
-- onKeyUp(callback) -- callback = function(keys)
context.onKeyUp = function(callback)
return context.callback("onKeyUp", callback)
end
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
context.onTalk = function(callback)
return context.callback("onTalk", callback)
end
-- onAddThing(callback) -- callback = function(tile, thing)
context.onAddThing = function(callback)
return context.callback("onAddThing", callback)
end
-- onRemoveThing(callback) -- callback = function(tile, thing)
context.onRemoveThing = function(callback)
return context.callback("onRemoveThing", callback)
end
-- onCreatureAppear(callback) -- callback = function(creature)
context.onCreatureAppear = function(callback)
return context.callback("onCreatureAppear", callback)
end
-- onCreatureDisappear(callback) -- callback = function(creature)
context.onCreatureDisappear = function(callback)
return context.callback("onCreatureDisappear", callback)
end
-- onCreaturePositionChange(callback) -- callback = function(creature, newPos, oldPos)
context.onCreaturePositionChange = function(callback)
return context.callback("onCreaturePositionChange", callback)
end
-- onCreatureHealthPercentChange(callback) -- callback = function(creature, healthPercent)
context.onCreatureHealthPercentChange = function(callback)
return context.callback("onCreatureHealthPercentChange", callback)
end
-- custom callbacks
-- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback)
if not name then return context.error("listen: invalid name") end
name = name:lower()
context.onTalk(function(name2, level, mode, text, channelId, pos)
if name == name2:lower() then
callback(text, channelId, pos)
end
end)
end
-- onPlayerPositionChange(callback) -- callback = function(newPos, oldPos)
context.onPlayerPositionChange = function(callback)
context.onCreaturePositionChange(function(creature, newPos, oldPos)
if creature == context.player then
callback(newPos, oldPos)
end
end)
end
-- onPlayerHealthChange(callback) -- callback = function(healthPercent)
context.onPlayerHealthChange = function(callback)
context.onCreatureHealthPercentChange(function(creature, healthPercent)
if creature == context.player then
callback(healthPercent)
end
end)
end

View File

@@ -0,0 +1,136 @@
local context = G.botContext
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, hotkey, callback)
-- macro(timeout, name, hotkey, callback, parent)
context.macro = function(timeout, name, hotkey, callback, parent)
if not parent then
parent = context.panel
end
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 hotkey == nil then
hotkey = ""
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, parent)
end
table.insert(context._macros, {
timeout = timeout,
name = name,
lastExecution = context.now,
hotkey = hotkey,
switch = switch
})
local macroData = context._macros[#context._macros]
macroData.callback = function()
if not macroData.delay or macroData.delay < context.now then
context._currentExecution = macroData
callback()
context._currentExecution = nil
return true
end
end
return macroData
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
-- hotkey(keys, name, callback, parent)
context.hotkey = function(keys, name, callback, single, parent)
if not parent then
parent = context.panel
end
if type(name) == 'function' then
callback = name
name = ""
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys, parent)
end
context._hotkeys[keys] = {
name = name,
lastExecution = context.now,
switch = switch,
single = single
}
local hotkeyData = context._hotkeys[keys]
hotkeyData.callback = function()
if not hotkeyData.delay or hotkeyData.delay < context.now then
context._currentExecution = hotkeyData
callback()
context._currentExecution = nil
return true
end
end
return hotkeyData
end
-- singlehotkey(keys, callback)
-- singlehotkey(keys, name, callback)
-- singlehotkey(keys, name, callback, parent)
context.singlehotkey = function(keys, name, callback, parent)
if type(name) == 'function' then
callback = name
name = ""
end
return context.hotkey(keys, name, callback, true, parent)
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
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
context.delay = function(duration)
if not context._currentExecution then
return context.error("Invalid usage of delay function, it should be used inside callbacks")
end
context._currentExecution.delay = context.now + duration
end

View File

@@ -0,0 +1,39 @@
local context = G.botContext
context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end
context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end
context.getSpectators = function(multifloor)
if multifloor ~= true then
multifloor = false
end
return g_map.getSpectators(context.player:getPosition(), multifloor)
end
context.getCreatureByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getName():lower() == name then
return spec
end
end
return nil
end
context.getPlayerByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:isPlayer() and spec:getName():lower() == name then
return spec
end
end
return nil
end

View File

@@ -0,0 +1,98 @@
local context = G.botContext
context.name = function() return context.player:getName() end
context.hp = function() return context.player:getHealth() end
context.mana = function() return context.player:getMana() end
context.hppercent = function() return context.player:getHealthPercent() end
context.manapercent = function() if context.player:getMaxMana() <= 1 then return 100 else return math.floor(context.player:getMana() * 100 / context.player:getMaxMana()) end end
context.maxhp = function() return context.player:getMaxHealth() end
context.maxmana = function() return context.player:getMaxMana() end
context.hpmax = function() return context.player:getMaxHealth() end
context.manamax = function() return context.player:getMaxMana() end
context.cap = function() return context.player:getCapacity() end
context.freecap = function() return context.player:getFreeCapacity() end
context.maxcap = function() return context.player:getTotalCapacity() end
context.capmax = function() return context.player:getTotalCapacity() end
context.exp = function() return context.player:getExperience() end
context.lvl = function() return context.player:getLevel() end
context.level = function() return context.player:getLevel() end
context.mlev = function() return context.player:getMagicLevel() end
context.magic = function() return context.player:getMagicLevel() end
context.mlevel = function() return context.player:getMagicLevel() end
context.soul = function() return context.player:getSoul() end
context.stamina = function() return context.player:getStamina() end
context.voc = function() return context.player:getVocation() end
context.vocation = function() return context.player:getVocation() end
context.bless = function() return context.player:getBlessings() end
context.blesses = function() return context.player:getBlessings() end
context.blessings = function() return context.player:getBlessings() end
context.pos = function() return context.player:getPosition() end
context.posx = function() return context.player:getPosition().x end
context.posy = function() return context.player:getPosition().y end
context.posz = function() return context.player:getPosition().z end
context.direction = function() return context.player:getDirection() end
context.speed = function() return context.player:getSpeed() end
context.skull = function() return context.player:getSkull() end
context.outfit = function() return context.player:getOutfit() end
context.setOutfit = function(outfit)
modules.game_outfit.ignoreNextOutfitWindow = g_clock.millis()
g_game.requestOutfit()
context.schedule(100, function()
g_game.changeOutfit(outfit)
end)
end
context.changeOutfit = context.setOutfit
context.setSpeed = function(value) context.player:setSpeed(value) end
context.autoWalk = function(destination) return context.player:autoWalk(destination) end
context.walk = function(dir) return modules.game_walking.walk(dir) end
context.turn = function(dir) return g_game.turn(dir) end
-- game releated
context.say = g_game.talk
context.talk = g_game.talk
context.saySpell = function(text, lastSpellTimeout)
if context.lastSpell == nil then
context.lastSpell = 0
end
if not lastSpellTimeout then
lastSpellTimeout = 1000
end
if context.lastSpell + lastSpellTimeout > context.now then
return false
end
context.say(text)
context.lastSpell = context.now
return true
end
context.setSpellTimeout = function()
context.lastSpell = context.now
end
context.talkPrivate = g_game.talkPrivate
context.sayPrivate = g_game.talkPrivate
context.use = g_game.useInventoryItem
context.usewith = g_game.useInventoryItemWith
context.useWith = g_game.useInventoryItemWith
context.findItem = g_game.findItemInContainers
context.attack = g_game.attack
context.cancelAttack = g_game.cancelAttack
context.follow = g_game.follow
context.cancelFollow = g_game.cancelFollow
context.cancelAttackAndFollow = g_game.cancelAttackAndFollow
context.logout = g_game.forceLogout
context.ping = g_game.getPing

View File

@@ -0,0 +1,32 @@
local context = G.botContext
for i, state in ipairs(PlayerStates) do
context[state] = state
end
context.hasCondition = function(condition) return bit.band(context.player:getStates(), condition) > 0 end
context.isPoisioned = function() return context.hasCondition(PlayerStates.Poison) end
context.isBurnining = function() return context.hasCondition(PlayerStates.Burn) end
context.isEnergized = function() return context.hasCondition(PlayerStates.Energy) end
context.isDrunk = function() return context.hasCondition(PlayerStates.Drunk) end
context.hasManaShield = function() return context.hasCondition(PlayerStates.ManaShield) end
context.isParalyzed = function() return context.hasCondition(PlayerStates.Paralyze) end
context.hasHaste = function() return context.hasCondition(PlayerStates.Haste) end
context.hasSwords = function() return context.hasCondition(PlayerStates.Swords) end
context.isInFight = function() return context.hasCondition(PlayerStates.Swords) end
context.canLogout = function() return not context.hasCondition(PlayerStates.Swords) end
context.isDrowning = function() return context.hasCondition(PlayerStates.Drowning) end
context.isFreezing = function() return context.hasCondition(PlayerStates.Freezing) end
context.isDazzled = function() return context.hasCondition(PlayerStates.Dazzled) end
context.isCursed = function() return context.hasCondition(PlayerStates.Cursed) end
context.hasPartyBuff = function() return context.hasCondition(PlayerStates.PartyBuff) end
context.hasPzLock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.hasPzBlock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzLocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzBlocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isInProtectionZone = function() return context.hasCondition(PlayerStates.Pz) end
context.hasPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isInPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isBleeding = function() return context.hasCondition(PlayerStates.Bleeding) end
context.isHungry = function() return context.hasCondition(PlayerStates.Hungry) end

View File

@@ -0,0 +1,33 @@
local context = G.botContext
context.SlotOther = InventorySlotOther
context.SlotHead = InventorySlotHead
context.SlotNeck = InventorySlotNeck
context.SlotBack = InventorySlotBack
context.SlotBody = InventorySlotBody
context.SlotRight = InventorySlotRight
context.SlotLeft = InventorySlotLeft
context.SlotLeg = InventorySlotLeg
context.SlotFeet = InventorySlotFeet
context.SlotFinger = InventorySlotFinger
context.SlotAmmo = InventorySlotAmmo
context.SlotPurse = InventorySlotPurse
context.getInventoryItem = function(slot) return context.player:getInventoryItem(slot) end
context.getSlot = context.getInventoryItem
context.getHead = function() return context.getInventoryItem(context.SlotHead) end
context.getNeck = function() return context.getInventoryItem(context.SlotNeck) end
context.getBack = function() return context.getInventoryItem(context.SlotBack) end
context.getBody = function() return context.getInventoryItem(context.SlotBody) end
context.getRight = function() return context.getInventoryItem(context.SlotRight) end
context.getLeft = function() return context.getInventoryItem(context.SlotLeft) end
context.getLeg = function() return context.getInventoryItem(context.SlotLeg) end
context.getFeet = function() return context.getInventoryItem(context.SlotFeet) end
context.getFinger = function() return context.getInventoryItem(context.SlotFinger) end
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
context.getContainers = function() return g_game.getContainers() end
context.getContainer = function(index) return g_game.getContainer(index) end

View File

@@ -0,0 +1,3 @@
local context = G.botContext
context.test = function() return context.info("test") end

View File

@@ -0,0 +1,4 @@
local context = G.botContext
context.encode = function(data) return json.encode(data) end
context.decode = function(text) local status, result = pcall(function() return json.decode(text) end) if status then return result end return {} end

View File

@@ -0,0 +1,95 @@
local context = G.botContext
context.setupUI = function(otml, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.loadUIFromString(otml, parent)
return widget
end
context.addTab = function(name)
context.tabs:setOn(true)
return context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel
end
context.addSwitch = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local switch = g_ui.createWidget('BotSwitch', parent)
switch:setId(id)
switch:setText(text)
switch.onClick = onClickCallback
return switch
end
context.addButton = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local button = g_ui.createWidget('BotButton', parent)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
return button
end
context.addLabel = function(id, text, parent)
if not parent then
parent = context.panel
end
local label = g_ui.createWidget('BotLabel', parent)
label:setId(id)
label:setText(text)
return label
end
context.addTextEdit = function(id, text, onTextChangeCallback, parent)
if not parent then
parent = context.panel
end
local widget = g_ui.createWidget('BotTextEdit', parent)
widget:setId(id)
widget.onTextChange = onTextChangeCallback
widget:setText(text)
return widget
end
context.addSeparator = function(id, parent)
if not parent then
parent = context.panel
end
local separator = g_ui.createWidget('BotSeparator', parent)
separator:setId(id)
return separator
end
context._addMacroSwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
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, parent)
switch:setOn(context.storage._macros[name])
return switch
end
context._addHotkeySwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil, parent)
switch:setOn(false)
return switch
end

View File

@@ -0,0 +1,326 @@
local context = G.botContext
local Panels = context.Panels
Panels.Haste = function(parent)
context.macro(500, "Auto Haste", nil, function()
if not context.hasHaste() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoHasteText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoHasteText or "utani hur", function(widget, text)
context.storage.autoHasteText = text
end, parent)
end
Panels.ManaShield = function(parent)
context.macro(500, "Auto ManaShield", nil, function()
if not context.hasManaShield() then
if context.saySpell("utamo vita", 1000) then
context.delay(5000)
end
end
end, parent)
end
Panels.AntiParalyze = function(parent)
context.macro(500, "Anti Paralyze", nil, function()
if not context.isParalyzed() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoAntiParalyzeText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoAntiParalyzeText or "utani hur", function(widget, text)
context.storage.autoAntiParalyzeText = text
end, parent)
end
Panels.Health = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 70
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Healing
text-align: center
Label
id: label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
BotTextEdit
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: scroll1.bottom
]], parent)
ui:setId(panelName)
ui.text.onTextChange = function(widget, text)
context.storage["healthText" .. panelId] = text
end
ui.text:setText(context.storage["healthText" .. panelId] or "exura")
local updateText = function()
ui.label:setText("" .. (context.storage["healthPercentMin" .. panelId] or "") .. "% <= hp <= " .. (context.storage["healthPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["healthPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["healthPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["healthPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["healthPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["healthText" .. panelId]:len() > 0 and context.storage["healthPercentMin" .. panelId] <= context.hppercent() and context.hppercent() <= context.storage["healthPercentMax" .. panelId] then
if context.saySpell(context.storage["healthText" .. panelId], 500) then
context.delay(200)
end
end
end)
end
Panels.HealthItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 55
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Healing
text-align: center
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
Label
id: label
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
]], parent)
ui:setId(panelName)
ui.item.onItemChange = function(widget)
cp("item change")
context.storage["healthItem" .. panelId] = widget:getItemId()
end
ui.item:setItemId(context.storage["healthItem" .. panelId] or 266)
local updateText = function()
ui.label:setText("" .. (context.storage["healthItemPercentMin" .. panelId] or "") .. "% <= hp <= " .. (context.storage["healthItemPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["healthItemPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["healthItemPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["healthItemPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["healthItemPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["healthItem" .. panelId] > 0 and context.storage["healthItemPercentMin" .. panelId] <= context.hppercent() and context.hppercent() <= context.storage["healthItemPercentMax" .. panelId] then
context.useWith(context.storage["healthItem" .. panelId], context.player)
context.delay(300)
end
end)
end
Panels.Mana = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoManaItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 55
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Mana
text-align: center
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
Label
id: label
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
]], parent)
ui:setId(panelName)
ui.item.onItemChange = function(widget)
context.storage["manaItem" .. panelId] = widget:getItemId()
end
ui.item:setItemId(context.storage["manaItem" .. panelId] or 268)
local updateText = function()
ui.label:setText("" .. (context.storage["manaItemPercentMin" .. panelId] or "") .. "% <= mana <= " .. (context.storage["manaItemPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["manaItemPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["manaItemPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["manaItemPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["manaItemPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["manaItem" .. panelId] > 0 and context.storage["manaItemPercentMin" .. panelId] <= context.manapercent() and context.manapercent() <= context.storage["manaItemPercentMax" .. panelId] then
context.useWith(context.storage["manaItem" .. panelId], context.player)
context.delay(300)
end
end)
end
Panels.ManaItem = Panels.Mana
Panels.Turning = function(parent)
context.macro(1000, "Turning / AntiIdle", nil, function()
context.turn(math.random(1, 4))
end, parent)
end
Panels.AntiIdle = Panels.Turning

View File

@@ -619,22 +619,36 @@ function addTabText(text, speaktype, tab, creatureName)
local panel = consoleTabBar:getTabPanel(tab)
local consoleBuffer = panel:getChildById('consoleBuffer')
local label = g_ui.createWidget('ConsoleLabel', consoleBuffer)
local label = nil
if consoleBuffer:getChildCount() > MAX_LINES then
label = consoleBuffer:getFirstChild()
consoleBuffer:moveChildToIndex(label, consoleBuffer:getChildCount())
end
if not label then
label = g_ui.createWidget('ConsoleLabel', consoleBuffer)
end
label:setId('consoleLabel' .. consoleBuffer:getChildCount())
label:setText(text)
label:setColor(speaktype.color)
consoleTabBar:blinkTab(tab)
-- Overlay for consoleBuffer which shows highlighted words only
-- Overlay for consoleBuffer which shows highlighted words only
local labelHighlight = label:getChildById("consoleLabelHighlight")
if labelHighlight then
labelHighlight:setText("")
end
if speaktype.npcChat and (g_game.getCharacterName() ~= creatureName or g_game.getCharacterName() == 'Account Manager') then
local highlightData = getHighlightedText(text)
if #highlightData > 0 then
local labelHighlight = g_ui.createWidget('ConsolePhantomLabel', label)
labelHighlight:fill('parent')
labelHighlight:setId('consoleLabelHighlight' .. consoleBuffer:getChildCount())
labelHighlight:setColor("#1f9ffe")
if not labelHighlight then
labelHighlight = g_ui.createWidget('ConsolePhantomLabel', label)
labelHighlight:fill('parent')
labelHighlight:setId('consoleLabelHighlight')
labelHighlight:setColor("#1f9ffe")
end
-- Remove the curly braces
for i = 1, #highlightData / 3 do
@@ -768,12 +782,6 @@ function addTabText(text, speaktype, tab, creatureName)
return true
end
if consoleBuffer:getChildCount() > MAX_LINES then
local child = consoleBuffer:getFirstChild()
clearSelection(consoleBuffer)
child:destroy()
end
end
function removeTabLabelByName(tab, name)
@@ -1499,6 +1507,12 @@ function online()
defaultTab = addTab(tr('Default'), true)
serverTab = addTab(tr('Server Log'), false)
if g_game.getClientVersion() >= 820 then
local tab = addTab("NPCs", false)
tab.npcChat = true
end
if g_game.getClientVersion() < 862 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow, gameRootPanel)

View File

@@ -12,6 +12,7 @@ function updateFeatures(version)
-- you can add custom features here, list of them in modules\gamelib\const.lua
g_game.enableFeature(GameBot)
--g_game.enableFeature(GameMinimapLimitedToSingleFloor)
--g_game.enableFeature(GameSpritesAlphaChannel)
if(version >= 770) then
g_game.enableFeature(GameLooktypeU16);

View File

@@ -402,7 +402,7 @@ end
function prepareKeyCombo(keyCombo, repeated)
local hotKey = hotkeyList[keyCombo]
if keyCombo:lower():find("ctrl") or not hotKey or (hotKey.itemId == nil and (not hotKey.value or #hotKey.value == 0)) then
if (keyCombo:lower():find("ctrl") and not hotKey) or (hotKey and hotKey.itemId == nil and (not hotKey.value or #hotKey.value == 0)) then
keyCombo = keyCombo:gsub("Ctrl%+", "")
keyCombo = keyCombo:gsub("ctrl%+", "")
hotKey = hotkeyList[keyCombo]

View File

@@ -531,9 +531,13 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
end
end
if g_game.getFeature(GameBot) and useThing then
if g_game.getFeature(GameBot) and useThing and useThing:isItem() then
menu:addSeparator()
menu:addOption("ID: " .. useThing:getId())
if useThing:getSubType() > 1 then
menu:addOption("ID: " .. useThing:getId() .. " SubType: " .. useThing:getSubType())
else
menu:addOption("ID: " .. useThing:getId())
end
end
menu:display(menuPosition)

View File

@@ -4,7 +4,7 @@ Module
author: OTClient team
website: https://github.com/edubart/otclient
sandboxed: true
scripts: [ widgets/uigamemap, widgets/uiitem, gameinterface ]
scripts: [ widgets/uigamemap, gameinterface ]
load-later:
- game_hotkeys
- game_questlog
@@ -32,6 +32,8 @@ Module
- game_unjustifiedpoints
- game_walking
- game_shop
- game_itemselector
- game_textedit
- game_bot
@onLoad: init()
@onUnload: terminate()

View File

@@ -200,6 +200,7 @@ function refresh()
if player then
onSoulChange(player, player:getSoul())
onFreeCapacityChange(player, player:getFreeCapacity())
onStatesChange(player, player:getStates(), 0)
end
--purseButton:setVisible(g_game.getFeature(GamePurseSlot))

View File

@@ -0,0 +1,73 @@
local activeWindow
function init()
g_ui.importStyle('itemselector')
connect(g_game, { onGameEnd = destroyWindow })
end
function terminate()
disconnect(g_game, { onGameEnd = destroyWindow })
destroyWindow()
end
function destroyWindow()
if activeWindow then
activeWindow:destroy()
activeWindow = nil
end
end
function show(itemWidget)
if not itemWidget then
return
end
if activeWindow then
destroyWindow()
end
local window = g_ui.createWidget('ItemSelectorWindow', rootWidget)
local destroy = function()
window:destroy()
if window == activeWindow then
activeWindow = nil
end
end
local doneFunc = function()
itemWidget:setItemId(window.item:getItemId())
itemWidget:setItemCount(window.item:getItemCount())
if itemWidget.onItemChange then
itemWidget:onItemChange()
end
destroy()
end
window.okButton.onClick = doneFunc
window.cancelButton.onClick = destroy
window.onEnter = doneFunc
window.onEscape = destroy
window.item:setItemId(itemWidget:getItemId())
window.item:setItemCount(itemWidget:getItemCount())
window.itemId:setValue(itemWidget:getItemId())
if itemWidget:getItemCount() > 1 then
window.itemCount:setValue(itemWidget:getItemCount())
end
window.itemId.onValueChange = function(widget, value)
window.item:setItemId(value)
end
window.itemCount.onValueChange = function(widget, value)
window.item:setItemCount(value)
end
activeWindow = window
activeWindow:raise()
activeWindow:focus()
end
function hide()
destroyWindow()
end

View File

@@ -0,0 +1,10 @@
Module
name: game_itemselector
description: Allow to select item
author: OTClientV8
website: https://github.com/OTCv8/otclientv8
sandboxed: true
dependencies: [ game_interface ]
scripts: [ itemselector ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -0,0 +1,65 @@
ItemSelectorWindow < MainWindow
id: itemSelector
size: 260 120
!text: tr("Select item")
Item
id: item
virtual: true
size: 32 32
margin-top: 10
anchors.top: parent.top
anchors.left: parent.left
SpinBox
id: itemId
anchors.top: parent.top
anchors.left: prev.right
margin-top: 15
margin-left: 5
padding-left: 5
width: 70
minimum: 1
maximum: 999999999
focusable: true
Label
anchors.top: parent.top
anchors.left: prev.left
anchors.right: prev.right
text-align: center
!text: tr("Item ID")
SpinBox
id: itemCount
anchors.top: parent.top
anchors.left: prev.right
margin-top: 15
margin-left: 5
padding-left: 5
width: 120
minimum: 1
maximum: 100
focusable: true
Label
anchors.top: parent.top
anchors.left: prev.left
anchors.right: prev.right
text-align: center
!text: tr("Count / SubType")
Button
id: okButton
!text: tr('Ok')
anchors.bottom: parent.bottom
anchors.right: next.left
margin-right: 10
width: 60
Button
id: cancelButton
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 60

View File

@@ -0,0 +1,56 @@
local activeWindow
function init()
g_ui.importStyle('textedit')
connect(g_game, { onGameEnd = destroyWindow })
end
function terminate()
disconnect(g_game, { onGameEnd = destroyWindow })
destroyWindow()
end
function destroyWindow()
if activeWindow then
activeWindow:destroy()
activeWindow = nil
end
end
function show(widget)
if not widget then
return
end
if activeWindow then
destroyWindow()
end
local window = g_ui.createWidget('TextEditWindow', rootWidget)
local destroy = function()
window:destroy()
if window == activeWindow then
activeWindow = nil
end
end
local doneFunc = function()
widget:setText(window.text:getText())
destroy()
end
window.okButton.onClick = doneFunc
window.cancelButton.onClick = destroy
window.onEnter = doneFunc
window.onEscape = destroy
window.text:setText(widget:getText())
activeWindow = window
activeWindow:raise()
activeWindow:focus()
end
function hide()
destroyWindow()
end

View File

@@ -0,0 +1,10 @@
Module
name: game_textedit
description: Allow to edit text
author: OTClientV8
website: https://github.com/OTCv8/otclientv8
sandboxed: true
dependencies: [ game_interface ]
scripts: [ textedit ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -0,0 +1,25 @@
TextEditWindow < MainWindow
id: textedit
size: 260 105
!text: tr("Edit text")
TextEdit
id: text
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Button
id: okButton
!text: tr('Ok')
anchors.bottom: parent.bottom
anchors.right: next.left
margin-right: 10
width: 60
Button
id: cancelButton
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 60

View File

@@ -0,0 +1,133 @@
function UIItem:onDragEnter(mousePos)
if self:isVirtual() then return false end
local item = self:getItem()
if not item then return false end
self:setBorderWidth(1)
self.currentDragThing = item
g_mouse.pushCursor('target')
return true
end
function UIItem:onDragLeave(droppedWidget, mousePos)
if self:isVirtual() then return false end
self.currentDragThing = nil
g_mouse.popCursor('target')
self:setBorderWidth(0)
self.hoveredWho = nil
return true
end
function UIItem:onDrop(widget, mousePos, forced)
if not self:canAcceptDrop(widget, mousePos) and not forced then return false end
local item = widget.currentDragThing
if not item or not item:isItem() then return false end
if self.selectable then
self:setItemId(item:getId())
self:setItemCount(item:getCount())
if item:getSubType() > 1 then
self:setItemSubType(item:getSubType())
end
if self.onItemChange then
self:onItemChange()
end
return
end
local toPos = self.position
local itemPos = item:getPosition()
if itemPos.x == toPos.x and itemPos.y == toPos.y and itemPos.z == toPos.z then return false end
if item:getCount() > 1 then
modules.game_interface.moveStackableItem(item, toPos)
else
g_game.move(item, toPos, 1)
end
self:setBorderWidth(0)
return true
end
function UIItem:onDestroy()
if self == g_ui.getDraggingWidget() and self.hoveredWho then
self.hoveredWho:setBorderWidth(0)
end
if self.hoveredWho then
self.hoveredWho = nil
end
end
function UIItem:onHoverChange(hovered)
UIWidget.onHoverChange(self, hovered)
if self:isVirtual() or not self:isDraggable() then return end
local draggingWidget = g_ui.getDraggingWidget()
if draggingWidget and self ~= draggingWidget then
local gotMap = draggingWidget:getClassName() == 'UIGameMap'
local gotItem = draggingWidget:getClassName() == 'UIItem' and not draggingWidget:isVirtual()
if hovered and (gotItem or gotMap) then
self:setBorderWidth(1)
draggingWidget.hoveredWho = self
else
self:setBorderWidth(0)
draggingWidget.hoveredWho = nil
end
end
end
function UIItem:onMouseRelease(mousePosition, mouseButton)
if self.cancelNextRelease then
self.cancelNextRelease = false
return true
end
if self:isVirtual() then return false end
local item = self:getItem()
if not item or not self:containsPoint(mousePosition) then return false end
if modules.client_options.getOption('classicControl') and
((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or
(g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then
g_game.look(item)
self.cancelNextRelease = true
return true
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, nil) then
return true
end
return false
end
function UIItem:canAcceptDrop(widget, mousePos)
if not self.selectable and (self:isVirtual() or not self:isDraggable()) then return false end
if not widget or not widget.currentDragThing then return false end
local children = rootWidget:recursiveGetChildrenByPos(mousePos)
for i=1,#children do
local child = children[i]
if child == self then
return true
elseif not child:isPhantom() then
return false
end
end
error('Widget ' .. self:getId() .. ' not in drop list.')
return false
end
function UIItem:onClick(mousePos)
if not self.selectable then
return
end
if modules.game_itemselector then
modules.game_itemselector.show(self)
end
end