Bot update - waypoints, attacking, looting

This commit is contained in:
OTCv8 2019-11-02 08:43:11 +01:00
parent 62ff2b1cf5
commit 6dd9a54749
15 changed files with 1640 additions and 63 deletions

View File

@ -42,7 +42,7 @@ function init()
connect(Creature, { connect(Creature, {
onAppear = botCreatureAppear, onAppear = botCreatureAppear,
onDisappear =botCreatureDisappear, onDisappear = botCreatureDisappear,
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange onHealthPercentChange = botCraetureHealthPercentChange
}) })
@ -50,6 +50,9 @@ function init()
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange onHealthPercentChange = botCraetureHealthPercentChange
}) })
connect(Container, { onOpen = botContainerOpen,
onClose = botContainerClose,
onUpdateItem = botContainerUpdateItem })
botConfigFile = g_configs.create("/bot.otml") botConfigFile = g_configs.create("/bot.otml")
local config = botConfigFile:get("config") local config = botConfigFile:get("config")
@ -165,6 +168,9 @@ function terminate()
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange onHealthPercentChange = botCraetureHealthPercentChange
}) })
disconnect(Container, { onOpen = botContainerOpen,
onClose = botContainerClose,
onUpdateItem = botContainerUpdateItem })
removeEvent(executeEvent) removeEvent(executeEvent)
removeEvent(checkMsgsEvent) removeEvent(checkMsgsEvent)
@ -324,6 +330,12 @@ function clearConfig()
botMessages:destroyChildren() botMessages:destroyChildren()
botMessages:updateLayout() botMessages:updateLayout()
for i, widget in pairs(g_ui.getRootWidget():getChildren()) do
if widget.botWidget then
widget:destroy()
end
end
end end
function refreshConfig() function refreshConfig()
@ -479,3 +491,18 @@ function botOnUseWith(pos, itemId, target, subType)
if compiledConfig == nil then return false end if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onUseWith(pos, itemId, target, subType) end) safeBotCall(function() compiledConfig.callbacks.onUseWith(pos, itemId, target, subType) end)
end end
function botContainerOpen(container, previousContainer)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onContainerOpen(container, previousContainer) end)
end
function botContainerClose(container)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onContainerClose(container) end)
end
function botContainerUpdateItem(container, slot, item)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onContainerUpdateItem(container, slot, item) end)
end

View File

@ -15,16 +15,15 @@ BotLabel < Label
text-align: center text-align: center
text-wrap: true text-wrap: true
BotItem < UIItem BotItem < Item
virtual: true virtual: true
size: 32 32
border: 1 black
&selectable: true &selectable: true
BotTextEdit < TextEdit BotTextEdit < TextEdit
@onClick: modules.game_textedit.show(self) @onClick: modules.game_textedit.show(self)
text-align: center text-align: center
multiline: false multiline: false
focusable: false
BotSeparator < HorizontalSeparator BotSeparator < HorizontalSeparator
margin-top: 5 margin-top: 5
@ -40,8 +39,7 @@ CaveBotLabel < Label
focusable: true focusable: true
$focus: $focus:
background-color: #00000033 background-color: #00000055
color: #ffffff
MiniWindow MiniWindow
id: botWindow id: botWindow

View File

@ -15,22 +15,28 @@ local battleTab = addTab("Battle")
local caveTab = addTab("Cave") local caveTab = addTab("Cave")
local toolsTab = addTab("Tools") local toolsTab = addTab("Tools")
Panels.Health(battleTab)
Panels.HealthItem(battleTab) Panels.HealthItem(battleTab)
Panels.ManaItem(battleTab) Panels.ManaItem(battleTab)
Panels.AttackSpell(battleTab)
local waypoints = Panels.Waypoints(caveTab) local waypoints = Panels.Waypoints(caveTab)
local attacking = Panels.Attacking(caveTab) local attacking = Panels.Attacking(caveTab)
local looting = Panels.Looting(caveTab) local looting = Panels.Looting(caveTab)
addButton("tutorial", "Help & Tutorials", function()
g_platform.openUrl("https://github.com/OTCv8/otclientv8_bot")
end, caveTab)
--#macros --#macros
addSeparator("sep1")
local helloLabel = addLabel("helloLabel", "") local helloLabel = addLabel("helloLabel", "")
macro(1000, "example macro (time)", nil, function() macro(1000, "example macro (time)", nil, function()
helloLabel:setText("Time from start: " .. now) helloLabel:setText("Time from start: " .. now)
end) end)
macro(1000, "this macro does nothing", nil, function() macro(1000, "this macro does nothing", "f7", function()
end, toolsTab) end, toolsTab)
@ -40,7 +46,7 @@ hotkey("f5", "example hotkey", function()
info("Wow, you clicked f5 hotkey") info("Wow, you clicked f5 hotkey")
end) end)
singlehotkey("ctrl+f6", "example hotkey2", function() singlehotkey("ctrl+f6", "singlehotkey", function()
info("Wow, you clicked f6 singlehotkey") info("Wow, you clicked f6 singlehotkey")
end) end)

View File

@ -25,7 +25,10 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
onCreaturePositionChange = {}, onCreaturePositionChange = {},
onCreatureHealthPercentChange = {}, onCreatureHealthPercentChange = {},
onUse = {}, onUse = {},
onUseWith = {} onUseWith = {},
onContainerOpen = {},
onContainerClose = {},
onContainerUpdateItem = {}
} }
-- basic functions & classes -- basic functions & classes
@ -48,8 +51,13 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
context.g_game = g_game context.g_game = g_game
context.g_map = g_map context.g_map = g_map
context.g_ui = g_ui context.g_ui = g_ui
context.g_platform = g_platform
context.g_sounds = g_sounds
context.g_window = g_window
context.g_mouse = g_mouse
context.StaticText = StaticText context.StaticText = StaticText
context.Position = Position context.Config = Config
context.HTTP = HTTP context.HTTP = HTTP
-- log functions -- log functions
@ -80,14 +88,24 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
for i, macro in ipairs(context._macros) do 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 macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage._macros[macro.name]) then
local status, result = pcall(function()
if macro.callback() then if macro.callback() then
macro.lastExecution = context.now macro.lastExecution = context.now
end end
end)
if not status then
context.error("Macro: " .. macro.name .. " execution error: " .. result)
end
end end
end end
while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
local status, result = pcall(function()
context._scheduler[1].callback() context._scheduler[1].callback()
end)
if not status then
context.error("Schedule execution error: " .. result)
end
table.remove(context._scheduler, 1) table.remove(context._scheduler, 1)
end end
end, end,
@ -182,6 +200,21 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
for i, callback in ipairs(context._callbacks.onUseWith) do for i, callback in ipairs(context._callbacks.onUseWith) do
callback(pos, itemId, target, subType) callback(pos, itemId, target, subType)
end end
end,
onContainerOpen = function(container, previousContainer)
for i, callback in ipairs(context._callbacks.onContainerOpen) do
callback(container, previousContainer)
end
end,
onContainerClose = function(container)
for i, callback in ipairs(context._callbacks.onContainerClose) do
callback(container)
end
end,
onContainerUpdateItem = function(container, slot, item)
for i, callback in ipairs(context._callbacks.onContainerUpdateItem) do
callback(container, slot, item)
end
end end
} }
} }

View File

@ -79,8 +79,22 @@ context.onUseWith = function(callback)
return context.callback("onUseWith", callback) return context.callback("onUseWith", callback)
end end
-- onContainerOpen -- callback = function(container, previousContainer)
context.onContainerOpen = function(callback)
return context.callback("onContainerOpen", callback)
end
-- custom callbacks -- onContainerUpdateItem -- callback = function(container)
context.onContainerClose = function(callback)
return context.callback("onContainerClose", callback)
end
-- onContainerUpdateItem -- callback = function(container, slot, item)
context.onContainerUpdateItem = function(callback)
return context.callback("onContainerUpdateItem", callback)
end
-- CUSTOM CALLBACKS
-- listen(name, callback) -- callback = function(text, channelId, pos) -- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback) context.listen = function(name, callback)

View File

@ -36,7 +36,7 @@ context.macro = function(timeout, name, hotkey, callback, parent)
local switch = nil local switch = nil
if name:len() > 0 then if name:len() > 0 then
if context.storage._macros[name] == nil then if context.storage._macros[name] == nil then
context.storage._macros[name] = true context.storage._macros[name] = false
end end
switch = context._addMacroSwitch(name, hotkey, parent) switch = context._addMacroSwitch(name, hotkey, parent)
end end

View File

@ -10,6 +10,19 @@ context.getSpectators = function(multifloor)
return g_map.getSpectators(context.player:getPosition(), multifloor) return g_map.getSpectators(context.player:getPosition(), multifloor)
end end
context.getCreatureById = function(id, multifloor)
if type(id) ~= 'number' then return nil end
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getId() == id then
return spec
end
end
return nil
end
context.getCreatureByName = function(name, multifloor) context.getCreatureByName = function(name, multifloor)
if not name then return nil end if not name then return nil end
name = name:lower() name = name:lower()
@ -70,6 +83,10 @@ context.autoWalk = function(destination, maxDist, ignoreFields, ignoreCreatures)
if #path < 1 then if #path < 1 then
return false return false
end end
if g_game.getFeature(GameNewWalking) then
g_game.autoWalk(path, context.player:getPosition()) g_game.autoWalk(path, context.player:getPosition())
else
g_game.autoWalk(path, {x=0,y=0,z=0})
end
return true return true
end end

View File

@ -5,6 +5,7 @@ context.setupUI = function(otml, parent)
parent = context.panel parent = context.panel
end end
local widget = g_ui.loadUIFromString(otml, parent) local widget = g_ui.loadUIFromString(otml, parent)
widget.botWidget = true
return widget return widget
end end

View File

@ -1,8 +1,334 @@
local context = G.botContext local context = G.botContext
local Panels = context.Panels local Panels = context.Panels
Panels.MonsterEditor = function(monster, config, callback, parent)
local otherWindow = g_ui.getRootWidget():getChildById('monsterEditor')
if otherWindow then
otherWindow:destory()
end
local window = context.setupUI([[
MainWindow
id: monsterEditor
size: 400 300
!text: tr("Edit monster")
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
text: Use monster name * for any other monster not on the list
TextEdit
id: name
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-left: 100
margin-top: 5
multiline: false
Label
anchors.verticalCenter: prev.verticalCenter
anchors.left: parent.left
text: Monster name:
Label
id: priorityText
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-right: 10
margin-top: 10
text: Priority
text-align: center
HorizontalScrollBar
id: priority
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 10
step: 1
Label
id: dangerText
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-right: 10
margin-top: 10
text: Danger
text-align: center
HorizontalScrollBar
id: danger
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 10
step: 1
Label
id: distanceText
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-right: 10
margin-top: 10
text: Distance
text-align: center
HorizontalScrollBar
id: distance
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 5
step: 1
Label
id: minHealthText
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-right: 10
margin-top: 10
text: Minimum Health
text-align: center
HorizontalScrollBar
id: minHealth
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 100
step: 1
Label
id: maxHealthText
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-right: 10
margin-top: 10
text: Maximum Health
text-align: center
HorizontalScrollBar
id: maxHealth
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 100
step: 1
BotSwitch
id: attack
anchors.left: parent.horizontalCenter
anchors.top: name.bottom
margin-left: 10
margin-top: 10
width: 55
text: Attack
BotSwitch
id: ignore
anchors.left: prev.right
anchors.top: name.bottom
margin-left: 5
margin-top: 10
width: 55
text: Ignore
BotSwitch
id: avoid
anchors.left: prev.right
anchors.top: name.bottom
margin-left: 5
margin-top: 10
width: 55
text: Avoid
BotSwitch
id: keepDistance
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: prev.bottom
margin-left: 10
margin-top: 10
text: Keep distance
BotSwitch
id: avoidAttacks
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: prev.bottom
margin-left: 10
margin-top: 10
text: Avoid monster attacks
BotSwitch
id: chase
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: prev.bottom
margin-left: 10
margin-top: 10
text: Chase when running away
BotSwitch
id: loot
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: prev.bottom
margin-left: 10
margin-top: 10
text: Loot corpse
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
]], g_ui.getRootWidget())
local destroy = function()
window:destroy()
end
local doneFunc = function()
local monster = window.name:getText()
local config = {
priority = window.priority:getValue(),
danger = window.danger:getValue(),
distance = window.distance:getValue(),
minHealth = window.minHealth:getValue(),
maxHealth = window.maxHealth:getValue(),
attack = window.attack:isOn(),
ignore = window.ignore:isOn(),
avoid = window.avoid:isOn(),
keepDistance = window.keepDistance:isOn(),
avoidAttacks = window.avoidAttacks:isOn(),
chase = window.chase:isOn(),
loot = window.loot:isOn()
}
destroy()
callback(monster, config)
end
window.okButton.onClick = doneFunc
window.cancelButton.onClick = destroy
window.onEnter = doneFunc
window.onEscape = destroy
window.priority.onValueChange = function(scroll, value)
window.priorityText:setText("Priority: " .. value)
end
window.danger.onValueChange = function(scroll, value)
window.dangerText:setText("Danger: " .. value)
end
window.distance.onValueChange = function(scroll, value)
window.distanceText:setText("Distance: " .. value)
end
window.minHealth.onValueChange = function(scroll, value)
window.minHealthText:setText("Minimum health: " .. value .. "%")
end
window.maxHealth.onValueChange = function(scroll, value)
window.maxHealthText:setText("Maximum health: " .. value .. "%")
end
window.priority:setValue(config.priority or 1)
window.danger:setValue(config.danger or 1)
window.distance:setValue(config.distance or 1)
window.minHealth:setValue(1) -- to force onValueChange update
window.maxHealth:setValue(1) -- to force onValueChange update
window.minHealth:setValue(config.minHealth or 0)
window.maxHealth:setValue(config.maxHealth or 100)
window.attack.onClick = function(widget)
if widget:isOn() then
return
end
widget:setOn(true)
window.ignore:setOn(false)
window.avoid:setOn(false)
end
window.ignore.onClick = function(widget)
if widget:isOn() then
return
end
widget:setOn(true)
window.attack:setOn(false)
window.avoid:setOn(false)
end
window.avoid.onClick = function(widget)
if widget:isOn() then
return
end
widget:setOn(true)
window.attack:setOn(false)
window.ignore:setOn(false)
end
window.attack:setOn(config.attack)
window.ignore:setOn(config.ignore)
window.avoid:setOn(config.avoid)
if not window.attack:isOn() and not window.ignore:isOn() and not window.avoid:isOn() then
window.attack:setOn(true)
end
window.keepDistance.onClick = function(widget)
widget:setOn(not widget:isOn())
end
window.avoidAttacks.onClick = function(widget)
widget:setOn(not widget:isOn())
end
window.chase.onClick = function(widget)
widget:setOn(not widget:isOn())
end
window.loot.onClick = function(widget)
widget:setOn(not widget:isOn())
end
window.keepDistance:setOn(config.keepDistance)
window.avoidAttacks:setOn(config.avoidAttacks)
window.chase:setOn(config.chase)
window.loot:setOn(config.loot)
if config.loot == nil then
window.loot:setOn(true)
end
window.name:setText(monster)
window:show()
window:raise()
window:focus()
end
Panels.Attacking = function(parent) Panels.Attacking = function(parent)
context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: attacking id: attacking
height: 150 height: 150
@ -13,7 +339,649 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Attacking text: Attacking
ComboBox
id: config
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 5
text-offset: 3 0
width: 130
Button
id: enableButton
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
Button
margin-top: 1
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 60
height: 20
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 60
height: 20
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 60
height: 20
TextList
id: list
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
vertical-scrollbar: listScrollbar
margin-right: 15
margin-top: 2
height: 60
focusable: false
auto-focus: first
VerticalScrollBar
id: listScrollbar
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
pixels-scroll: true
step: 5
Button
margin-top: 2
id: mAdd
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 60
height: 20
Button
id: mEdit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 60
height: 20
Button
id: mRemove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 60
height: 20
]], parent) ]], parent)
if type(context.storage.attacking) ~= "table" then
context.storage.attacking = {}
end
if type(context.storage.attacking.configs) ~= "table" then
context.storage.attacking.configs = {}
end
local getConfigName = function(config)
local matches = regexMatch(config, [[name:\s*([^\n]*)$]])
if matches[1] and matches[1][2] then
return matches[1][2]:trim()
end
return nil
end
local commands = {}
local monsters = {}
local configName = nil
local refreshConfig = nil -- declared later
local createNewConfig = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
local newConfig = ""
if configName ~= nil then
newConfig = "name:" .. configName .. "\n"
end
for monster, config in pairs(monsters) do
newConfig = newConfig .. "\n" .. monster .. ":" .. json.encode(config) .. "\n"
end
context.storage.attacking.configs[context.storage.attacking.activeConfig] = newConfig
refreshConfig()
end
local parseConfig = function(config)
commands = {}
monsters = {}
configName = nil
local matches = regexMatch(config, [[([^:^\n]+)(:?)([^\n]*)]])
for i=1,#matches do
local command = matches[i][2]
local validation = (matches[i][3] == ":")
local text = matches[i][4]
if validation then
table.insert(commands, {command=command:lower(), text=text})
elseif #commands > 0 then
commands[#commands].text = commands[#commands].text .. "\n" .. matches[i][1]
end
end
local labels = {}
for i, command in ipairs(commands) do
if commands[i].command == "name" then
configName = commands[i].text
else
local status, result = pcall(function() return json.decode(command.text) end)
if not status then
context.error("Invalid monster config: " .. commands[i].command .. ", error: " .. result)
print(command.text)
else
monsters[commands[i].command] = result
table.insert(labels, commands[i].command)
end
end
end
table.sort(labels)
for i, text in ipairs(labels) do
local label = g_ui.createWidget("CaveBotLabel", ui.list)
label:setText(text)
end
end
local ignoreOnOptionChange = true
refreshConfig = function(scrollDown)
ignoreOnOptionChange = true
if context.storage.attacking.enabled then
ui.enableButton:setText("On")
ui.enableButton:setColor('#00AA00FF')
else
ui.enableButton:setText("Off")
ui.enableButton:setColor('#FF0000FF')
end
ui.config:clear()
for i, config in ipairs(context.storage.attacking.configs) do
local name = getConfigName(config)
if not name then
name = "Unnamed config"
end
ui.config:addOption(name)
end
if not context.storage.attacking.activeConfig and #context.storage.attacking.configs > 0 then
context.storage.attacking.activeConfig = 1
end
ui.list:destroyChildren()
if context.storage.attacking.activeConfig and context.storage.attacking.configs[context.storage.attacking.activeConfig] then
ui.config:setCurrentIndex(context.storage.attacking.activeConfig)
parseConfig(context.storage.attacking.configs[context.storage.attacking.activeConfig])
end
context.saveConfig()
if scrollDown and ui.list:getLastChild() then
ui.list:focusChild(ui.list:getLastChild())
end
ignoreOnOptionChange = false
end
ui.config.onOptionChange = function(widget)
if not ignoreOnOptionChange then
context.storage.attacking.activeConfig = widget.currentIndex
refreshConfig()
end
end
ui.enableButton.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
context.storage.attacking.enabled = not context.storage.attacking.enabled
refreshConfig()
end
ui.add.onClick = function()
modules.game_textedit.multilineEditor("Target list editor", "name:Config name", function(newText)
table.insert(context.storage.attacking.configs, newText)
context.storage.attacking.activeConfig = #context.storage.attacking.configs
refreshConfig()
end)
end
ui.edit.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
modules.game_textedit.multilineEditor("Target list editor", context.storage.attacking.configs[context.storage.attacking.activeConfig], function(newText)
context.storage.attacking.configs[context.storage.attacking.activeConfig] = newText
refreshConfig()
end)
end
ui.remove.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
context.storage.attacking.enabled = false
table.remove(context.storage.attacking.configs, context.storage.attacking.activeConfig)
context.storage.attacking.activeConfig = 0
refreshConfig()
end
ui.mAdd.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
Panels.MonsterEditor("", {}, function(name, config)
if name:len() > 0 then
monsters[name] = config
end
createNewConfig()
end, parent)
end
ui.mEdit.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
local monsterWidget = ui.list:getFocusedChild()
if not monsterWidget or not monsters[monsterWidget:getText()] then
return
end
Panels.MonsterEditor(monsterWidget:getText(), monsters[monsterWidget:getText()], function(name, config)
monsters[monsterWidget:getText()] = nil
if name:len() > 0 then
monsters[name] = config
end
createNewConfig()
end, parent)
end
ui.mRemove.onClick = function()
if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then
return
end
local monsterWidget = ui.list:getFocusedChild()
if not monsterWidget or not monsters[monsterWidget:getText()] then
return
end
monsters[monsterWidget:getText()] = nil
createNewConfig()
end
refreshConfig()
local getMonsterConfig = function(monster)
if monsters[monster:getName():lower()] then
return monsters[monster:getName():lower()]
end
return monsters["*"]
end
local calculatePriority = function(monster)
local priority = 0
local config = getMonsterConfig(monster)
if not config or type(config.priority) ~= 'number' then
return -1
end
if not config.attack then
return -1
end
local distance = context.getDistanceBetween(context.player:getPosition(), monster:getPosition())
if distance > 10 then
return -1
end
local mpos = monster:getPosition()
local hasPath = false
for x=-1,1 do
for y=-1,1 do
local pathTo = context.findPath(context.player:getPosition(), {x=mpos.x-x, y=mpos.y-y, z=mpos.z}, 100, true, false)
if #pathTo > 0 then
hasPath = true
break
end
end
end
if distance > 2 and not hasPath then
return -1
end
if monster == g_game.getAttackingCreature() then
priority = priority + 10
end
if distance <= 4 then
priority = priority + 10
end
if distance <= 2 then
priority = priority + 20
end
if monster:getHealthPercent() <= 10 then
priority = priority + 10
end
if monster:getHealthPercent() <= 25 then
priority = priority + 10
end
if monster:getHealthPercent() <= 50 then
priority = priority + 10
end
if monster:getHealthPercent() <= 75 then
priority = priority + 10
end
priority = priority + config.priority * 10
return priority
end
local calculateMonsterDanger = function(monster)
local danger = 0
local config = getMonsterConfig(monster)
if not config or type(config.danger) ~= 'number' then
return danger
end
danger = danger + config.danger
return danger
end
local lastAttack = context.now
local lootContainers = {}
local lootTries = 0
local openContainerRequest = 0
local waitForLooting = 0
local goForLoot = function()
if #lootContainers == 0 or not context.storage.looting.enabled then
return false
end
local pos = context.player:getPosition()
table.sort(lootContainers, function(pos1, pos2)
local dist1 = math.max(math.abs(pos.x-pos1.x), math.abs(pos.y-pos1.y))
local dist2 = math.max(math.abs(pos.x-pos2.x), math.abs(pos.y-pos2.y))
return dist1 < dist2
end)
local cpos = lootContainers[1]
if cpos.z ~= pos.z then
table.remove(lootContainers, 1)
return true
end
if lootTries >= 5 then
lootTries = 0
table.remove(lootContainers, 1)
return true
end
local dist = math.max(math.abs(pos.x-cpos.x), math.abs(pos.y-cpos.y))
if dist <= 5 then
local tile = g_map.getTile(cpos)
if not tile then
table.remove(lootContainers, 1)
return true
end
local topItem = tile:getTopUseThing()
if not topItem:isContainer() then
table.remove(lootContainers, 1)
return true
end
if dist <= 1 then
lootTries = lootTries + 1
openContainerRequest = context.now
g_game.open(topItem)
waitForLooting = math.max(waitForLooting, context.now + 500)
return true
end
end
if dist <= 20 then
if context.player:isWalking() then
return true
end
lootTries = lootTries + 1
if context.autoWalk(cpos, 100 + dist * 2) then
return true
end
if context.autoWalk(cpos, 100 + dist * 2, true) then
return true
end
for i=1,5 do
local cpos2 = {x=cpos.x + math.random(-1, 1),y = cpos.y + math.random(-1, 1), z = cpos.z}
if context.autoWalk(cpos2, 100 + dist * 2) then
return true
end
end
-- try again, ignore field
for i=1,5 do
local cpos2 = {x=cpos.x + math.random(-1, 1),y = cpos.y + math.random(-1, 1), z = cpos.z}
if context.autoWalk(cpos2, 100 + dist * 2, true) then
return true
end
end
-- ignore fields and monsters
if context.autoWalk(cpos, 100 + dist * 2, true, true) then
return true
end
else
table.remove(lootContainers, 1)
return false
end
return true
end
context.onCreatureDisappear(function(creature)
if not creature:isMonster() then
return
end
local pos = context.player:getPosition()
local tpos = creature:getPosition()
if tpos.z ~= pos.z then
return
end
local config = getMonsterConfig(creature)
if not config or not config.loot then
return
end
local distance = math.max(math.abs(pos.x-tpos.x), math.abs(pos.y-tpos.y))
if distance > 6 then
return
end
local tile = g_map.getTile(tpos)
if not tile then
return
end
local topItem = tile:getTopUseThing()
if not topItem:isContainer() then
return
end
table.insert(lootContainers, tpos)
end)
context.onContainerOpen(function(container, prevContainer)
lootTries = 0
if not context.storage.attacking.enabled then
return
end
if openContainerRequest + 500 > context.now and #lootContainers > 0 then
waitForLooting = math.max(waitForLooting, context.now + 1000 + container:getItemsCount() * 100)
table.remove(lootContainers, 1)
end
if prevContainer then
container.autoLooting = prevContainer.autoLooting
else
container.autoLooting = (openContainerRequest + 3000 > context.now)
end
end)
context.macro(200, function()
if not context.storage.attacking.enabled then
return
end
local attacking = nil
local following = nil
local attackingCandidate = g_game.getAttackingCreature()
local followingCandidate = g_game.getFollowingCreature()
local spectators = context.getSpectators()
local monsters = {}
local danger = 0
for i, spec in ipairs(spectators) do
if attackingCandidate and attackingCandidate:getId() == spec:getId() then
attacking = spec
end
if followingCandidate and followingCandidate:getId() == spec:getId() then
following = spec
end
if spec:isMonster() then
danger = danger + calculateMonsterDanger(spec)
spec.attackingPriority = calculatePriority(spec)
table.insert(monsters, spec)
end
end
if following then
return
end
if waitForLooting > context.now then
return
end
if #monsters == 0 then
goForLoot()
return
end
table.sort(monsters, function(a, b)
return a.attackingPriority > b.attackingPriority
end)
local target = monsters[1]
if target.attackingPriority < 0 then
return
end
local pos = context.player:getPosition()
local tpos = target:getPosition()
local config = getMonsterConfig(target)
local offsetX = pos.x - tpos.x
local offsetY = pos.y - tpos.y
if target ~= attacking then
g_game.attack(target)
attacking = target
lastAttack = context.now
end
-- proceed attack
if lastAttack + 15000 < context.now then
-- stop and attack again, just in case
g_game.cancelAttack()
g_game.attack(target)
lastAttack = context.now
return
end
if danger < 8 then
-- low danger, go for loot first
if goForLoot() then
return
end
end
local distance = math.max(math.abs(offsetX), math.abs(offsetY))
if config.keepDistance then
if (distance == config.distance or distance == config.distance + 1) then
return
else
local bestDist = 10
local bestPos = pos
for i=1,5 do
local testPos = {x=pos.x + math.random(-3,3), y=pos.y + math.random(-3,3), z=pos.z}
local dist = math.abs(config.distance - math.max(math.abs(tpos.x - testPos.x), math.abs(tpos.y - testPos.y)))
if dist < bestDist then
local path = context.findPath(pos, testPos, 100, false, false)
if #path > 0 then
bestPos = testPos
bestDist = dist
end
end
end
if bestDist > 1 then
for i=1,10 do
local testPos = {x=pos.x + math.random(-4,4), y=pos.y + math.random(-4,4), z=pos.z}
local dist = math.abs(config.distance - math.max(math.abs(tpos.x - testPos.x), math.abs(tpos.y - testPos.y)))
if dist < bestDist then
local path = context.findPath(pos, testPos, 100, true, false)
if #path > 0 then
bestPos = testPos
bestDist = dist
end
end
end
end
if bestDist < 10 then
context.autoWalk(bestPos, 100, true, false)
context.delay(300)
end
end
return
end
if config.avoidAttacks and distance <= 1 then
if (offsetX == 0 and offsetY ~= 0) then
if context.player:canWalk(Directions.East) then
g_game.walk(Directions.East)
elseif context.player:canWalk(Directions.West) then
g_game.walk(Directions.West)
end
elseif (offsetX ~= 0 and offsetY == 0) then
if context.player:canWalk(Directions.North) then
g_game.walk(Directions.North)
elseif context.player:canWalk(Directions.South) then
g_game.walk(Directions.South)
end
end
end
if distance > 1 then
for x=-1,1 do
for y=-1,1 do
if context.autoWalk({x=tpos.x-x, y=tpos.y-y, z=tpos.z}, 100, true, false) then
return
end
end
end
if not context.autoWalk(tpos, 100, false, true) then
context.autoWalk(tpos, 100, true, true)
end
end
end)
end end

View File

@ -17,7 +17,7 @@ end
Panels.ManaShield = function(parent) Panels.ManaShield = function(parent)
context.macro(500, "Auto ManaShield", nil, function() context.macro(500, "Auto ManaShield", nil, function()
if not context.hasManaShield() then if not context.hasManaShield() then
if context.saySpell("utamo vita", 1000) then if context.saySpell("utamo vita", 500) then
context.delay(5000) context.delay(5000)
end end
end end
@ -323,3 +323,16 @@ Panels.Turning = function(parent)
end end
Panels.AntiIdle = Panels.Turning Panels.AntiIdle = Panels.Turning
Panels.AttackSpell = function(parent)
context.macro(500, "Auto attack spell", nil, function()
local target = g_game.getAttackingCreature()
if target and context.getCreatureById(target:getId()) and context.storage.autoAttackText:len() > 0 then
if context.saySpell(context.storage.autoAttackText, 1000) then
context.delay(1000)
end
end
end, parent)
context.addTextEdit("autoAttackText", context.storage.autoAttackText or "exori vis", function(widget, text)
context.storage.autoAttackText = text
end, parent)
end

View File

@ -1,12 +1,11 @@
local context = G.botContext local context = G.botContext
local Panels = context.Panels local Panels = context.Panels
Panels.Looting = function(parent) Panels.Looting = function(parent)
context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: looting id: looting
height: 150 height: 190
BotLabel BotLabel
anchors.top: parent.top anchors.top: parent.top
@ -14,7 +13,436 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Looting text: Looting
ComboBox
id: config
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 5
text-offset: 3 0
width: 130
Button
id: enableButton
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
Button
margin-top: 1
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 60
height: 20
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 60
height: 20
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 60
height: 20
ScrollablePanel
id: items
anchors.top: prev.bottom
anchors.right: parent.right
anchors.left: parent.left
vertical-scrollbar: scrollBar
margin-right: 10
margin-top: 2
height: 70
layout:
type: grid
cell-size: 34 34
flow: true
VerticalScrollBar
id: scrollBar
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true
BotLabel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 4
text: Loot Containers
Panel
id: containers
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 33
margin-top: 2
BotItem
id: item1
anchors.top: parent.top
anchors.left: parent.left
margin-left: 3
BotItem
id: item2
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item3
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item4
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item5
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
]], parent) ]], parent)
local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 }
if type(context.storage.looting) ~= "table" then
context.storage.looting = {}
end
if type(context.storage.looting.configs) ~= "table" then
context.storage.looting.configs = {}
end
local getConfigName = function(config)
local matches = regexMatch(config, [[name:\s*([^\n]*)$]])
if matches[1] and matches[1][2] then
return matches[1][2]:trim()
end
return nil
end
local items = {}
local itemsByKey = {}
local containers = {}
local commands = {}
local refreshConfig = nil -- declared later
local createNewConfig = function(focusedWidget)
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
local tmpItems = {}
local tmpContainers = {}
local focusIndex = 0
local newConfig = ""
for i, text in ipairs(commands) do
newConfig = newConfig .. text .. "\n"
end
for i=1,ui.items:getChildCount() do
local widget = ui.items:getChildByIndex(i)
if widget and widget:getItemId() >= 100 then
if tmpItems[widget:getItemId()] == nil then
tmpItems[widget:getItemId()] = 1
newConfig = newConfig .. "\n" .. widget:getItemId()
end
end
if widget == focusedWidget then
focusIndex = i
end
end
for i, widget in ipairs(lootContainers) do
if widget:getItemId() >= 100 then
if tmpContainers[widget:getItemId()] == nil then
tmpContainers[widget:getItemId()] = 1 -- remove duplicates
newConfig = newConfig .. "\ncontainer:" .. widget:getItemId()
end
end
end
context.storage.looting.configs[context.storage.looting.activeConfig] = newConfig
refreshConfig(focusIndex)
end
local parseConfig = function(config)
items = {}
itemsByKey = {}
containers = {}
commands = {}
local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
for i=1,#matches do
local command = matches[i][2]
local validation = (matches[i][3] == ":")
local text = matches[i][4]
local commandAsNumber = tonumber(command)
local textAsNumber = tonumber(text)
if commandAsNumber and commandAsNumber >= 100 then
table.insert(items, commandAsNumber)
itemsByKey[commandAsNumber] = 1
elseif command == "container" and validation and textAsNumber and textAsNumber >= 100 then
containers[textAsNumber] = 1
elseif validation then
table.insert(commands, command .. ":" .. text)
end
end
local itemsToShow = #items + 2
if itemsToShow % 5 ~= 0 then
itemsToShow = itemsToShow + 5 - itemsToShow % 5
end
if itemsToShow < 10 then
itemsToShow = 10
end
for i=1,itemsToShow do
local widget = g_ui.createWidget("BotItem", ui.items)
local itemId = 0
if i <= #items then
itemId = items[i]
end
widget:setItemId(itemId)
widget.onItemChange = createNewConfig
end
for i, widget in ipairs(lootContainers) do
widget:setItemId(0)
end
local containerIndex = 1
for containerId, i in pairs(containers) do
if lootContainers[containerIndex] then
lootContainers[containerIndex]:setItemId(containerId)
end
containerIndex = containerIndex + 1
end
for i, widget in ipairs(lootContainers) do
widget.onItemChange = createNewConfig
end
end
local ignoreOnOptionChange = true
refreshConfig = function(focusIndex)
ignoreOnOptionChange = true
if context.storage.looting.enabled then
ui.enableButton:setText("On")
ui.enableButton:setColor('#00AA00FF')
else
ui.enableButton:setText("Off")
ui.enableButton:setColor('#FF0000FF')
end
ui.config:clear()
for i, config in ipairs(context.storage.looting.configs) do
local name = getConfigName(config)
if not name then
name = "Unnamed config"
end
ui.config:addOption(name)
end
if not context.storage.looting.activeConfig and #context.storage.looting.configs > 0 then
context.storage.looting.activeConfig = 1
end
ui.items:destroyChildren()
for i, widget in ipairs(lootContainers) do
widget.onItemChange = nil
widget:setItemId(0)
widget:setItemCount(0)
end
if context.storage.looting.activeConfig and context.storage.looting.configs[context.storage.looting.activeConfig] then
ui.config:setCurrentIndex(context.storage.looting.activeConfig)
parseConfig(context.storage.looting.configs[context.storage.looting.activeConfig])
end
context.saveConfig()
if focusIndex and focusIndex > 0 and ui.items:getChildByIndex(focusIndex) then
ui.items:focusChild(ui.items:getChildByIndex(focusIndex))
end
ignoreOnOptionChange = false
end
ui.config.onOptionChange = function(widget)
if not ignoreOnOptionChange then
context.storage.looting.activeConfig = widget.currentIndex
refreshConfig()
end
end
ui.enableButton.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = not context.storage.looting.enabled
refreshConfig()
end
ui.add.onClick = function()
modules.game_textedit.multilineEditor("Looting editor", "name:Config name", function(newText)
table.insert(context.storage.looting.configs, newText)
context.storage.looting.activeConfig = #context.storage.looting.configs
refreshConfig()
end)
end
ui.edit.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
modules.game_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText)
context.storage.looting.configs[context.storage.looting.activeConfig] = newText
refreshConfig()
end)
end
ui.remove.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = false
table.remove(context.storage.looting.configs, context.storage.looting.activeConfig)
context.storage.looting.activeConfig = 0
refreshConfig()
end
refreshConfig()
context.onContainerOpen(function(container, prevContainer)
if context.storage.attacking.enabled then
return
end
if prevContainer then
container.autoLooting = prevContainer.autoLooting
else
container.autoLooting = true
end
end)
context.macro(200, function()
if not context.storage.looting.enabled then
return
end
local candidates = {}
local lootContainersCandidates = {}
for containerId, container in pairs(g_game.getContainers()) do
local containerItem = container:getContainerItem()
if container.autoLooting and container:getItemsCount() > 0 and (not containerItem or containers[containerItem:getId()] == nil) then
table.insert(candidates, container)
elseif containerItem and containers[containerItem:getId()] ~= nil then
table.insert(lootContainersCandidates, container)
end
end
if #lootContainersCandidates == 0 then
for slot = InventorySlotFirst, InventorySlotLast do
local item = context.getInventoryItem(slot)
if item and item:isContainer() and containers[item:getId()] ~= nil then
table.insert(lootContainersCandidates, item)
end
end
if #lootContainersCandidates > 0 then
-- try to open inventory backpack
local target = lootContainersCandidates[math.random(1,#lootContainersCandidates)]
g_game.open(target, nil)
context.delay(200)
end
return
end
if #candidates == 0 then
return
end
local container = candidates[math.random(1,#candidates)]
local nextContainers = {}
local foundItem = nil
for i, item in ipairs(container:getItems()) do
if item:isContainer() then
table.insert(nextContainers, item)
elseif itemsByKey[item:getId()] ~= nil then
foundItem = item
break
end
end
-- found item to loot
if foundItem then
-- find backpack for it, first backpack with same items
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() or foundItem:isStackable() then -- has space
for j, item in ipairs(container:getItems()) do
if item:getId() == foundItem:getId() then
if foundItem:isStackable() then
if item:getCount() ~= 100 then
g_game.move(foundItem, container:getSlotPosition(j), foundItem:getCount())
return
end
else
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
end
end
end
-- now any backpack with empty slot
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() then -- has space
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
-- can't find backpack, try to open new
for i, container in ipairs(lootContainersCandidates) do
local candidates = {}
for j, item in ipairs(container:getItems()) do
if item:isContainer() and containers[item:getId()] ~= nil then
table.insert(candidates, item)
end
end
if #candidates > 0 then
g_game.open(candidates[math.random(1,#candidates)], container)
return
end
-- full, close it
g_game.close(container)
return
end
return
end
-- open remaining containers
if #nextContainers == 0 then
return
end
local delay = 1
for i=2,#nextContainers do
-- if more than 1 container, open them in new window
context.schedule(delay, function()
g_game.open(nextContainers[i], nil)
end)
delay = delay + 250
end
context.schedule(delay, function()
g_game.open(nextContainers[1], container)
end)
context.delay(150 + delay)
end)
end end

View File

@ -5,7 +5,7 @@ Panels.Waypoints = function(parent)
local ui = context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: waypoints id: waypoints
height: 213 height: 203
BotLabel BotLabel
anchors.top: parent.top anchors.top: parent.top
@ -35,6 +35,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
text: Add text: Add
width: 60 width: 60
height: 20
Button Button
id: edit id: edit
@ -42,6 +43,7 @@ Panel
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Edit text: Edit
width: 60 width: 60
height: 20
Button Button
id: remove id: remove
@ -49,6 +51,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Remove text: Remove
width: 60 width: 60
height: 20
TextList TextList
id: list id: list
@ -85,6 +88,7 @@ Panel
text: Goto text: Goto
width: 61 width: 61
margin-top: 1 margin-top: 1
height: 20
Button Button
id: wUse id: wUse
@ -92,6 +96,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Use text: Use
width: 61 width: 61
height: 20
Button Button
id: wUseWith id: wUseWith
@ -99,6 +104,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: UseWith text: UseWith
width: 61 width: 61
height: 20
Button Button
id: wWait id: wWait
@ -107,6 +113,7 @@ Panel
text: Wait text: Wait
width: 61 width: 61
margin-top: 1 margin-top: 1
height: 20
Button Button
id: wSay id: wSay
@ -114,6 +121,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Say text: Say
width: 61 width: 61
height: 20
Button Button
id: wFunction id: wFunction
@ -121,6 +129,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Function text: Function
width: 61 width: 61
height: 20
BotSwitch BotSwitch
id: recording id: recording
@ -128,6 +137,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
text: Auto Recording text: Auto Recording
height: 20
]], parent) ]], parent)
@ -157,6 +167,12 @@ Panel
return true return true
elseif command == "say" then elseif command == "say" then
return true return true
elseif command == "label" then
return true
elseif command == "gotolabel" then
return true
elseif command == "comment" then
return true
elseif command == "function" then elseif command == "function" then
return true return true
end end
@ -169,7 +185,7 @@ Panel
local parseConfig = function(config) local parseConfig = function(config)
commands = {} commands = {}
local matches = regexMatch(config, [[\s*([^:^\n]+)(:?)([^\n]*)]]) local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
for i=1,#matches do for i=1,#matches do
local command = matches[i][2] local command = matches[i][2]
local validation = (matches[i][3] == ":") local validation = (matches[i][3] == ":")
@ -178,7 +194,7 @@ Panel
if validation then if validation then
table.insert(commands, {command=command:lower(), text=text}) table.insert(commands, {command=command:lower(), text=text})
elseif #commands > 0 then elseif #commands > 0 then
commands[#commands].text = commands[#commands].text .. "\n" .. command commands[#commands].text = commands[#commands].text .. "\n" .. matches[i][1]
end end
end end
end end
@ -186,6 +202,18 @@ Panel
for i=1,#commands do for i=1,#commands do
local label = g_ui.createWidget("CaveBotLabel", ui.list) local label = g_ui.createWidget("CaveBotLabel", ui.list)
label:setText(commands[i].command .. ":" .. commands[i].text) label:setText(commands[i].command .. ":" .. commands[i].text)
if commands[i].command == "goto" then
label:setColor("green")
elseif commands[i].command == "label" then
label:setColor("yellow")
elseif commands[i].command == "comment" then
label:setText(commands[i].text)
label:setColor("white")
elseif commands[i].command == "use" or commands[i].command == "usewith" then
label:setColor("orange")
elseif commands[i].command == "gotolabel" then
label:setColor("red")
end
end end
end end
@ -218,7 +246,7 @@ Panel
ui.list:destroyChildren() ui.list:destroyChildren()
if context.storage.cavebot.activeConfig then if context.storage.cavebot.activeConfig and context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then
ui.config:setCurrentIndex(context.storage.cavebot.activeConfig) ui.config:setCurrentIndex(context.storage.cavebot.activeConfig)
parseConfig(context.storage.cavebot.configs[context.storage.cavebot.activeConfig]) parseConfig(context.storage.cavebot.configs[context.storage.cavebot.activeConfig])
end end
@ -247,7 +275,7 @@ Panel
refreshConfig() refreshConfig()
end end
ui.add.onClick = function() ui.add.onClick = function()
modules.game_textedit.multilineEditor("Waypoints editor", "name:Config name\n", function(newText) modules.game_textedit.multilineEditor("Waypoints editor", "name:Config name\nlabel:start\n", function(newText)
table.insert(context.storage.cavebot.configs, newText) table.insert(context.storage.cavebot.configs, newText)
context.storage.cavebot.activeConfig = #context.storage.cavebot.configs context.storage.cavebot.activeConfig = #context.storage.cavebot.configs
refreshConfig() refreshConfig()
@ -287,12 +315,7 @@ Panel
local newText = "" local newText = ""
if newPos.z ~= oldPos.z then if newPos.z ~= oldPos.z then
newText = "goto:" .. oldPos.x .. "," .. oldPos.y .. "," .. oldPos.z newText = "goto:" .. oldPos.x .. "," .. oldPos.y .. "," .. oldPos.z
if #commands > 0 then newText = newText .. "\ngoto:" .. newPos.x .. "," .. newPos.y .. "," .. newPos.z
local lastCommand = commands[#commands].command .. ":" .. commands[#commands].text
if lastCommand == newText then
return
end
end
stepsSincleLastPos = 0 stepsSincleLastPos = 0
else else
stepsSincleLastPos = stepsSincleLastPos + 1 stepsSincleLastPos = stepsSincleLastPos + 1
@ -319,7 +342,8 @@ Panel
return return
end end
stepsSincleLastPos = 0 stepsSincleLastPos = 0
newText = "use:" .. pos.x .. "," .. pos.y .. "," .. pos.z local playerPos = context.player:getPosition()
newText = "goto:" .. playerPos.x .. "," .. playerPos.y .. "," .. playerPos.z .. "\nuse:" .. pos.x .. "," .. pos.y .. "," .. pos.z
context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\n" .. newText context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\n" .. newText
refreshConfig(true) refreshConfig(true)
end) end)
@ -338,7 +362,8 @@ Panel
return return
end end
stepsSincleLastPos = 0 stepsSincleLastPos = 0
newText = "usewith:" .. itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z local playerPos = context.player:getPosition()
newText = "goto:" .. playerPos.x .. "," .. playerPos.y .. "," .. playerPos.z .. "\nusewith:" .. itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z
context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\n" .. newText context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\n" .. newText
refreshConfig(true) refreshConfig(true)
end) end)
@ -404,7 +429,7 @@ Panel
if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then
return return
end end
modules.game_textedit.multilineEditor("Add function", "function(waypoints)\n --your lua code\n\n -- must return true to execute next command, othwerwise will run in loop till correct return\n return true\nend", function(newText) modules.game_textedit.multilineEditor("Add function", "function(waypoints)\n -- your lua code, function is executed if previous goto was successful or is just after label\n\n -- must return true to execute next command, otherwise will run in loop till correct return\n return true\nend", function(newText)
context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nfunction:" .. newText context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nfunction:" .. newText
refreshConfig(true) refreshConfig(true)
end) end)
@ -424,6 +449,12 @@ Panel
refreshConfig() refreshConfig()
local usedGotoLabel = false
local executeNextMacroCall = false
local commandExecutionNo = 0
local lastGotoSuccesful = true
local lastOpenedContainer = 0
local functions = { local functions = {
enable = function() enable = function()
context.storage.cavebot.enabled = true context.storage.cavebot.enabled = true
@ -435,19 +466,48 @@ Panel
end, end,
refresh = function() refresh = function()
refreshConfig() refreshConfig()
end,
wait = function(peroid)
waitTo = context.now + peroid
end,
waitTo = function(timepoint)
waitTo = timepoint
end,
gotoLabel = function(name)
for i=1,ui.list:getChildCount() do
local command = commands[i]
if command and command.command == "label" and command.text == name then
ui.list:focusChild(ui.list:getChildByIndex(i))
usedGotoLabel = true
lastGotoSuccesful = true
return true
end
end
end end
} }
local executeNextMacroCall = false context.onContainerOpen(function(container)
local commandExecutionNo = 0 if container:getItemsCount() > 0 then
local lastGotoSuccesful = true lastOpenedContainer = context.now
end
end)
context.macro(250, function() context.macro(250, function()
if not context.storage.cavebot.enabled then if not context.storage.cavebot.enabled then
return return
end end
if context.player:isWalking() then -- wait if walked or opened container recently
if context.player:isWalking() or lastOpenedContainer + 1000 > context.now then
executeNextMacroCall = false
return
end
-- wait if attacking/following creature
local attacking = g_game.getAttackingCreature()
local following = g_game.getFollowingCreature()
if (attacking and context.getCreatureById(attacking:getId())) or (following and context.getCreatureById(following:getId())) then
executeNextMacroCall = false executeNextMacroCall = false
return return
end end
@ -474,32 +534,45 @@ Panel
end end
return return
end end
if commandIndex == 1 then
lastGotoSuccesful = true
end
if command.command == "goto" then if command.command == "goto" then
local matches = regexMatch(command.text, [[([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)]]) local matches = regexMatch(command.text, [[([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)]])
if #matches == 1 and #matches[1] == 4 then if #matches == 1 and #matches[1] == 4 then
local position = {x=tonumber(matches[1][2]), y=tonumber(matches[1][3]), z=tonumber(matches[1][4])} local position = {x=tonumber(matches[1][2]), y=tonumber(matches[1][3]), z=tonumber(matches[1][4])}
local distance = context.getDistanceBetween(position, context.player:getPosition()) local distance = context.getDistanceBetween(position, context.player:getPosition())
if distance > 0 and position.z == context.player:getPosition().z then if distance > 100 or position.z ~= context.player:getPosition().z then
lastGotoSuccesful = false
elseif distance > 0 then
commandExecutionNo = commandExecutionNo + 1 commandExecutionNo = commandExecutionNo + 1
lastGotoSuccesful = false lastGotoSuccesful = false
if commandExecutionNo <= 3 then -- try max 3 times if commandExecutionNo <= 3 then -- try max 3 times
if not context.autoWalk(position, 100 + distance * 2, commandExecutionNo > 1, false) then if not context.autoWalk(position, 100 + distance * 2, commandExecutionNo > 1, false) then
context.autoWalk(position, 100 + distance * 2, true, true) if commandExecutionNo > 1 then
context.autoWalk(position, 100 + distance * 2, true, true) -- ignore creatures
end
context.delay(500) context.delay(500)
return return
end end
return return
elseif commandExecutionNo == 4 then -- try last time, location close to destination elseif commandExecutionNo == 4 then -- try last time, location close to destination
for i=1,3 do
position.x = position.x + math.random(-1, 1) position.x = position.x + math.random(-1, 1)
position.y = position.y + math.random(-1, 1) position.y = position.y + math.random(-1, 1)
if context.autoWalk(position, 100 + distance * 2, true) then if context.autoWalk(position, 100 + distance * 2, true) then
return return
end end
elseif distance < 2 then end
elseif distance <= 2 then
lastGotoSuccesful = true lastGotoSuccesful = true
executeNextMacroCall = lastGotoSuccesful
end end
else else
lastGotoSuccesful = (position.z == context.player:getPosition().z) lastGotoSuccesful = true
executeNextMacroCall = lastGotoSuccesful
end end
else else
context.error("Waypoints: invalid use of goto function") context.error("Waypoints: invalid use of goto function")
@ -550,6 +623,7 @@ Panel
elseif command.command == "say" and lastGotoSuccesful then elseif command.command == "say" and lastGotoSuccesful then
context.say(command.text) context.say(command.text)
elseif command.command == "function" and lastGotoSuccesful then elseif command.command == "function" and lastGotoSuccesful then
usedGotoLabel = false
local status, result = pcall(function() local status, result = pcall(function()
return assert(load("return " .. command.text, nil, nil, context))()(functions) return assert(load("return " .. command.text, nil, nil, context))()(functions)
end) end)
@ -557,7 +631,11 @@ Panel
context.error("Waypoints function execution error:\n" .. result) context.error("Waypoints function execution error:\n" .. result)
context.delay(2500) context.delay(2500)
end end
if not result then if not result or usedGotoLabel then
return
end
elseif command.command == "gotolabel" then
if functions.gotoLabel(command.text) then
return return
end end
end end

View File

@ -37,9 +37,6 @@ function show(itemWidget)
local doneFunc = function() local doneFunc = function()
itemWidget:setItemId(window.item:getItemId()) itemWidget:setItemId(window.item:getItemId())
itemWidget:setItemCount(window.item:getItemCount()) itemWidget:setItemCount(window.item:getItemCount())
if itemWidget.onItemChange then
itemWidget:onItemChange()
end
destroy() destroy()
end end

View File

@ -19,8 +19,8 @@ ItemSelectorWindow < MainWindow
margin-left: 5 margin-left: 5
padding-left: 5 padding-left: 5
width: 70 width: 70
minimum: 1 minimum: 0
maximum: 999999999 maximum: 999999
focusable: true focusable: true
Label Label

View File

@ -31,9 +31,6 @@ function UIItem:onDrop(widget, mousePos, forced)
if item:getSubType() > 1 then if item:getSubType() > 1 then
self:setItemSubType(item:getSubType()) self:setItemSubType(item:getSubType())
end end
if self.onItemChange then
self:onItemChange()
end
return return
end end