mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-04-29 18:59:20 +02:00
Bot update - waypoints, attacking, looting
This commit is contained in:
parent
62ff2b1cf5
commit
6dd9a54749
@ -42,7 +42,7 @@ function init()
|
||||
|
||||
connect(Creature, {
|
||||
onAppear = botCreatureAppear,
|
||||
onDisappear =botCreatureDisappear,
|
||||
onDisappear = botCreatureDisappear,
|
||||
onPositionChange = botCreaturePositionChange,
|
||||
onHealthPercentChange = botCraetureHealthPercentChange
|
||||
})
|
||||
@ -50,6 +50,9 @@ function init()
|
||||
onPositionChange = botCreaturePositionChange,
|
||||
onHealthPercentChange = botCraetureHealthPercentChange
|
||||
})
|
||||
connect(Container, { onOpen = botContainerOpen,
|
||||
onClose = botContainerClose,
|
||||
onUpdateItem = botContainerUpdateItem })
|
||||
|
||||
botConfigFile = g_configs.create("/bot.otml")
|
||||
local config = botConfigFile:get("config")
|
||||
@ -165,6 +168,9 @@ function terminate()
|
||||
onPositionChange = botCreaturePositionChange,
|
||||
onHealthPercentChange = botCraetureHealthPercentChange
|
||||
})
|
||||
disconnect(Container, { onOpen = botContainerOpen,
|
||||
onClose = botContainerClose,
|
||||
onUpdateItem = botContainerUpdateItem })
|
||||
|
||||
removeEvent(executeEvent)
|
||||
removeEvent(checkMsgsEvent)
|
||||
@ -324,6 +330,12 @@ function clearConfig()
|
||||
|
||||
botMessages:destroyChildren()
|
||||
botMessages:updateLayout()
|
||||
|
||||
for i, widget in pairs(g_ui.getRootWidget():getChildren()) do
|
||||
if widget.botWidget then
|
||||
widget:destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function refreshConfig()
|
||||
@ -479,3 +491,18 @@ function botOnUseWith(pos, itemId, target, subType)
|
||||
if compiledConfig == nil then return false end
|
||||
safeBotCall(function() compiledConfig.callbacks.onUseWith(pos, itemId, target, subType) 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
|
||||
|
@ -15,16 +15,15 @@ BotLabel < Label
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
|
||||
BotItem < UIItem
|
||||
BotItem < Item
|
||||
virtual: true
|
||||
size: 32 32
|
||||
border: 1 black
|
||||
&selectable: true
|
||||
|
||||
BotTextEdit < TextEdit
|
||||
@onClick: modules.game_textedit.show(self)
|
||||
text-align: center
|
||||
multiline: false
|
||||
focusable: false
|
||||
|
||||
BotSeparator < HorizontalSeparator
|
||||
margin-top: 5
|
||||
@ -40,8 +39,7 @@ CaveBotLabel < Label
|
||||
focusable: true
|
||||
|
||||
$focus:
|
||||
background-color: #00000033
|
||||
color: #ffffff
|
||||
background-color: #00000055
|
||||
|
||||
MiniWindow
|
||||
id: botWindow
|
||||
|
@ -15,22 +15,28 @@ local battleTab = addTab("Battle")
|
||||
local caveTab = addTab("Cave")
|
||||
local toolsTab = addTab("Tools")
|
||||
|
||||
Panels.Health(battleTab)
|
||||
Panels.HealthItem(battleTab)
|
||||
Panels.ManaItem(battleTab)
|
||||
Panels.AttackSpell(battleTab)
|
||||
|
||||
local waypoints = Panels.Waypoints(caveTab)
|
||||
local attacking = Panels.Attacking(caveTab)
|
||||
local looting = Panels.Looting(caveTab)
|
||||
addButton("tutorial", "Help & Tutorials", function()
|
||||
g_platform.openUrl("https://github.com/OTCv8/otclientv8_bot")
|
||||
end, caveTab)
|
||||
|
||||
--#macros
|
||||
|
||||
addSeparator("sep1")
|
||||
local helloLabel = addLabel("helloLabel", "")
|
||||
|
||||
macro(1000, "example macro (time)", nil, function()
|
||||
helloLabel:setText("Time from start: " .. now)
|
||||
end)
|
||||
|
||||
macro(1000, "this macro does nothing", nil, function()
|
||||
macro(1000, "this macro does nothing", "f7", function()
|
||||
|
||||
end, toolsTab)
|
||||
|
||||
@ -40,7 +46,7 @@ hotkey("f5", "example hotkey", function()
|
||||
info("Wow, you clicked f5 hotkey")
|
||||
end)
|
||||
|
||||
singlehotkey("ctrl+f6", "example hotkey2", function()
|
||||
singlehotkey("ctrl+f6", "singlehotkey", function()
|
||||
info("Wow, you clicked f6 singlehotkey")
|
||||
end)
|
||||
|
||||
|
@ -25,7 +25,10 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
|
||||
onCreaturePositionChange = {},
|
||||
onCreatureHealthPercentChange = {},
|
||||
onUse = {},
|
||||
onUseWith = {}
|
||||
onUseWith = {},
|
||||
onContainerOpen = {},
|
||||
onContainerClose = {},
|
||||
onContainerUpdateItem = {}
|
||||
}
|
||||
|
||||
-- basic functions & classes
|
||||
@ -48,8 +51,13 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
|
||||
context.g_game = g_game
|
||||
context.g_map = g_map
|
||||
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.Position = Position
|
||||
context.Config = Config
|
||||
context.HTTP = HTTP
|
||||
|
||||
-- log functions
|
||||
@ -80,14 +88,24 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
|
||||
|
||||
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.callback() then
|
||||
macro.lastExecution = context.now
|
||||
local status, result = pcall(function()
|
||||
if macro.callback() then
|
||||
macro.lastExecution = context.now
|
||||
end
|
||||
end)
|
||||
if not status then
|
||||
context.error("Macro: " .. macro.name .. " execution error: " .. result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
|
||||
context._scheduler[1].callback()
|
||||
local status, result = pcall(function()
|
||||
context._scheduler[1].callback()
|
||||
end)
|
||||
if not status then
|
||||
context.error("Schedule execution error: " .. result)
|
||||
end
|
||||
table.remove(context._scheduler, 1)
|
||||
end
|
||||
end,
|
||||
@ -161,7 +179,7 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
|
||||
onCreatureDisappear = function(creature)
|
||||
for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
|
||||
callback(creature)
|
||||
end
|
||||
end
|
||||
end,
|
||||
onCreaturePositionChange = function(creature, newPos, oldPos)
|
||||
for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
|
||||
@ -182,7 +200,22 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback)
|
||||
for i, callback in ipairs(context._callbacks.onUseWith) do
|
||||
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
|
@ -79,8 +79,22 @@ context.onUseWith = function(callback)
|
||||
return context.callback("onUseWith", callback)
|
||||
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)
|
||||
context.listen = function(name, callback)
|
||||
|
@ -36,7 +36,7 @@ context.macro = function(timeout, name, hotkey, callback, parent)
|
||||
local switch = nil
|
||||
if name:len() > 0 then
|
||||
if context.storage._macros[name] == nil then
|
||||
context.storage._macros[name] = true
|
||||
context.storage._macros[name] = false
|
||||
end
|
||||
switch = context._addMacroSwitch(name, hotkey, parent)
|
||||
end
|
||||
|
@ -10,6 +10,19 @@ context.getSpectators = function(multifloor)
|
||||
return g_map.getSpectators(context.player:getPosition(), multifloor)
|
||||
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)
|
||||
if not name then return nil end
|
||||
name = name:lower()
|
||||
@ -70,6 +83,10 @@ context.autoWalk = function(destination, maxDist, ignoreFields, ignoreCreatures)
|
||||
if #path < 1 then
|
||||
return false
|
||||
end
|
||||
g_game.autoWalk(path, context.player:getPosition())
|
||||
if g_game.getFeature(GameNewWalking) then
|
||||
g_game.autoWalk(path, context.player:getPosition())
|
||||
else
|
||||
g_game.autoWalk(path, {x=0,y=0,z=0})
|
||||
end
|
||||
return true
|
||||
end
|
@ -5,6 +5,7 @@ context.setupUI = function(otml, parent)
|
||||
parent = context.panel
|
||||
end
|
||||
local widget = g_ui.loadUIFromString(otml, parent)
|
||||
widget.botWidget = true
|
||||
return widget
|
||||
end
|
||||
|
||||
|
@ -1,8 +1,334 @@
|
||||
local context = G.botContext
|
||||
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)
|
||||
context.setupUI([[
|
||||
local ui = context.setupUI([[
|
||||
Panel
|
||||
id: attacking
|
||||
height: 150
|
||||
@ -12,8 +338,650 @@ Panel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: Attacking
|
||||
|
||||
]], parent)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -17,7 +17,7 @@ end
|
||||
Panels.ManaShield = function(parent)
|
||||
context.macro(500, "Auto ManaShield", nil, function()
|
||||
if not context.hasManaShield() then
|
||||
if context.saySpell("utamo vita", 1000) then
|
||||
if context.saySpell("utamo vita", 500) then
|
||||
context.delay(5000)
|
||||
end
|
||||
end
|
||||
@ -323,3 +323,16 @@ Panels.Turning = function(parent)
|
||||
end
|
||||
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
|
||||
|
@ -1,20 +1,448 @@
|
||||
local context = G.botContext
|
||||
local Panels = context.Panels
|
||||
|
||||
|
||||
Panels.Looting = function(parent)
|
||||
context.setupUI([[
|
||||
local ui = context.setupUI([[
|
||||
Panel
|
||||
id: looting
|
||||
height: 150
|
||||
height: 190
|
||||
|
||||
BotLabel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: Looting
|
||||
|
||||
]], parent)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -5,7 +5,7 @@ Panels.Waypoints = function(parent)
|
||||
local ui = context.setupUI([[
|
||||
Panel
|
||||
id: waypoints
|
||||
height: 213
|
||||
height: 203
|
||||
|
||||
BotLabel
|
||||
anchors.top: parent.top
|
||||
@ -35,6 +35,7 @@ Panel
|
||||
anchors.left: parent.left
|
||||
text: Add
|
||||
width: 60
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: edit
|
||||
@ -42,6 +43,7 @@ Panel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: Edit
|
||||
width: 60
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: remove
|
||||
@ -49,6 +51,7 @@ Panel
|
||||
anchors.right: parent.right
|
||||
text: Remove
|
||||
width: 60
|
||||
height: 20
|
||||
|
||||
TextList
|
||||
id: list
|
||||
@ -85,6 +88,7 @@ Panel
|
||||
text: Goto
|
||||
width: 61
|
||||
margin-top: 1
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: wUse
|
||||
@ -92,6 +96,7 @@ Panel
|
||||
anchors.left: prev.right
|
||||
text: Use
|
||||
width: 61
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: wUseWith
|
||||
@ -99,6 +104,7 @@ Panel
|
||||
anchors.left: prev.right
|
||||
text: UseWith
|
||||
width: 61
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: wWait
|
||||
@ -107,6 +113,7 @@ Panel
|
||||
text: Wait
|
||||
width: 61
|
||||
margin-top: 1
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: wSay
|
||||
@ -114,6 +121,7 @@ Panel
|
||||
anchors.left: prev.right
|
||||
text: Say
|
||||
width: 61
|
||||
height: 20
|
||||
|
||||
Button
|
||||
id: wFunction
|
||||
@ -121,6 +129,7 @@ Panel
|
||||
anchors.left: prev.right
|
||||
text: Function
|
||||
width: 61
|
||||
height: 20
|
||||
|
||||
BotSwitch
|
||||
id: recording
|
||||
@ -128,6 +137,7 @@ Panel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: Auto Recording
|
||||
height: 20
|
||||
|
||||
]], parent)
|
||||
|
||||
@ -157,6 +167,12 @@ Panel
|
||||
return true
|
||||
elseif command == "say" then
|
||||
return true
|
||||
elseif command == "label" then
|
||||
return true
|
||||
elseif command == "gotolabel" then
|
||||
return true
|
||||
elseif command == "comment" then
|
||||
return true
|
||||
elseif command == "function" then
|
||||
return true
|
||||
end
|
||||
@ -169,7 +185,7 @@ Panel
|
||||
|
||||
local parseConfig = function(config)
|
||||
commands = {}
|
||||
local matches = regexMatch(config, [[\s*([^:^\n]+)(:?)([^\n]*)]])
|
||||
local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
|
||||
for i=1,#matches do
|
||||
local command = matches[i][2]
|
||||
local validation = (matches[i][3] == ":")
|
||||
@ -178,7 +194,7 @@ Panel
|
||||
if validation then
|
||||
table.insert(commands, {command=command:lower(), text=text})
|
||||
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
|
||||
@ -186,6 +202,18 @@ Panel
|
||||
for i=1,#commands do
|
||||
local label = g_ui.createWidget("CaveBotLabel", ui.list)
|
||||
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
|
||||
|
||||
@ -218,7 +246,7 @@ Panel
|
||||
|
||||
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)
|
||||
parseConfig(context.storage.cavebot.configs[context.storage.cavebot.activeConfig])
|
||||
end
|
||||
@ -247,7 +275,7 @@ Panel
|
||||
refreshConfig()
|
||||
end
|
||||
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)
|
||||
context.storage.cavebot.activeConfig = #context.storage.cavebot.configs
|
||||
refreshConfig()
|
||||
@ -287,12 +315,7 @@ Panel
|
||||
local newText = ""
|
||||
if newPos.z ~= oldPos.z then
|
||||
newText = "goto:" .. oldPos.x .. "," .. oldPos.y .. "," .. oldPos.z
|
||||
if #commands > 0 then
|
||||
local lastCommand = commands[#commands].command .. ":" .. commands[#commands].text
|
||||
if lastCommand == newText then
|
||||
return
|
||||
end
|
||||
end
|
||||
newText = newText .. "\ngoto:" .. newPos.x .. "," .. newPos.y .. "," .. newPos.z
|
||||
stepsSincleLastPos = 0
|
||||
else
|
||||
stepsSincleLastPos = stepsSincleLastPos + 1
|
||||
@ -319,7 +342,8 @@ Panel
|
||||
return
|
||||
end
|
||||
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
|
||||
refreshConfig(true)
|
||||
end)
|
||||
@ -338,7 +362,8 @@ Panel
|
||||
return
|
||||
end
|
||||
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
|
||||
refreshConfig(true)
|
||||
end)
|
||||
@ -404,7 +429,7 @@ Panel
|
||||
if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then
|
||||
return
|
||||
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
|
||||
refreshConfig(true)
|
||||
end)
|
||||
@ -423,6 +448,12 @@ Panel
|
||||
end
|
||||
|
||||
refreshConfig()
|
||||
|
||||
local usedGotoLabel = false
|
||||
local executeNextMacroCall = false
|
||||
local commandExecutionNo = 0
|
||||
local lastGotoSuccesful = true
|
||||
local lastOpenedContainer = 0
|
||||
|
||||
local functions = {
|
||||
enable = function()
|
||||
@ -435,23 +466,52 @@ Panel
|
||||
end,
|
||||
refresh = function()
|
||||
refreshConfig()
|
||||
end
|
||||
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
|
||||
}
|
||||
|
||||
local executeNextMacroCall = false
|
||||
local commandExecutionNo = 0
|
||||
local lastGotoSuccesful = true
|
||||
context.onContainerOpen(function(container)
|
||||
if container:getItemsCount() > 0 then
|
||||
lastOpenedContainer = context.now
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
context.macro(250, function()
|
||||
if not context.storage.cavebot.enabled then
|
||||
return
|
||||
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
|
||||
return
|
||||
end
|
||||
|
||||
if not executeNextMacroCall then
|
||||
executeNextMacroCall = true
|
||||
return
|
||||
@ -474,32 +534,45 @@ Panel
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if commandIndex == 1 then
|
||||
lastGotoSuccesful = true
|
||||
end
|
||||
|
||||
if command.command == "goto" then
|
||||
local matches = regexMatch(command.text, [[([0-9]+)[^0-9]+([0-9]+)[^0-9]+([0-9]+)]])
|
||||
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 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
|
||||
lastGotoSuccesful = false
|
||||
if commandExecutionNo <= 3 then -- try max 3 times
|
||||
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)
|
||||
return
|
||||
end
|
||||
return
|
||||
elseif commandExecutionNo == 4 then -- try last time, location close to destination
|
||||
position.x = position.x + math.random(-1, 1)
|
||||
position.y = position.y + math.random(-1, 1)
|
||||
if context.autoWalk(position, 100 + distance * 2, true) then
|
||||
return
|
||||
for i=1,3 do
|
||||
position.x = position.x + math.random(-1, 1)
|
||||
position.y = position.y + math.random(-1, 1)
|
||||
if context.autoWalk(position, 100 + distance * 2, true) then
|
||||
return
|
||||
end
|
||||
end
|
||||
elseif distance < 2 then
|
||||
elseif distance <= 2 then
|
||||
lastGotoSuccesful = true
|
||||
executeNextMacroCall = lastGotoSuccesful
|
||||
end
|
||||
else
|
||||
lastGotoSuccesful = (position.z == context.player:getPosition().z)
|
||||
lastGotoSuccesful = true
|
||||
executeNextMacroCall = lastGotoSuccesful
|
||||
end
|
||||
else
|
||||
context.error("Waypoints: invalid use of goto function")
|
||||
@ -550,6 +623,7 @@ Panel
|
||||
elseif command.command == "say" and lastGotoSuccesful then
|
||||
context.say(command.text)
|
||||
elseif command.command == "function" and lastGotoSuccesful then
|
||||
usedGotoLabel = false
|
||||
local status, result = pcall(function()
|
||||
return assert(load("return " .. command.text, nil, nil, context))()(functions)
|
||||
end)
|
||||
@ -557,7 +631,11 @@ Panel
|
||||
context.error("Waypoints function execution error:\n" .. result)
|
||||
context.delay(2500)
|
||||
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
|
||||
end
|
||||
end
|
||||
|
@ -37,9 +37,6 @@ function show(itemWidget)
|
||||
local doneFunc = function()
|
||||
itemWidget:setItemId(window.item:getItemId())
|
||||
itemWidget:setItemCount(window.item:getItemCount())
|
||||
if itemWidget.onItemChange then
|
||||
itemWidget:onItemChange()
|
||||
end
|
||||
destroy()
|
||||
end
|
||||
|
||||
|
@ -19,8 +19,8 @@ ItemSelectorWindow < MainWindow
|
||||
margin-left: 5
|
||||
padding-left: 5
|
||||
width: 70
|
||||
minimum: 1
|
||||
maximum: 999999999
|
||||
minimum: 0
|
||||
maximum: 999999
|
||||
focusable: true
|
||||
|
||||
Label
|
||||
|
@ -31,9 +31,6 @@ function UIItem:onDrop(widget, mousePos, forced)
|
||||
if item:getSubType() > 1 then
|
||||
self:setItemSubType(item:getSubType())
|
||||
end
|
||||
if self.onItemChange then
|
||||
self:onItemChange()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user