Updated to OTCv8 3.1 rev 229

This commit is contained in:
OTCv8 2022-07-09 22:09:37 +00:00
parent 88fb41bafc
commit a48d0fd0d1
125 changed files with 24548 additions and 732 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -45,17 +45,17 @@ OptionPanel
type: horizontalBox
OptionCheckBox
id: actionbarBottom1
id: actionbar1
!text: tr('Bar 1')
width: 60
OptionCheckBox
id: actionbarBottom2
id: actionbar2
!text: tr('Bar 2')
width: 60
OptionCheckBox
id: actionbarBottom3
id: actionbar3
!text: tr('Bar 3')
width: 60
@ -75,17 +75,17 @@ OptionPanel
type: horizontalBox
OptionCheckBox
id: actionbarLeft1
id: actionbar4
!text: tr('Bar 1')
width: 60
OptionCheckBox
id: actionbarLeft2
id: actionbar5
!text: tr('Bar 2')
width: 60
OptionCheckBox
id: actionbarLeft3
id: actionbar6
!text: tr('Bar 3')
width: 60
@ -104,17 +104,17 @@ OptionPanel
visible: false
OptionCheckBox
id: actionbarRight1
id: actionbar7
!text: tr('Bar 1')
width: 60
OptionCheckBox
id: actionbarRight2
id: actionbar8
!text: tr('Bar 2')
width: 60
OptionCheckBox
id: actionbarRight3
id: actionbar9
!text: tr('Bar 3')
width: 60

View File

@ -52,17 +52,15 @@ local defaultOptions = {
topBar = true,
actionbarBottom1 = true,
actionbarBottom2 = false,
actionbarBottom3 = false,
actionbarLeft1 = false,
actionbarLeft2 = false,
actionbarLeft3 = false,
actionbarRight1 = false,
actionbarRight2 = false,
actionbarRight3 = false,
actionbar1 = true,
actionbar2 = false,
actionbar3 = false,
actionbar4 = false,
actionbar5 = false,
actionbar6 = false,
actionbar7 = false,
actionbar8 = false,
actionbar9 = false,
actionbarLock = false,

View File

@ -156,6 +156,7 @@ function UIMiniWindow:setup()
local parent = rootWidget:recursiveGetChildById(selfSettings.parentId)
if parent then
if parent:getClassName() == 'UIMiniWindowContainer' and selfSettings.index and parent:isOn() then
self:setParent(parent, true)
self.miniIndex = selfSettings.index
parent:scheduleInsert(self, selfSettings.index)
elseif selfSettings.position then

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,7 @@
Module
name: game_actionbar
description: Action bar
author: otclient@otclient.ovh
website: otclient.ovh
author: https://github.com/Vithrax/
sandboxed: true
scripts: [ actionbar ]
@onLoad: init()
@onUnload: terminate()
scripts: [ actionbar ]

View File

@ -1,29 +1,42 @@
ActionButton < Panel
ActionButton < UIButton
font: cipsoftFont
width: 40
padding: 1 1 1 1
width: 36
padding: 1
margin-left: 1
draggable: true
Item
id: item
anchors.fill: parent
padding: 1
&selectable: true
&editable: false
virtual: true
border-width: 1
border-color: #00000000
draggable: true
$!on:
image-source: /images/game/actionbarslot
image-source: /images/game/actionbar/actionbarslot
image-color: #dfdfdf
image-clip: 0 0 34 34
image-border: 0
$on:
image-source: /images/ui/button
image-color: #dfdfdf
image-clip: 0 0 22 23
image-border: 3
$pressed on:
image-clip: 0 46 22 23
Label
id: text
anchors.fill: parent
anchors.fill: prev
text-auto-resize: true
text-wrap: true
phantom: true
margin: 1
text-align: center
font: verdana-9px
@ -31,7 +44,7 @@ ActionButton < Panel
id: hotkeyLabel
anchors.top: parent.top
anchors.right: parent.right
margin: 2 3 3 3
margin: 3 4 3 3
text-auto-resize: true
text-wrap: false
phantom: true
@ -39,22 +52,172 @@ ActionButton < Panel
color: white
background: #292A2A
Label
id: parameterText
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-bottom: -3
margin-left: 2
margin-right: 2
font: verdana-9px
color: white
text-align: center
UIProgressRect
id: cooldown
background: #585858AA
background: #101010aa
percent: 100
focusable: false
phantom: true
anchors.fill: parent
margin: 1 1 1 1
font: verdana-11px-rounded
color: white
LeftSliders < Panel
size: 17 34
Button
id: prev
anchors.fill: parent
anchors.bottom: parent.verticalCenter
@onClick: modules.game_actionbar.moveActionButtons(self)
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the previous action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
$!on:
image-source: /images/game/actionbar/arrow-disabled
$on:
image-source: /images/game/actionbar/arrow
Button
id: first
anchors.fill: parent
anchors.top: parent.verticalCenter
@onClick: modules.game_actionbar.moveActionButtons(self)
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the first action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
$!on:
image-source: /images/game/actionbar/arrow-skip-disabled
$on:
image-source: /images/game/actionbar/arrow-skip
RightSliders < Panel
size: 29 34
Button
id: lock
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 12
@onClick: modules.game_actionbar.changeLockState(self)
$!on:
tooltip: Action Bar Locked: You cannot assign actions to or switch actions on action buttons by "drag&drop".
$on:
tooltip: Action Bar Unlocked: You can assign actions to or switch actions on action buttons by "drag&drop".
UIWidget
id: image
anchors.centerIn: parent
margin-right: 1
phantom: true
$!on:
image-source: /images/game/actionbar/locked
$on:
image-source: /images/game/actionbar/unlocked
Button
id: next
anchors.top: parent.top
anchors.bottom: parent.verticalCenter
anchors.left: parent.left
anchors.right: lock.left
margin-right: 1
@onClick: modules.game_actionbar.moveActionButtons(self)
on: true
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the next action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-top: 1
margin-left: 2
rotation: 180
on: true
$!on:
image-source: /images/game/actionbar/arrow-disabled
$on:
image-source: /images/game/actionbar/arrow
Button
id: last
anchors.top: parent.verticalCenter
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: prev.right
@onClick: modules.game_actionbar.moveActionButtons(self)
on: true
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the last action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-top: 1
margin-left: 1
rotation: 180
on: true
$!on:
image-source: /images/game/actionbar/arrow-skip-disabled
$on:
image-source: /images/game/actionbar/arrow-skip
Panel
id: actionBar
focusable: false
image-source: /images/ui/panel_side
image-border: 4
image-source: /images/ui/actionbar_background
image-border: 1
margin-top: -1
$on:
@ -65,77 +228,37 @@ Panel
height: 0
visible: false
TabButton
id: prevButton
icon: /images/game/console/leftarrow
LeftSliders
id: prevPanel
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter
margin-left: 1
margin-top: 1
margin-bottom: 2
Panel
ScrollablePanel
id: tabBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: prev.right
anchors.right: next.left
margin-right: 3
anchors.right: nextPanel.left
margin-left: 4
margin-right: 4
clipping: true
padding-top: 2
padding-bottom: 2
horizontal-scrollbar: actionScroll
layout: horizontalBox
TabButton
id: nextButton
icon: /images/game/console/rightarrow
anchors.right: parent.right
HorizontalScrollBar
id: actionScroll
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 1
margin-top: 1
margin-bottom: 2
ActionAssignWindow < MainWindow
id: assignWindow
!text: tr('Button Assign')
size: 360 150
@onEscape: self:destroy()
Label
!text: tr('Please, press the key you wish to use for action')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-auto-resize: true
text-align: left
step: 37
pixels-scroll: true
visible: false
Label
id: comboPreview
!text: tr('Current action hotkey: %s', 'none')
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 10
text-auto-resize: true
HorizontalSeparator
id: separator
anchors.left: parent.left
RightSliders
id: nextPanel
anchors.right: parent.right
anchors.bottom: next.top
margin-bottom: 10
Button
id: addButton
!text: tr('Add')
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin-right: 10
Button
id: cancelButton
!text: tr('Cancel')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
@onClick: self:getParent():destroy()
anchors.verticalCenter: parent.verticalCenter
margin-right: 1

View File

@ -0,0 +1,56 @@
MainWindow
id: assignHotkeyWindow
size: 400 170
FlatLabel
id: display
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 33
text-align: center
font: verdana-11px-rounded
Label
id: desc
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 10
multi-line: true
height: 40
text-wrap: true
text: Click "Ok" to assign the hotkey. Click "Clear" to remove the hotkey from "
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
anchors.right: parent.right
Button
id: buttonClose
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Close
@onClick: self:getParent():destroy()
Button
id: buttonClear
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Clear
Button
id: buttonOk
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Ok

View File

@ -0,0 +1,100 @@
RoundCheckBox < CheckBox
image-source: /images/ui/checkbox_round
$first:
margin-top: 2
$!first:
margin-top: 5
MainWindow
id: assignItemWindow
size: 275 195
UIItem
id: item
anchors.top: parent.top
anchors.left: parent.left
size: 86 86
padding: 5
&selectable: true
&editable: false
image-source: /images/ui/panel_flat
image-border: 1
Panel
id: checks
anchors.left: prev.right
anchors.top: parent.top
anchors.right: parent.right
margin-left: 10
layout:
type: verticalBox
fit-children: true
RoundCheckBox
id: useSelf
text: Use on yourself
enabled: false
RoundCheckBox
id: useTarget
text: Use on target
enabled: false
RoundCheckBox
id: useCross
text: With crosshair
enabled: false
RoundCheckBox
id: equip
text: Equip/Unequip
enabled: false
RoundCheckBox
id: use
text: Use
enabled: false
Button
id: select
anchors.left: item.left
anchors.right: item.right
anchors.top: item.bottom
margin-top: 5
font: cipsoftFont
height: 20
text: Select item
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
anchors.right: parent.right
Button
id: buttonClose
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Close
Button
id: buttonApply
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Apply
Button
id: buttonOk
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Ok

View File

@ -1,30 +1,37 @@
SideActionButton < Panel
SideActionButton < UIButton
font: cipsoftFont
height: 40
padding: 1 1 1 1
$first:
margin-top: -4
$!first:
margin-top: -1
height: 36
padding: 1
margin-top: 1
Item
id: item
anchors.fill: parent
padding: 1
&selectable: true
&editable: false
virtual: true
border-width: 1
border-color: #00000000
$!on:
image-source: /images/game/actionbarslot
image-source: /images/game/actionbar/actionbarslot
image-color: #dfdfdf
image-clip: 0 0 34 34
image-border: 0
$on:
image-source: /images/ui/button
image-color: #dfdfdf
image-clip: 0 0 22 23
image-border: 3
$pressed on:
image-clip: 0 46 22 23
Label
id: text
anchors.fill: parent
anchors.fill: prev
text-auto-resize: true
text-wrap: true
phantom: true
@ -35,7 +42,7 @@ SideActionButton < Panel
id: hotkeyLabel
anchors.top: parent.top
anchors.right: parent.right
margin: 2 3 3 3
margin: 3 4 3 3
text-auto-resize: true
text-wrap: false
phantom: true
@ -43,103 +50,214 @@ SideActionButton < Panel
color: white
background: #292A2A
Label
id: parameterText
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-bottom: -3
margin-left: 2
margin-right: 2
font: verdana-9px
color: white
text-align: center
UIProgressRect
id: cooldown
background: #585858AA
background: #101010aa
percent: 100
focusable: false
phantom: true
anchors.fill: parent
margin: 1 1 1 1
font: verdana-11px-rounded
color: white
TopSliders < Panel
size: 34 17
Button
id: prev
anchors.fill: parent
anchors.right: parent.horizontalCenter
@onClick: modules.game_actionbar.moveActionButtons(self)
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the previous action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-left: 1
rotation: 90
$!on:
image-source: /images/game/actionbar/arrow-disabled
$on:
image-source: /images/game/actionbar/arrow
Button
id: first
anchors.fill: parent
anchors.left: parent.horizontalCenter
@onClick: modules.game_actionbar.moveActionButtons(self)
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the first action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-left: 1
rotation: 90
$!on:
image-source: /images/game/actionbar/arrow-skip-disabled
$on:
image-source: /images/game/actionbar/arrow-skip
BottomSliders < Panel
size: 34 32
Button
id: lock
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 15
@onClick: modules.game_actionbar.changeLockState(self)
$!on:
tooltip: Action Bar Locked: You cannot assign actions to or switch actions on action buttons by "drag&drop".
$on:
tooltip: Action Bar Unlocked: You can assign actions to or switch actions on action buttons by "drag&drop".
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-bottom: 1
$!on:
image-source: /images/game/actionbar/locked
$on:
image-source: /images/game/actionbar/unlocked
Button
id: next
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
height: 17
margin-right: 1
@onClick: modules.game_actionbar.moveActionButtons(self)
on: true
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the next action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-top: 1
rotation: 270
on: true
$!on:
image-source: /images/game/actionbar/arrow-disabled
$on:
image-source: /images/game/actionbar/arrow
Button
id: last
anchors.top: parent.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
height: 17
@onClick: modules.game_actionbar.moveActionButtons(self)
on: true
$!on:
tooltip: No further action buttons in this direction
$on:
tooltip: Move to the last action button
UIWidget
id: image
anchors.centerIn: parent
phantom: true
margin-top: 1
rotation: 270
on: true
$!on:
image-source: /images/game/actionbar/arrow-skip-disabled
$on:
image-source: /images/game/actionbar/arrow-skip
Panel
id: actionBar
focusable: false
image-source: /images/ui/panel_side
image-border: 4
margin-top: -2
image-source: /images/ui/actionbar_background
image-border: 1
margin-top: -5
$on:
width: 40
width: 37
visible: true
$!on:
width: 0
visible: false
TabButton
id: prevButton
icon: /images/game/console/uparrow
anchors.left: parent.left
TopSliders
id: prevPanel
anchors.top: parent.top
anchors.right: parent.right
margin-top: -1
margin-left: 1
margin-right: 1
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 1
Panel
ScrollablePanel
id: tabBar
anchors.top: prev.bottom
anchors.right: parent.right
anchors.bottom: nextPanel.top
anchors.left: parent.left
anchors.bottom: next.top
margin-top: 3
anchors.right: parent.right
margin-top: 2
margin-bottom: 4
clipping: true
padding-left: 1
vertical-scrollbar: actionScroll
layout: verticalBox
TabButton
id: nextButton
icon: /images/game/console/downarrow
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-right: 1
margin-left: 1
margin-bottom: 2
ActionAssignWindow < MainWindow
id: assignWindow
!text: tr('Button Assign')
size: 360 150
@onEscape: self:destroy()
Label
!text: tr('Please, press the key you wish to use for action')
anchors.left: parent.left
anchors.right: parent.right
VerticalScrollBar
id: actionScroll
anchors.top: parent.top
text-auto-resize: true
text-align: left
anchors.bottom: parent.bottom
anchors.right: parent.right
step: 37
pixels-scroll: true
visible: false
Label
id: comboPreview
!text: tr('Current action hotkey: %s', 'none')
BottomSliders
id: nextPanel
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 10
text-auto-resize: true
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: next.top
margin-bottom: 10
Button
id: addButton
!text: tr('Add')
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin-right: 10
Button
id: cancelButton
!text: tr('Cancel')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
@onClick: self:getParent():destroy()
margin-bottom: 3

View File

@ -0,0 +1,150 @@
SpellPreview < UICheckBox
height: 34
text-wrap: true
multi-line: true
focusable: true
text-offset: 38 2
image-source:
change-cursor-image: false
$hover !disabled:
color: #ffffff
$!checked:
background-color: alpha
$checked:
background-color: #ffffff22
$disabled:
image-color: #dfdfdf88
color: #dfdfdf88
opacity: 0.8
UIWidget
id: image
anchors.left: parent.left
anchors.top: parent.top
phantom: true
padding: 1
size: 34 34
MainWindow
id: assignSpellWindow
size: 275 310
@onEscape: self:destroy()
SpellPreview
id: preview
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
focusable: false
$!checked:
background-color: alpha
$checked:
background-color: alpha
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
anchors.right: parent.right
TextList
id: spellList
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 10
height: 120
padding: 1
focusable: false
vertical-scrollbar: listScrollBar
background-color: #484848
VerticalScrollBar
id: listScrollBar
anchors.top: spellList.top
anchors.right: spellList.right
margin-right: 1
anchors.bottom: spellList.bottom
step: 14
pixels-scroll: true
FlatPanel
id: checkPanel
anchors.top: spellList.bottom
margin-top: 8
anchors.left: parent.left
anchors.right: parent.right
height: 20
CheckBox
id: tick
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
margin-left: 5
text: Only show vocation spells
checked: true
Label
id: paramLabel
anchors.top: prev.bottom
margin-top: 5
anchors.left: parent.left
text: Parameter:
height: 20
text-horizontal-auto-resize: true
text-offset: 0 3
on: false
$on:
color: white
$!on:
color: #c0c0c0
TextEdit
id: paramText
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
anchors.left: prev.right
margin-left: 5
enabled: false
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
anchors.right: parent.right
Button
id: buttonClose
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Close
Button
id: buttonApply
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Apply
Button
id: buttonOk
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Ok

View File

@ -0,0 +1,66 @@
MainWindow
id: assignTextWindow
size: 275 150
text: Assign Text
Label
anchors.top: parent.top
anchors.left: parent.left
text: Text:
text-horizontal-auto-resize: true
TextEdit
id: text
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 5
FlatPanel
id: checkPanel
anchors.top: prev.bottom
margin-top: 5
anchors.left: parent.left
anchors.right: parent.right
height: 20
CheckBox
id: tick
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
margin-left: 5
text: Send automatically
checked: true
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
anchors.right: parent.right
Button
id: buttonClose
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Close
Button
id: buttonApply
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Apply
Button
id: buttonOk
anchors.right: prev.left
margin-right: 5
anchors.bottom: parent.bottom
size: 45 21
font: cipsoftFont
text: Ok

View File

@ -497,9 +497,7 @@ function initCallbacks()
onRemoveItem = botContainerRemoveItem,
onGameEditText = botGameEditText,
onSpellCooldown = botSpellCooldown,
onSpellGroupCooldown = botGroupSpellCooldown,
onQuestLog = botGameQuestLog,
onQuestLine = botGameQuestLine
onSpellGroupCooldown = botGroupSpellCooldown
})
connect(Tile, {
@ -563,9 +561,7 @@ function terminateCallbacks()
onAttackingCreatureChange = botAttackingCreatureChange,
onGameEditText = botGameEditText,
onSpellCooldown = botSpellCooldown,
onSpellGroupCooldown = botGroupSpellCooldown,
onQuestLog = botGameQuestLog,
onQuestLine = botGameQuestLine
onSpellGroupCooldown = botGroupSpellCooldown
})
disconnect(Tile, {
@ -800,14 +796,4 @@ end
function botInventoryChange(player, slot, item, oldItem)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onInventoryChange(player, slot, item, oldItem) end)
end
function botGameQuestLog(quests)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onGameQuestLog(quests) end)
end
function botGameQuestLine(quests)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onGameQuestLine(questId, questMissions) end)
end

View File

@ -0,0 +1,64 @@
-- load all otui files, order doesn't matter
local configName = modules.game_bot.contentsPanel.config:getCurrentOption().text
local configFiles = g_resources.listDirectoryFiles("/bot/" .. configName .. "/vBot", true, false)
for i, file in ipairs(configFiles) do
local ext = file:split(".")
if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then
g_ui.importStyle(file)
end
end
local function loadScript(name)
return dofile("/vBot/" .. name .. ".lua")
end
-- here you can set manually order of scripts
-- libraries should be loaded first
local luaFiles = {
"main",
"items",
"vlib",
"new_cavebot_lib",
"configs", -- do not change this and above
"extras",
"cavebot",
"playerlist",
"BotServer",
"alarms",
"Conditions",
"Equipper",
"pushmax",
"combo",
"HealBot",
"new_healer",
"AttackBot", -- last of major modules
"ingame_editor",
"Dropper",
"Containers",
"quiver_manager",
"quiver_label",
"tools",
"antiRs",
"depot_withdraw",
"eat_food",
"equip",
"exeta",
"analyzer",
"spy_level",
"supplies",
"depositer_config",
"npc_talk",
"xeno_menu",
"hold_target",
"cavebot_control_panel"
}
for i, file in ipairs(luaFiles) do
loadScript(file)
end
setDefaultTab("Main")
UI.Separator()
UI.Label("Private Scripts:")
UI.Separator()

View File

@ -0,0 +1,509 @@
CaveBot.Actions = {}
vBot.lastLabel = ""
local oldTibia = g_game.getClientVersion() < 960
local nextTile = nil
local noPath = 0
-- antistuck f()
local nextPos = nil -- creature
local nextPosF = nil -- furniture
local function modPos(dir)
local y = 0
local x = 0
if dir == 0 then
y = -1
elseif dir == 1 then
x = 1
elseif dir == 2 then
y = 1
elseif dir == 3 then
x = -1
elseif dir == 4 then
y = -1
x = 1
elseif dir == 5 then
y = 1
x = 1
elseif dir == 6 then
y = 1
x = -1
elseif dir == 7 then
y = -1
x = -1
end
return {x, y}
end
-- stack-covered antystuck, in & out pz
local lastMoved = now - 200
onTextMessage(function(mode, text)
if text ~= 'There is not enough room.' then return end
if CaveBot.isOff() then return end
local tiles = getNearTiles(pos())
for i, tile in ipairs(tiles) do
if not tile:hasCreature() and tile:isWalkable() and #tile:getItems() > 9 then
local topThing = tile:getTopThing()
if not isInPz() then
return useWith(3197, tile:getTopThing()) -- disintegrate
else
if now < lastMoved + 200 then return end -- delay to prevent clogging
local nearTiles = getNearTiles(tile:getPosition())
for i, tile in ipairs(nearTiles) do
local tpos = tile:getPosition()
if pos() ~= tpos then
if tile:isWalkable() then
lastMoved = now
return g_game.move(topThing, tpos) -- move item
end
end
end
end
end
end
end)
local furnitureIgnore = { 2986 }
local function breakFurniture(destPos)
if isInPz() then return false end
local candidate = {thing=nil, dist=100}
for i, tile in ipairs(g_map.getTiles(posz())) do
local walkable = tile:isWalkable()
local topThing = tile:getTopThing()
local isWg = topThing and topThing:getId() == 2130
if topThing and (isWg or not table.find(furnitureIgnore, topThing:getId()) and topThing:isItem()) then
local moveable = not topThing:isNotMoveable()
local tpos = tile:getPosition()
local path = findPath(player:getPosition(), tpos, 7, { ignoreNonPathable = true, precision = 1 })
if path then
if isWg or (not walkable and moveable) then
local distance = getDistanceBetween(destPos, tpos)
if distance < candidate.dist then
candidate = {thing=topThing, dist=distance}
end
end
end
end
end
local thing = candidate.thing
if thing then
useWith(3197, thing)
return true
end
return false
end
local function pushPlayer(creature)
local cpos = creature:getPosition()
local tiles = getNearTiles(cpos)
for i, tile in ipairs(tiles) do
local pos = tile:getPosition()
local minimapColor = g_map.getMinimapColor(pos)
local stairs = (minimapColor >= 210 and minimapColor <= 213)
if not stairs and tile:isWalkable() then
g_game.move(creature, pos)
end
end
end
local function pathfinder()
if not storage.extras.pathfinding then return end
if noPath < 10 then return end
if not CaveBot.gotoNextWaypointInRange() then
if getConfigFromName and getConfigFromName() then
local profile = CaveBot.getCurrentProfile()
local config = getConfigFromName()
local newProfile = profile == '#Unibase' and config or '#Unibase'
CaveBot.setCurrentProfile(newProfile)
end
end
noPath = 0
return true
end
-- it adds an action widget to list
CaveBot.addAction = function(action, value, focus)
action = action:lower()
local raction = CaveBot.Actions[action]
if not raction then
return warn("Invalid cavebot action: " .. action)
end
if type(value) == 'number' then
value = tostring(value)
end
local widget = UI.createWidget("CaveBotAction", CaveBot.actionList)
widget:setText(action .. ":" .. value:split("\n")[1])
widget.action = action
widget.value = value
if raction.color then
widget:setColor(raction.color)
end
widget.onDoubleClick = function(cwidget) -- edit on double click
if CaveBot.Editor then
schedule(20, function() -- schedule to have correct focus
CaveBot.Editor.edit(cwidget.action, cwidget.value, function(action, value)
CaveBot.editAction(cwidget, action, value)
CaveBot.save()
end)
end)
end
end
if focus then
widget:focus()
CaveBot.actionList:ensureChildVisible(widget)
end
return widget
end
-- it updates existing widget, you should call CaveBot.save() later
CaveBot.editAction = function(widget, action, value)
action = action:lower()
local raction = CaveBot.Actions[action]
if not raction then
return warn("Invalid cavebot action: " .. action)
end
if not widget.action or not widget.value then
return warn("Invalid cavebot action widget, has missing action or value")
end
widget:setText(action .. ":" .. value:split("\n")[1])
widget.action = action
widget.value = value
if raction.color then
widget:setColor(raction.color)
end
return widget
end
--[[
registerAction:
action - string, color - string, callback = function(value, retries, prev)
value is a string value of action, retries is number which will grow by 1 if return is "retry"
prev is a true when previuos action was executed succesfully, false otherwise
it must return true if executed correctly, false otherwise
it can also return string "retry", then the function will be called again in 20 ms
]]--
CaveBot.registerAction = function(action, color, callback)
action = action:lower()
if CaveBot.Actions[action] then
return warn("Duplicated acction: " .. action)
end
CaveBot.Actions[action] = {
color=color,
callback=callback
}
end
CaveBot.registerAction("label", "yellow", function(value, retries, prev)
vBot.lastLabel = value
return true
end)
CaveBot.registerAction("gotolabel", "#FFFF55", function(value, retries, prev)
return CaveBot.gotoLabel(value)
end)
CaveBot.registerAction("delay", "#AAAAAA", function(value, retries, prev)
if retries == 0 then
local data = string.split(value, ",")
local val = tonumber(data[1]:trim())
local random
local final
if #data == 2 then
random = tonumber(data[2]:trim())
end
if random then
local diff = (val/100) * random
local min = val - diff
local max = val + diff
final = math.random(min, max)
end
final = final or val
CaveBot.delay(final)
return "retry"
end
return true
end)
CaveBot.registerAction("follow", "#FF8400", function(value, retries, prev)
local c = getCreatureByName(value)
if not c then
print("CaveBot[follow]: can't find creature to follow")
return false
end
local cpos = c:getPosition()
local pos = pos()
if getDistanceBetween(cpos, pos) < 2 then
g_game.cancelFollow()
return true
else
follow(c)
delay(200)
return "retry"
end
end)
CaveBot.registerAction("function", "red", function(value, retries, prev)
local prefix = "local retries = " .. retries .. "\nlocal prev = " .. tostring(prev) .. "\nlocal delay = CaveBot.delay\nlocal gotoLabel = CaveBot.gotoLabel\n"
prefix = prefix .. "local macro = function() warn('Macros inside cavebot functions are not allowed') end\n"
for extension, callbacks in pairs(CaveBot.Extensions) do
prefix = prefix .. "local " .. extension .. " = CaveBot.Extensions." .. extension .. "\n"
end
local status, result = pcall(function()
return assert(load(prefix .. value, "cavebot_function"))()
end)
if not status then
warn("warn in cavebot function:\n" .. result)
return false
end
return result
end)
CaveBot.registerAction("goto", "green", function(value, retries, prev)
local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)")
if not pos[1] then
warn("Invalid cavebot goto action value. It should be position (x,y,z), is: " .. value)
return false
end
-- reset pathfinder
nextPosF = nil
nextPos = nil
if CaveBot.Config.get("mapClick") then
if retries >= 5 then
noPath = noPath + 1
pathfinder()
return false -- tried 5 times, can't get there
end
else
if retries >= 100 then
noPath = noPath + 1
pathfinder()
return false -- tried 100 times, can't get there
end
end
local precision = tonumber(pos[1][5])
pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
local playerPos = player:getPosition()
if pos.z ~= playerPos.z then
noPath = noPath + 1
pathfinder()
return false -- different floor
end
local maxDist = storage.extras.gotoMaxDistance or 40
if math.abs(pos.x-playerPos.x) + math.abs(pos.y-playerPos.y) > maxDist then
noPath = noPath + 1
pathfinder()
return false -- too far way
end
local minimapColor = g_map.getMinimapColor(pos)
local stairs = (minimapColor >= 210 and minimapColor <= 213)
if stairs then
if math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 0 then
noPath = 0
return true -- already at position
end
elseif math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= (precision or 1) then
noPath = 0
return true -- already at position
end
-- check if there's a path to that place, ignore creatures and fields
local path = findPath(playerPos, pos, maxDist, { ignoreNonPathable = true, precision = 1, ignoreCreatures = true, allowUnseen = true, allowOnlyVisibleTiles = false })
if not path then
if breakFurniture(pos, storage.extras.machete) then
CaveBot.delay(1000)
retries = 0
return "retry"
end
noPath = noPath + 1
pathfinder()
return false -- there's no way
end
-- check if there's a path to destination but consider Creatures (attack only if trapped)
local path2 = findPath(playerPos, pos, maxDist, { ignoreNonPathable = true, precision = 1 })
if not path2 then
local foundMonster = false
for i, dir in ipairs(path) do
local dirs = modPos(dir)
nextPos = nextPos or playerPos
nextPos.x = nextPos.x + dirs[1]
nextPos.y = nextPos.y + dirs[2]
local tile = g_map.getTile(nextPos)
if tile then
if tile:hasCreature() then
local creature = tile:getCreatures()[1]
local hppc = creature:getHealthPercent()
if creature:isMonster() and (hppc and hppc > 0) and (oldTibia or creature:getType() < 3) then
-- real blocking creature can not meet those conditions - ie. it could be player, so just in case check if the next creature is reachable
local path = findPath(playerPos, creature:getPosition(), 7, { ignoreNonPathable = true, precision = 1 })
if path then
foundMonster = true
if g_game.getAttackingCreature() ~= creature then
if distanceFromPlayer(creature:getPosition()) > 3 then
CaveBot.walkTo(creature:getPosition(), 7, { ignoreNonPathable = true, precision = 1 })
else
attack(creature)
end
end
g_game.setChaseMode(1)
CaveBot.delay(100)
retries = 0 -- reset retries, we are trying to unclog the cavebot
break
end
end
end
end
end
if not foundMonster then
foundMonster = false
return false -- no other way
end
end
-- try to find path, don't ignore creatures, don't ignore fields
if not CaveBot.Config.get("ignoreFields") and CaveBot.walkTo(pos, 40) then
return "retry"
end
-- try to find path, don't ignore creatures, ignore fields
if CaveBot.walkTo(pos, maxDist, { ignoreNonPathable = true, allowUnseen = true, allowOnlyVisibleTiles = false }) then
return "retry"
end
if retries >= 3 then
-- try to lower precision, find something close to final position
local precison = retries - 1
if stairs then
precison = 0
end
if CaveBot.walkTo(pos, 50, { ignoreNonPathable = true, precision = precison, allowUnseen = true, allowOnlyVisibleTiles = false }) then
return "retry"
end
end
if not CaveBot.Config.get("mapClick") and retries >= 5 then
noPath = noPath + 1
pathfinder()
return false
end
if CaveBot.Config.get("skipBlocked") then
noPath = noPath + 1
pathfinder()
return false
end
-- everything else failed, try to walk ignoring creatures, maybe will work
CaveBot.walkTo(pos, maxDist, { ignoreNonPathable = true, precision = 1, ignoreCreatures = true, allowUnseen = true, allowOnlyVisibleTiles = false })
return "retry"
end)
CaveBot.registerAction("use", "#FFB272", function(value, retries, prev)
local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
if not pos[1] then
local itemid = tonumber(value)
if not itemid then
warn("Invalid cavebot use action value. It should be (x,y,z) or item id, is: " .. value)
return false
end
use(itemid)
return true
end
pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
local playerPos = player:getPosition()
if pos.z ~= playerPos.z then
return false -- different floor
end
if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then
return false -- too far way
end
local tile = g_map.getTile(pos)
if not tile then
return false
end
local topThing = tile:getTopUseThing()
if not topThing then
return false
end
use(topThing)
CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping"))
return true
end)
CaveBot.registerAction("usewith", "#EEB292", function(value, retries, prev)
local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
if not pos[1] then
if not itemid then
warn("Invalid cavebot usewith action value. It should be (itemid,x,y,z) or item id, is: " .. value)
return false
end
use(itemid)
return true
end
local itemid = tonumber(pos[1][2])
pos = {x=tonumber(pos[1][3]), y=tonumber(pos[1][4]), z=tonumber(pos[1][5])}
local playerPos = player:getPosition()
if pos.z ~= playerPos.z then
return false -- different floor
end
if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then
return false -- too far way
end
local tile = g_map.getTile(pos)
if not tile then
return false
end
local topThing = tile:getTopUseThing()
if not topThing then
return false
end
usewith(itemid, topThing)
CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping"))
return true
end)
CaveBot.registerAction("say", "#FF55FF", function(value, retries, prev)
say(value)
return true
end)
CaveBot.registerAction("npcsay", "#FF55FF", function(value, retries, prev)
NPC.say(value)
return true
end)

View File

@ -0,0 +1,92 @@
CaveBot.Extensions.Bank = {}
local balance = 0
CaveBot.Extensions.Bank.setup = function()
CaveBot.registerAction("bank", "#db5a5a", function(value, retries)
local data = string.split(value, ",")
local waitVal = 300
local amount = 0
local actionType
local npcName
local transferName
local balanceLeft
if #data ~= 3 and #data ~= 2 and #data ~= 4 then
warn("CaveBot[Bank]: incorrect value!")
return false
else
actionType = data[1]:trim():lower()
npcName = data[2]:trim()
if #data == 3 then
amount = tonumber(data[3]:trim())
end
if #data == 4 then
transferName = data[3]:trim()
balanceLeft = tonumber(data[4]:trim())
end
end
if actionType ~= "withdraw" and actionType ~= "deposit" and actionType ~= "transfer" then
warn("CaveBot[Bank]: incorrect action type! should be withdraw/deposit/transfer, is: " .. actionType)
return false
elseif actionType == "withdraw" then
local value = tonumber(amount)
if not value then
warn("CaveBot[Bank]: incorrect amount value! should be number, is: " .. amount)
return false
end
end
if retries > 5 then
print("CaveBot[Bank]: too many tries, skipping")
return false
end
local npc = getCreatureByName(npcName)
if not npc then
print("CaveBot[Bank]: NPC not found, skipping")
return false
end
if not CaveBot.ReachNPC(npcName) then
return "retry"
end
if actionType == "deposit" then
CaveBot.Conversation("hi", "deposit all", "yes")
CaveBot.delay(storage.extras.talkDelay*3)
return true
elseif actionType == "withdraw" then
CaveBot.Conversation("hi", "withdraw", value, "yes")
CaveBot.delay(storage.extras.talkDelay*4)
return true
else
-- first check balance
CaveBot.Conversation("hi", "balance")
schedule(5000, function()
local amountToTransfer = balance - balanceLeft
if amountToTransfer <= 0 then
warn("CaveBot[Bank] Not enough gold to transfer! proceeding")
return false
end
CaveBot.Conversation("hi", "transfer", amountToTransfer, transferName, "yes")
warn("CaveBot[Bank] transferred "..amountToTransfer.." gold to: "..transferName)
end)
CaveBot.delay(storage.extras.talkDelay*11)
return true
end
end)
CaveBot.Editor.registerAction("bank", "bank", {
value="action, NPC name",
title="Banker",
description="action type(withdraw/deposit/transfer), NPC name, (if withdraw: amount|if transfer: name, balance left)",
})
end
onTalk(function(name, level, mode, text, channelId, pos)
if mode == 51 and text:find("Your account balance is") then
balance = getFirstNumberInText(text)
end
end)

View File

@ -0,0 +1,76 @@
CaveBot.Extensions.BuySupplies = {}
CaveBot.Extensions.BuySupplies.setup = function()
CaveBot.registerAction("BuySupplies", "#C300FF", function(value, retries)
local possibleItems = {}
local val = string.split(value, ",")
local waitVal
if #val == 0 or #val > 2 then
warn("CaveBot[BuySupplies]: incorrect BuySupplies value")
return false
elseif #val == 2 then
waitVal = tonumber(val[2]:trim())
end
local npcName = val[1]:trim()
local npc = getCreatureByName(npcName)
if not npc then
print("CaveBot[BuySupplies]: NPC not found")
return false
end
if not waitVal and #val == 2 then
warn("CaveBot[BuySupplies]: incorrect delay values!")
elseif waitVal and #val == 2 then
delay(waitVal)
end
if retries > 50 then
print("CaveBot[BuySupplies]: Too many tries, can't buy")
return false
end
if not CaveBot.ReachNPC(npcName) then
return "retry"
end
if not NPC.isTrading() then
CaveBot.OpenNpcTrade()
CaveBot.delay(storage.extras.talkDelay*2)
return "retry"
end
-- get items from npc
local npcItems = NPC.getBuyItems()
for i,v in pairs(npcItems) do
table.insert(possibleItems, v.id)
end
for id, values in pairs(Supplies.getItemsData()) do
id = tonumber(id)
if table.find(possibleItems, id) then
local max = values.max
local current = player:getItemsCount(id)
local toBuy = max - current
if toBuy > 0 then
toBuy = math.min(100, toBuy)
NPC.buy(id, math.min(100, toBuy))
print("CaveBot[BuySupplies]: bought " .. toBuy .. "x " .. id)
return "retry"
end
end
end
print("CaveBot[BuySupplies]: bought everything, proceeding")
return true
end)
CaveBot.Editor.registerAction("buysupplies", "buy supplies", {
value="NPC name",
title="Buy Supplies",
description="NPC Name, delay(in ms, optional)",
})
end

View File

@ -0,0 +1,447 @@
local cavebotMacro = nil
local config = nil
-- ui
local configWidget = UI.Config()
local ui = UI.createWidget("CaveBotPanel")
ui.list = ui.listPanel.list -- shortcut
CaveBot.actionList = ui.list
if CaveBot.Editor then
CaveBot.Editor.setup()
end
if CaveBot.Config then
CaveBot.Config.setup()
end
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.setup then
callbacks.setup()
end
end
-- main loop, controlled by config
local actionRetries = 0
local prevActionResult = true
cavebotMacro = macro(20, function()
if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then
CaveBot.resetWalking()
return -- target bot or looting is working, wait
end
if CaveBot.doWalking() then
return -- executing walking3
end
local actions = ui.list:getChildCount()
if actions == 0 then return end
local currentAction = ui.list:getFocusedChild()
if not currentAction then
currentAction = ui.list:getFirstChild()
end
local action = CaveBot.Actions[currentAction.action]
local value = currentAction.value
local retry = false
if action then
local status, result = pcall(function()
CaveBot.resetWalking()
return action.callback(value, actionRetries, prevActionResult)
end)
if status then
if result == "retry" then
actionRetries = actionRetries + 1
retry = true
elseif type(result) == 'boolean' then
actionRetries = 0
prevActionResult = result
else
warn("Invalid return from cavebot action (" .. currentAction.action .. "), should be \"retry\", false or true, is: " .. tostring(result))
end
else
warn("warn while executing cavebot action (" .. currentAction.action .. "):\n" .. result)
end
else
warn("Invalid cavebot action: " .. currentAction.action)
end
if retry then
return
end
if currentAction ~= ui.list:getFocusedChild() then
-- focused child can change durring action, get it again and reset state
currentAction = ui.list:getFocusedChild() or ui.list:getFirstChild()
actionRetries = 0
prevActionResult = true
end
local nextAction = ui.list:getChildIndex(currentAction) + 1
if nextAction > actions then
nextAction = 1
end
ui.list:focusChild(ui.list:getChildByIndex(nextAction))
end)
-- config, its callback is called immediately, data can be nil
local lastConfig = ""
config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, enabled, data)
if enabled and CaveBot.Recorder.isOn() then
CaveBot.Recorder.disable()
CaveBot.setOff()
return
end
local currentActionIndex = ui.list:getChildIndex(ui.list:getFocusedChild())
ui.list:destroyChildren()
if not data then return cavebotMacro.setOff() end
local cavebotConfig = nil
for k,v in ipairs(data) do
if type(v) == "table" and #v == 2 then
if v[1] == "config" then
local status, result = pcall(function()
return json.decode(v[2])
end)
if not status then
warn("warn while parsing CaveBot extensions from config:\n" .. result)
else
cavebotConfig = result
end
elseif v[1] == "extensions" then
local status, result = pcall(function()
return json.decode(v[2])
end)
if not status then
warn("warn while parsing CaveBot extensions from config:\n" .. result)
else
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.onConfigChange then
callbacks.onConfigChange(name, enabled, result[extension])
end
end
end
else
CaveBot.addAction(v[1], v[2])
end
end
end
CaveBot.Config.onConfigChange(name, enabled, cavebotConfig)
actionRetries = 0
CaveBot.resetWalking()
prevActionResult = true
cavebotMacro.setOn(enabled)
cavebotMacro.delay = nil
if lastConfig == name then
-- restore focused child on the action list
ui.list:focusChild(ui.list:getChildByIndex(currentActionIndex))
end
lastConfig = name
end)
-- ui callbacks
ui.showEditor.onClick = function()
if not CaveBot.Editor then return end
if ui.showEditor:isOn() then
CaveBot.Editor.hide()
ui.showEditor:setOn(false)
else
CaveBot.Editor.show()
ui.showEditor:setOn(true)
end
end
ui.showConfig.onClick = function()
if not CaveBot.Config then return end
if ui.showConfig:isOn() then
CaveBot.Config.hide()
ui.showConfig:setOn(false)
else
CaveBot.Config.show()
ui.showConfig:setOn(true)
end
end
-- public function, you can use them in your scripts
CaveBot.isOn = function()
return config.isOn()
end
CaveBot.isOff = function()
return config.isOff()
end
CaveBot.setOn = function(val)
if val == false then
return CaveBot.setOff(true)
end
config.setOn()
end
CaveBot.setOff = function(val)
if val == false then
return CaveBot.setOn(true)
end
config.setOff()
end
CaveBot.getCurrentProfile = function()
return storage._configs.cavebot_configs.selected
end
CaveBot.lastReachedLabel = function()
return vBot.lastLabel
end
CaveBot.gotoNextWaypointInRange = function()
local currentAction = ui.list:getFocusedChild()
local index = ui.list:getChildIndex(currentAction)
local actions = ui.list:getChildren()
-- start searching from current index
for i, child in ipairs(actions) do
if i > index then
local text = child:getText()
if string.starts(text, "goto:") then
local re = regexMatch(text, [[(?:goto:)([^,]+),([^,]+),([^,]+)]])
local pos = {x = tonumber(re[1][2]), y = tonumber(re[1][3]), z = tonumber(re[1][4])}
if posz() == pos.z then
local maxDist = storage.extras.gotoMaxDistance
if distanceFromPlayer(pos) <= maxDist then
if findPath(player:getPosition(), pos, maxDist, { ignoreNonPathable = true }) then
ui.list:focusChild(ui.list:getChildByIndex(i-1))
return true
end
end
end
end
end
end
-- if not found then damn go from start
for i, child in ipairs(actions) do
if i <= index then
local text = child:getText()
if string.starts(text, "goto:") then
local re = regexMatch(text, [[(?:goto:)([^,]+),([^,]+),([^,]+)]])
local pos = {x = tonumber(re[1][2]), y = tonumber(re[1][3]), z = tonumber(re[1][4])}
if posz() == pos.z then
local maxDist = storage.extras.gotoMaxDistance
if distanceFromPlayer(pos) <= maxDist then
if findPath(player:getPosition(), pos, maxDist, { ignoreNonPathable = true }) then
ui.list:focusChild(ui.list:getChildByIndex(i-1))
return true
end
end
end
end
end
end
-- not found
return false
end
local function reverseTable(t, max)
local reversedTable = {}
local itemCount = max or #t
for i, v in ipairs(t) do
reversedTable[itemCount + 1 - i] = v
end
return reversedTable
end
function rpairs(t)
test()
return function(t, i)
i = i - 1
if i ~= 0 then
return i, t[i]
end
end, t, #t + 1
end
CaveBot.gotoFirstPreviousReachableWaypoint = function()
local currentAction = ui.list:getFocusedChild()
local currentIndex = ui.list:getChildIndex(currentAction)
local index = ui.list:getChildIndex(currentAction)
-- check up to 100 childs
for i=0,100 do
index = index - i
if index <= 0 or index > currentIndex or math.abs(index-currentIndex) > 100 then
break
end
local child = ui.list:getChildByIndex(index)
if child then
local text = child:getText()
if string.starts(text, "goto:") then
local re = regexMatch(text, [[(?:goto:)([^,]+),([^,]+),([^,]+)]])
local pos = {x = tonumber(re[1][2]), y = tonumber(re[1][3]), z = tonumber(re[1][4])}
if posz() == pos.z then
if distanceFromPlayer(pos) <= storage.extras.gotoMaxDistance/2 then
print("found pos, going back "..currentIndex-index.. " waypoints.")
return ui.list:focusChild(child)
end
end
end
end
end
-- not found
print("previous pos not found, proceeding")
return false
end
CaveBot.getFirstWaypointBeforeLabel = function(label)
label = "label:"..label
label = label:lower()
local actions = ui.list:getChildren()
local index
-- find index of label
for i, child in pairs(actions) do
local name = child:getText():lower()
if name == label then
index = i
break
end
end
-- if there's no index then label was not found
if not index then return false end
for i=1,#actions do
if index - 1 < 1 then
-- did not found any waypoint in range before label
return false
end
local child = ui.list:getChildByIndex(index-i)
if child then
local text = child:getText()
if string.starts(text, "goto:") then
local re = regexMatch(text, [[(?:goto:)([^,]+),([^,]+),([^,]+)]])
local pos = {x = tonumber(re[1][2]), y = tonumber(re[1][3]), z = tonumber(re[1][4])}
if posz() == pos.z then
if distanceFromPlayer(pos) <= storage.extras.gotoMaxDistance/2 then
return ui.list:focusChild(child)
end
end
end
end
end
end
CaveBot.getPreviousLabel = function()
local actions = ui.list:getChildren()
-- check if config is empty
if #actions == 0 then return false end
local currentAction = ui.list:getFocusedChild()
--check we made any progress in waypoints, if no focused or first then no point checking
if not currentAction or currentAction == ui.list:getFirstChild() then return false end
local index = ui.list:getChildIndex(currentAction)
-- if not index then something went wrong and there's no selected child
if not index then return false end
for i=1,#actions do
if index - i < 1 then
-- did not found any waypoint in range before label
return false
end
local child = ui.list:getChildByIndex(index-i)
if child then
if child.action == "label" then
return child.value
end
end
end
end
CaveBot.getNextLabel = function()
local actions = ui.list:getChildren()
-- check if config is empty
if #actions == 0 then return false end
local currentAction = ui.list:getFocusedChild() or ui.list:getFirstChild()
local index = ui.list:getChildIndex(currentAction)
-- if not index then something went wrong
if not index then return false end
for i=1,#actions do
if index + i > #actions then
-- did not found any waypoint in range before label
return false
end
local child = ui.list:getChildByIndex(index+i)
if child then
if child.action == "label" then
return child.value
end
end
end
end
local botConfigName = modules.game_bot.contentsPanel.config:getCurrentOption().text
CaveBot.setCurrentProfile = function(name)
if not g_resources.fileExists("/bot/"..botConfigName.."/cavebot_configs/"..name..".cfg") then
return warn("there is no cavebot profile with that name!")
end
CaveBot.setOff()
storage._configs.cavebot_configs.selected = name
CaveBot.setOn()
end
CaveBot.delay = function(value)
cavebotMacro.delay = math.max(cavebotMacro.delay or 0, now + value)
end
CaveBot.gotoLabel = function(label)
label = label:lower()
for index, child in ipairs(ui.list:getChildren()) do
if child.action == "label" and child.value:lower() == label then
ui.list:focusChild(child)
return true
end
end
return false
end
CaveBot.save = function()
local data = {}
for index, child in ipairs(ui.list:getChildren()) do
table.insert(data, {child.action, child.value})
end
if CaveBot.Config then
table.insert(data, {"config", json.encode(CaveBot.Config.save())})
end
local extension_data = {}
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.onSave then
local ext_data = callbacks.onSave()
if type(ext_data) == "table" then
extension_data[extension] = ext_data
end
end
end
table.insert(data, {"extensions", json.encode(extension_data, 2)})
config.save(data)
end
CaveBotList = function()
return ui.list
end

View File

@ -0,0 +1,58 @@
CaveBotAction < Label
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #00000055
CaveBotPanel < Panel
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 2
margin-bottom: 5
Panel
id: listPanel
height: 100
margin-top: 2
TextList
id: list
anchors.fill: parent
vertical-scrollbar: listScrollbar
margin-right: 15
focusable: false
auto-focus: first
VerticalScrollBar
id: listScrollbar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
pixels-scroll: true
step: 10
BotSwitch
id: showEditor
margin-top: 2
$on:
text: Hide waypoints editor
$!on:
text: Show waypoints editor
BotSwitch
id: showConfig
margin-top: 2
$on:
text: Hide config
$!on:
text: Show config

View File

@ -0,0 +1,128 @@
CaveBot.Extensions.ClearTile = {}
CaveBot.Extensions.ClearTile.setup = function()
CaveBot.registerAction("ClearTile", "#00FFFF", function(value, retries)
local data = string.split(value, ",")
local pos = {x=tonumber(data[1]), y=tonumber(data[2]), z=tonumber(data[3])}
local doors = false
local stand = false
local pPos = player:getPosition()
for i, value in ipairs(data) do
value = value:lower():trim()
if value == "stand" then
stand = true
elseif value == "doors" then
doors = true
end
end
if not #pos == 3 then
warn("CaveBot[ClearTile]: invalid value. It should be position (x,y,z), is: " .. value)
return false
end
if retries >= 20 then
print("CaveBot[ClearTile]: too many tries, can't clear it")
return false -- tried 20 times, can't clear it
end
if getDistanceBetween(player:getPosition(), pos) == 0 then
print("CaveBot[ClearTile]: tile reached, proceeding")
return true
end
local tile = g_map.getTile(pos)
if not tile then
print("CaveBot[ClearTile]: can't find tile or tile is unreachable, skipping")
return false
end
local tPos = tile:getPosition()
-- no items on tile and walkability means we are done
if tile:isWalkable() and tile:getTopUseThing():isNotMoveable() and not tile:hasCreature() and not doors then
if stand then
if not CaveBot.MatchPosition(tPos, 0) then
CaveBot.GoTo(tPos, 0)
return "retry"
end
end
print("CaveBot[ClearTile]: tile clear, proceeding")
return true
end
if not CaveBot.MatchPosition(tPos, 3) then
CaveBot.GoTo(tPos, 3)
return "retry"
end
if retries > 0 then
delay(1100)
end
-- monster
if tile:hasCreature() then
local c = tile:getCreatures()[1]
if c:isMonster() then
attack(c)
return "retry"
end
end
-- moveable item
local item = tile:getTopMoveThing()
if item:isItem() then
if item and not item:isNotMoveable() then
print("CaveBot[ClearTile]: moving item... " .. item:getId().. " from tile")
g_game.move(item, pPos, item:getCount())
return "retry"
end
end
-- player
-- push creature
if tile:hasCreature() then
local c = tile:getCreatures()[1]
if c and c:isPlayer() then
local candidates = {}
for _, tile in ipairs(g_map.getTiles(posz())) do
local tPos = tile:getPosition()
if getDistanceBetween(c:getPosition(), tPos) == 1 and tPos ~= pPos and tile:isWalkable() then
table.insert(candidates, tPos)
end
end
if #candidates == 0 then
print("CaveBot[ClearTile]: can't find tile to push, cannot clear way, skipping")
return false
else
print("CaveBot[ClearTile]: pushing player... " .. c:getName() .. " out of the way")
local pos = candidates[math.random(1,#candidates)]
local tile = g_map.getTile(pos)
tile:setText("here")
schedule(500, function() tile:setText("") end)
g_game.move(c, pos, 1)
return "retry"
end
end
end
-- doors
if doors then
use(tile:getTopUseThing())
return "retry"
end
return "retry"
end)
CaveBot.Editor.registerAction("cleartile", "clear tile", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="position of tile to clear",
description="tile position (x,y,z), doors/stand - optional",
multiline=false
})
end

View File

@ -0,0 +1,111 @@
-- config for bot
CaveBot.Config = {}
CaveBot.Config.values = {}
CaveBot.Config.default_values = {}
CaveBot.Config.value_setters = {}
CaveBot.Config.setup = function()
CaveBot.Config.ui = UI.createWidget("CaveBotConfigPanel")
local ui = CaveBot.Config.ui
local add = CaveBot.Config.add
add("ping", "Server ping", 100)
add("walkDelay", "Walk delay", 10)
add("mapClick", "Use map click", false)
add("mapClickDelay", "Map click delay", 100)
add("ignoreFields", "Ignore fields", false)
add("skipBlocked", "Skip blocked path", false)
add("useDelay", "Delay after use", 400)
end
CaveBot.Config.show = function()
CaveBot.Config.ui:show()
end
CaveBot.Config.hide = function()
CaveBot.Config.ui:hide()
end
CaveBot.Config.onConfigChange = function(configName, isEnabled, configData)
for k, v in pairs(CaveBot.Config.default_values) do
CaveBot.Config.value_setters[k](v)
end
if not configData then return end
for k, v in pairs(configData) do
if CaveBot.Config.value_setters[k] then
CaveBot.Config.value_setters[k](v)
end
end
end
CaveBot.Config.save = function()
return CaveBot.Config.values
end
CaveBot.Config.add = function(id, title, defaultValue)
if CaveBot.Config.values[id] then
return warn("Duplicated config key: " .. id)
end
local panel
local setter -- sets value
if type(defaultValue) == "number" then
panel = UI.createWidget("CaveBotConfigNumberValuePanel", CaveBot.Config.ui)
panel:setId(id)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setText(value, true)
end
setter(defaultValue)
panel.value.onTextChange = function(widget, newValue)
newValue = tonumber(newValue)
if newValue then
CaveBot.Config.values[id] = newValue
CaveBot.save()
end
end
elseif type(defaultValue) == "boolean" then
panel = UI.createWidget("CaveBotConfigBooleanValuePanel", CaveBot.Config.ui)
panel:setId(id)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setOn(value, true)
end
setter(defaultValue)
panel.value.onClick = function(widget)
widget:setOn(not widget:isOn())
CaveBot.Config.values[id] = widget:isOn()
CaveBot.save()
end
else
return warn("Invalid default value of config for key " .. id .. ", should be number or boolean")
end
panel.title:setText(tr(title) .. ":")
CaveBot.Config.value_setters[id] = setter
CaveBot.Config.values[id] = defaultValue
CaveBot.Config.default_values[id] = defaultValue
end
CaveBot.Config.get = function(id)
if CaveBot.Config.values[id] == nil then
return warn("Invalid CaveBot.Config.get, id: " .. id)
end
return CaveBot.Config.values[id]
end
CaveBot.Config.set = function(id, value)
local valueType = CaveBot.Config.get(id)
local panel = CaveBot.Config.ui[id]
if valueType == 'boolean' then
CaveBot.Config.values[id] = value
panel.value:setOn(value, true)
CaveBot.save()
else
CaveBot.Config.values[id] = value
panel.value:setText(value, true)
CaveBot.save()
end
end

View File

@ -0,0 +1,57 @@
CaveBotConfigPanel < Panel
id: cavebotEditor
visible: false
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 5
Label
text-align: center
text: CaveBot Config
margin-top: 5
CaveBotConfigNumberValuePanel < Panel
height: 20
margin-top: 5
BotTextEdit
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
CaveBotConfigBooleanValuePanel < Panel
height: 20
margin-top: 5
BotSwitch
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
$on:
text: On
$!on:
text: Off
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5

View File

@ -0,0 +1,105 @@
CaveBot.Extensions.DWithdraw = {}
CaveBot.Extensions.DWithdraw.setup = function()
CaveBot.registerAction("dpwithdraw", "#002FFF", function(value, retries)
local capLimit
local data = string.split(value, ",")
if retries > 600 then
print("CaveBot[DepotWithdraw]: actions limit reached, proceeding")
return false
end
local destContainer
local depotContainer
delay(70)
-- input validation
if not value or #data ~= 3 and #data ~= 4 then
warn("CaveBot[DepotWithdraw]: incorrect value!")
return false
end
local indexDp = tonumber(data[1]:trim())
local destName = data[2]:trim():lower()
local destId = tonumber(data[3]:trim())
if #data == 4 then
capLimit = tonumber(data[4]:trim())
end
-- cap check
if freecap() < (capLimit or 200) then
for i, container in ipairs(getContainers()) do
if container:getName():lower():find("depot") or container:getName():lower():find("locker") then
g_game.close(container)
end
end
print("CaveBot[DepotWithdraw]: cap limit reached, proceeding")
return false
end
-- containers
for i, container in ipairs(getContainers()) do
local cName = container:getName():lower()
if destName == cName then
destContainer = container
elseif cName:find("depot box") then
depotContainer = container
end
end
if not destContainer then
print("CaveBot[DepotWithdraw]: container not found!")
return false
end
if containerIsFull(destContainer) then
for i, item in pairs(destContainer:getItems()) do
if item:getId() == destId then
g_game.open(item, destContainer)
return "retry"
end
end
end
-- stash validation
if depotContainer and #depotContainer:getItems() == 0 then
print("CaveBot[DepotWithdraw]: all items withdrawn")
g_game.close(depotContainer)
return true
end
if containerIsFull(destContainer) then
for i, item in pairs(destContainer:getItems()) do
if item:getId() == destId then
g_game.open(foundNextContainer, destContainer)
return "retry"
end
end
print("CaveBot[DepotWithdraw]: loot containers full!")
return false
end
if not CaveBot.OpenDepotBox(indexDp) then
return "retry"
end
CaveBot.PingDelay(2)
for i, container in pairs(g_game.getContainers()) do
if string.find(container:getName():lower(), "depot box") then
for j, item in ipairs(container:getItems()) do
statusMessage("[D_Withdraw] witdhrawing item: "..item:getId())
g_game.move(item, destContainer:getSlotPosition(destContainer:getItemsCount()), item:getCount())
return "retry"
end
end
end
return "retry"
end)
CaveBot.Editor.registerAction("dpwithdraw", "dpwithdraw", {
value="1, shopping bag, 21411",
title="Loot Withdraw",
description="insert index, destination container name and it's ID",
})
end

View File

@ -0,0 +1,138 @@
CaveBot.Extensions.Depositor = {}
--local variables
local destination = nil
local lootTable = nil
local reopenedContainers = false
local function resetCache()
reopenedContainers = false
destination = nil
lootTable = nil
for i, container in ipairs(getContainers()) do
if container:getName():lower():find("depot") or container:getName():lower():find("locker") then
g_game.close(container)
end
end
if storage.caveBot.backStop then
storage.caveBot.backStop = false
CaveBot.setOff()
elseif storage.caveBot.backTrainers then
storage.caveBot.backTrainers = false
CaveBot.gotoLabel('toTrainers')
elseif storage.caveBot.backOffline then
storage.caveBot.backOffline = false
CaveBot.gotoLabel('toOfflineTraining')
end
end
local description = g_game.getClientVersion() > 960 and "No - just deposit \n Yes - also reopen loot containers" or "currently not supported, will be added in near future"
CaveBot.Extensions.Depositor.setup = function()
CaveBot.registerAction("depositor", "#002FFF", function(value, retries)
-- version check, TODO old tibia
if g_game.getClientVersion() < 960 then
resetCache()
warn("CaveBot[Depositor]: unsupported Tibia version, will be added in near future")
return false
end
-- loot list check
lootTable = lootTable or CaveBot.GetLootItems()
if #lootTable == 0 then
print("CaveBot[Depositor]: no items in loot list. Wrong TargetBot Config? Proceeding")
resetCache()
return true
end
delay(70)
-- backpacks etc
if value:lower() == "yes" then
if not reopenedContainers then
CaveBot.CloseAllLootContainers()
delay(3000)
reopenedContainers = true
return "retry"
end
-- open next backpacks if no more loot
if not CaveBot.HasLootItems() then
local lootContainers = CaveBot.GetLootContainers()
for _, container in ipairs(getContainers()) do
local cId = container:getContainerItem():getId()
if table.find(lootContainers, cId) then
for i, item in ipairs(container:getItems()) do
if item:getId() == cId then
g_game.open(item, container)
delay(100)
return "retry"
end
end
end
end
-- couldn't find next container, so we done
print("CaveBot[Depositor]: all items stashed, no backpack to open next, proceeding")
CaveBot.CloseAllLootContainers()
delay(3000)
resetCache()
return true
end
end
-- first check items
if retries == 0 then
if not CaveBot.HasLootItems() then -- resource consuming function
print("CaveBot[Depositor]: no items to stash, proceeding")
resetCache()
return true
end
end
-- next check retries
if retries > 400 then
print("CaveBot[Depositor]: Depositor actions limit reached, proceeding")
resetCache()
return true
end
-- reaching and opening depot
if not CaveBot.ReachAndOpenDepot() then
return "retry"
end
-- add delay to prevent bugging
CaveBot.PingDelay(2)
-- prep time and stashing
destination = destination or getContainerByName("Depot chest")
if not destination then return "retry" end
for _, container in pairs(getContainers()) do
local name = container:getName():lower()
if not name:find("depot") and not name:find("your inbox") then
for _, item in pairs(container:getItems()) do
local id = item:getId()
if table.find(lootTable, id) then
local index = getStashingIndex(id) or item:isStackable() and 1 or 0
statusMessage("[Depositer] stashing item: " ..id.. " to depot: "..index+1)
CaveBot.StashItem(item, index, destination)
return "retry"
end
end
end
end
-- we gucci
resetCache()
return true
end)
CaveBot.Editor.registerAction("depositor", "depositor", {
value="no",
title="Depositor",
description=description,
validation="(yes|Yes|YES|no|No|NO)"
})
end

View File

@ -0,0 +1,58 @@
CaveBot.Extensions.OpenDoors = {}
CaveBot.Extensions.OpenDoors.setup = function()
CaveBot.registerAction("OpenDoors", "#00FFFF", function(value, retries)
local pos = string.split(value, ",")
local key = nil
if #pos == 4 then
key = tonumber(pos[4])
end
if not pos[1] then
warn("CaveBot[OpenDoors]: invalid value. It should be position (x,y,z), is: " .. value)
return false
end
if retries >= 5 then
print("CaveBot[OpenDoors]: too many tries, can't open doors")
return false -- tried 5 times, can't open
end
pos = {x=tonumber(pos[1]), y=tonumber(pos[2]), z=tonumber(pos[3])}
local doorTile
if not doorTile then
for i, tile in ipairs(g_map.getTiles(posz())) do
if tile:getPosition().x == pos.x and tile:getPosition().y == pos.y and tile:getPosition().z == pos.z then
doorTile = tile
end
end
end
if not doorTile then
return false
end
if not doorTile:isWalkable() then
if not key then
use(doorTile:getTopUseThing())
delay(200)
return "retry"
else
useWith(key, doorTile:getTopUseThing())
delay(200)
return "retry"
end
else
print("CaveBot[OpenDoors]: possible to cross, proceeding")
return true
end
end)
CaveBot.Editor.registerAction("opendoors", "open doors", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Door position",
description="doors position (x,y,z) and key id (optional)",
multiline=false,
validation=[[\d{1,5},\d{1,5},\d{1,2}(?:,\d{1,5}$|$)]]
})
end

View File

@ -0,0 +1,186 @@
CaveBot.Editor = {}
CaveBot.Editor.Actions = {}
-- also works as registerAction(action, params), then text == action
-- params are options for text editor or function to be executed when clicked
-- you have many examples how to use it bellow
CaveBot.Editor.registerAction = function(action, text, params)
if type(text) ~= 'string' then
params = text
text = action
end
local color = nil
if type(params) ~= 'function' then
local raction = CaveBot.Actions[action]
if not raction then
return warn("CaveBot editor warn: action " .. action .. " doesn't exist")
end
CaveBot.Editor.Actions[action] = params
color = raction.color
end
local button = UI.createWidget('CaveBotEditorButton', CaveBot.Editor.ui.buttons)
button:setText(text)
if color then
button:setColor(color)
end
button.onClick = function()
if type(params) == 'function' then
params()
return
end
CaveBot.Editor.edit(action, nil, function(action, value)
local focusedAction = CaveBot.actionList:getFocusedChild()
local index = CaveBot.actionList:getChildCount()
if focusedAction then
index = CaveBot.actionList:getChildIndex(focusedAction)
end
local widget = CaveBot.addAction(action, value)
CaveBot.actionList:moveChildToIndex(widget, index + 1)
CaveBot.actionList:focusChild(widget)
CaveBot.save()
end)
end
return button
end
CaveBot.Editor.setup = function()
CaveBot.Editor.ui = UI.createWidget("CaveBotEditorPanel")
local ui = CaveBot.Editor.ui
local registerAction = CaveBot.Editor.registerAction
registerAction("move up", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
local index = CaveBot.actionList:getChildIndex(action)
if index < 2 then return end
CaveBot.actionList:moveChildToIndex(action, index - 1)
CaveBot.actionList:ensureChildVisible(action)
CaveBot.save()
end)
registerAction("edit", function()
local action = CaveBot.actionList:getFocusedChild()
if not action or not action.onDoubleClick then return end
action.onDoubleClick(action)
end)
registerAction("move down", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
local index = CaveBot.actionList:getChildIndex(action)
if index >= CaveBot.actionList:getChildCount() then return end
CaveBot.actionList:moveChildToIndex(action, index + 1)
CaveBot.actionList:ensureChildVisible(action)
CaveBot.save()
end)
registerAction("remove", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
action:destroy()
CaveBot.save()
end)
registerAction("label", {
value="labelName",
title="Label",
description="Add label",
multiline=false
})
registerAction("delay", {
value="500",
title="Delay",
description="Delay next action (in milliseconds),randomness (in percent-optional)",
multiline=false,
validation="^[0-9]{1,10}$|^[0-9]{1,10},[0-9]{1,4}$"
})
registerAction("gotolabel", "go to label", {
value="labelName",
title="Go to label",
description="Go to label",
multiline=false
})
registerAction("goto", "go to", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Go to position",
description="Go to position (x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)$"
})
registerAction("use", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Use",
description="Use item from position (x,y,z) or from inventory (itemId)",
multiline=false
})
registerAction("usewith", "use with", {
value=function() return "itemId," .. posx() .. "," .. posy() .. "," .. posz() end,
title="Use with",
description="Use item at position (itemid,x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
})
registerAction("say", {
value="text",
title="Say",
description="Enter text to say",
multiline=false
})
registerAction("follow", {
value="NPC name",
title="Follow Creature",
description="insert creature name to follow",
multiline=false
})
registerAction("npcsay", {
value="text",
title="NPC Say",
description="Enter text to NPC say",
multiline=false
})
registerAction("function", {
title="Edit bot function",
multiline=true,
value=CaveBot.Editor.ExampleFunctions[1][2],
examples=CaveBot.Editor.ExampleFunctions,
width=650
})
ui.autoRecording.onClick = function()
if ui.autoRecording:isOn() then
CaveBot.Recorder.disable()
else
CaveBot.Recorder.enable()
end
end
-- callbacks
onPlayerPositionChange(function(pos)
ui.pos:setText("Position: " .. pos.x .. ", " .. pos.y .. ", " .. pos.z)
end)
ui.pos:setText("Position: " .. posx() .. ", " .. posy() .. ", " .. posz())
end
CaveBot.Editor.show = function()
CaveBot.Editor.ui:show()
end
CaveBot.Editor.hide = function()
CaveBot.Editor.ui:hide()
end
CaveBot.Editor.edit = function(action, value, callback) -- callback = function(action, value)
local params = CaveBot.Editor.Actions[action]
if not params then return end
if not value then
if type(params.value) == 'function' then
value = params.value()
elseif type(params.value) == 'string' then
value = params.value
end
end
UI.EditorWindow(value, params, function(newText)
callback(action, newText)
end)
end

View File

@ -0,0 +1,44 @@
CaveBotEditorButton < Button
CaveBotEditorPanel < Panel
id: cavebotEditor
visible: false
layout:
type: verticalBox
fit-children: true
Label
id: pos
text-align: center
text: -
Panel
id: buttons
margin-top: 2
layout:
type: grid
cell-size: 86 20
cell-spacing: 1
flow: true
fit-children: true
Label
text: Double click on action from action list to edit it
text-align: center
text-auto-resize: true
text-wrap: true
margin-top: 3
margin-left: 2
margin-right: 2
BotSwitch
id: autoRecording
text: Auto Recording
margin-top: 3
BotButton
margin-top: 3
margin-bottom: 3
text: Documentation
@onClick: g_platform.openUrl("http://bot.otclient.ovh/")

View File

@ -0,0 +1,114 @@
CaveBot.Editor.ExampleFunctions = {}
local function addExampleFunction(title, text)
return table.insert(CaveBot.Editor.ExampleFunctions, {title, text:trim()})
end
addExampleFunction("Click to browse example functions", [[
-- available functions/variables:
-- prev - result of previous action (true or false)
-- retries - number of retries of current function, goes up by one when you return "retry"
-- delay(number) - delays bot next action, value in milliseconds
-- gotoLabel(string) - goes to specific label, return true if label exists
-- you can easily access bot extensions, Depositer.run() instead of CaveBot.Extensions.Depositer.run()
-- also you can access bot global variables, like CaveBot, TargetBot
-- use storage variable to store date between calls
-- function should return false, true or "retry"
-- if "retry" is returned, function will be executed again in 20 ms (so better call delay before)
return true
]])
addExampleFunction("Check for PZ and wait until dropped", [[
if retries > 25 or not isPzLocked() then
return true
else
if isPoisioned() then
say("exana pox")
end
if isPzLocked() then
delay(8000)
end
return "retry"
end
]])
addExampleFunction("Check for stamina and imbues", [[
if stamina() < 900 or player:getSkillLevel(11) == 0 then CaveBot.setOff() return false else return true end
]])
addExampleFunction("buy 200 mana potion from npc Eryn", [[
--buy 200 mana potions
local npc = getCreatureByName("Eryn")
if not npc then
return false
end
if retries > 10 then
return false
end
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, {precision=3})
delay(300)
return "retry"
end
if not NPC.isTrading() then
NPC.say("hi")
NPC.say("trade")
delay(200)
return "retry"
end
NPC.buy(268, 100)
schedule(1000, function()
-- buy again in 1s
NPC.buy(268, 100)
NPC.closeTrade()
NPC.say("bye")
end)
delay(1200)
return true
]])
addExampleFunction("Say hello 5 times with some delay", [[
--say hello
if retries > 5 then
return true -- finish
end
say("hello")
delay(100 + retries * 100)
return "retry"
]])
addExampleFunction("Disable TargetBot", [[
TargetBot.setOff()
return true
]])
addExampleFunction("Enable TargetBot", [[
TargetBot.setOn()
return true
]])
addExampleFunction("Enable TargetBot luring", [[
TargetBot.enableLuring()
return true
]])
addExampleFunction("Disable TargetBot luring", [[
TargetBot.disableLuring()
return true
]])
addExampleFunction("Logout", [[
g_game.safeLogout()
delay(1000)
return "retry"
]])
addExampleFunction("Close Loot Containers", [[
CaveBot.CloseAllLootContainers()
delay(3000)
return true
]])

View File

@ -0,0 +1,58 @@
-- example cavebot extension (remember to add this file to ../cavebot.lua)
CaveBot.Extensions.Example = {}
local ui
-- setup is called automaticly when cavebot is ready
CaveBot.Extensions.Example.setup = function()
ui = UI.createWidget('BotTextEdit')
ui:setText("Hello")
ui.onTextChange = function()
CaveBot.save() -- save new config when you change something
end
-- add custom cavebot action (check out actions.lua)
CaveBot.registerAction("sayhello", "orange", function(value, retries, prev)
local how_many_times = tonumber(value)
if retries >= how_many_times then
return true
end
say("hello " .. (retries + 1))
delay(250)
return "retry"
end)
-- add this custom action to editor (check out editor.lua)
CaveBot.Editor.registerAction("sayhello", "say hello", {
value="5",
title="Say hello",
description="Says hello x times",
validation="[0-9]{1,5}" -- regex, optional
})
end
-- called when cavebot config changes, configData is a table but it can also be nil
CaveBot.Extensions.Example.onConfigChange = function(configName, isEnabled, configData)
if not configData then return end
if configData["text"] then
ui:setText(configData["text"])
end
end
-- called when cavebot is saving config (so when CaveBot.save() is called), should return table or nil
CaveBot.Extensions.Example.onSave = function()
return {text=ui:getText()}
end
-- bellow add you custom functions to be used in cavebot function action
-- an example: return Example.run(retries, prev)
-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua and example_functions.lua to learn more
CaveBot.Extensions.Example.run = function(retries, prev)
-- it will say text 10 times with some delay and then continue
if retries > 10 then
return true
end
say(ui:getText() .. " x" .. retries)
delay(100 + retries * 100)
return "retry"
end

View File

@ -0,0 +1,119 @@
-- imbuing window should be handled separatly
-- reequiping should be handled separatly (ie. equipment manager)
CaveBot.Extensions.Imbuing = {}
local SHRINES = {25060, 25061, 25182, 25183}
local currentIndex = 1
local shrine = nil
local item = nil
local currentId = 0
local triedToTakeOff = false
local destination = nil
local function reset()
EquipManager.setOn()
shrine = nil
currentIndex = 1
item = nil
currentId = 0
triedToTakeOff = false
destination = nil
end
CaveBot.Extensions.Imbuing.setup = function()
CaveBot.registerAction("imbuing", "red", function(value, retries)
local data = string.split(value, ",")
local ids = {}
if #data == 0 and value ~= 'name' then
warn("CaveBot[Imbuing] no items added, proceeding")
reset()
return false
end
-- setting of equipment manager so it wont disturb imbuing process
EquipManager.setOff()
if value == 'name' then
local imbuData = AutoImbueTable[player:getName()]
for id, imbues in pairs(imbuData) do
table.insert(ids, id)
end
else
-- convert to number
for i, id in ipairs(data) do
id = tonumber(id)
if not table.find(ids, id) then
table.insert(ids, id)
end
end
end
-- all items imbued, can proceed
if currentIndex > #ids then
warn("CaveBot[Imbuing] used shrine on all items, proceeding")
reset()
return true
end
for _, tile in ipairs(g_map.getTiles(posz())) do
for _, item in ipairs(tile:getItems()) do
local id = item:getId()
if table.find(SHRINES, id) then
shrine = item
break
end
end
end
-- if not shrine
if not shrine then
warn("CaveBot[Imbuing] shrine not found! proceeding")
reset()
return false
end
destination = shrine:getPosition()
currentId = ids[currentIndex]
item = findItem(currentId)
-- maybe equipped? try to take off
if not item then
-- did try before, still not found so item is unavailable
if triedToTakeOff then
warn("CaveBot[Imbuing] item not found! skipping: "..currentId)
triedToTakeOff = false
currentIndex = currentIndex + 1
return "retry"
end
triedToTakeOff = true
g_game.equipItemId(currentId)
delay(1000)
return "retry"
end
-- we are past unequiping so just in case we were forced before, reset var
triedToTakeOff = false
-- reaching shrine
if not CaveBot.MatchPosition(destination, 1) then
CaveBot.GoTo(destination, 1)
delay(200)
return "retry"
end
useWith(shrine, item)
currentIndex = currentIndex + 1
warn("CaveBot[Imbuing] Using shrine on item: "..currentId)
delay(4000)
return "retry"
end)
CaveBot.Editor.registerAction("imbuing", "imbuing", {
value="name",
title="Auto Imbuing",
description="insert below item ids to be imbued, separated by comma\nor 'name' to load from file",
})
end

View File

@ -0,0 +1,91 @@
CaveBot.Extensions.InWithdraw = {}
CaveBot.Extensions.InWithdraw.setup = function()
CaveBot.registerAction("inwithdraw", "#002FFF", function(value, retries)
local data = string.split(value, ",")
local withdrawId
local amount
-- validation
if #data ~= 2 then
warn("CaveBot[InboxWithdraw]: incorrect withdraw value")
return false
else
withdrawId = tonumber(data[1])
amount = tonumber(data[2])
end
local currentAmount = itemAmount(withdrawId)
if currentAmount >= amount then
print("CaveBot[InboxWithdraw]: enough items, proceeding")
return true
end
if retries > 400 then
print("CaveBot[InboxWithdraw]: actions limit reached, proceeding")
return true
end
-- actions
local inboxContainer = getContainerByName("your inbox")
delay(100)
if not inboxContainer then
if not CaveBot.ReachAndOpenInbox() then
return "retry"
end
end
local inboxAmount = 0
if not inboxContainer then
return "retry"
end
for i, item in pairs(inboxContainer:getItems()) do
if item:getId() == withdrawId then
inboxAmount = inboxAmount + item:getCount()
end
end
if inboxAmount == 0 then
warn("CaveBot[InboxWithdraw]: not enough items in inbox container, proceeding")
g_game.close(inboxContainer)
return true
end
local destination
for i, container in pairs(getContainers()) do
if container:getCapacity() > #container:getItems() and not string.find(container:getName():lower(), "quiver") and not string.find(container:getName():lower(), "depot") and not string.find(container:getName():lower(), "loot") and not string.find(container:getName():lower(), "inbox") then
destination = container
end
end
if not destination then
print("CaveBot[InboxWithdraw]: couldn't find proper destination container, skipping")
g_game.close(inboxContainer)
return false
end
CaveBot.PingDelay(2)
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "your inbox") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
if item:isStackable() then
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), math.min(item:getCount(), (amount - currentAmount)))
return "retry"
else
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), 1)
return "retry"
end
return "retry"
end
end
end
end
end)
CaveBot.Editor.registerAction("inwithdraw", "in withdraw", {
value="id,amount",
title="Withdraw Items",
description="insert item id and amount",
})
end

View File

@ -0,0 +1,29 @@
CaveBot.Extensions.Lure = {}
CaveBot.Extensions.Lure.setup = function()
CaveBot.registerAction("lure", "#FF0090", function(value, retries)
value = value:lower()
if value == "start" then
TargetBot.setOff()
elseif value == "stop" then
TargetBot.setOn()
elseif value == "toggle" then
if TargetBot.isOn() then
TargetBot.setOff()
else
TargetBot.setOn()
end
else
warn("incorrect lure value!")
end
return true
end)
CaveBot.Editor.registerAction("lure", "lure", {
value="toggle",
title="Lure",
description="TargetBot: start, stop, toggle",
multiline=false,
validation=[[(start|stop|toggle)$]]
})
end

View File

@ -0,0 +1,26 @@
local minimap = modules.game_minimap.minimapWidget
minimap.onMouseRelease = function(widget,pos,button)
if not minimap.allowNextRelease then return true end
minimap.allowNextRelease = false
local mapPos = minimap:getTilePosition(pos)
if not mapPos then return end
if button == 1 then
local player = g_game.getLocalPlayer()
if minimap.autowalk then
player:autoWalk(mapPos)
end
return true
elseif button == 2 then
local menu = g_ui.createWidget('PopupMenu')
menu:setId("minimapMenu")
menu:setGameMenu(true)
menu:addOption(tr('Create mark'), function() minimap:createFlagWindow(mapPos) end)
menu:addOption(tr('Add CaveBot GoTo'), function() CaveBot.addAction("goto", mapPos.x .. "," .. mapPos.y .. "," .. mapPos.z, true) CaveBot.save() end)
menu:display(pos)
return true
end
return false
end

View File

@ -0,0 +1,47 @@
CaveBot.Extensions.PosCheck = {}
local posCheckRetries = 0
CaveBot.Extensions.PosCheck.setup = function()
CaveBot.registerAction("PosCheck", "#00FFFF", function(value, retries)
local tilePos
local data = string.split(value, ",")
if #data ~= 5 then
warn("wrong travel format, should be: label, distance, x, y, z")
return false
end
local tilePos = player:getPosition()
tilePos.x = tonumber(data[3])
tilePos.y = tonumber(data[4])
tilePos.z = tonumber(data[5])
if posCheckRetries > 10 then
posCheckRetries = 0
print("CaveBot[CheckPos]: waypoints locked, too many tries, unclogging cavebot and proceeding")
return false
elseif (tilePos.z == player:getPosition().z) and (getDistanceBetween(player:getPosition(), tilePos) <= tonumber(data[2])) then
posCheckRetries = 0
print("CaveBot[CheckPos]: position reached, proceeding")
return true
else
posCheckRetries = posCheckRetries + 1
if data[1] == "last" then
CaveBot.gotoFirstPreviousReachableWaypoint()
print("CaveBot[CheckPos]: position not-reached, going back to first reachable waypoint.")
return false
else
CaveBot.gotoLabel(data[1])
print("CaveBot[CheckPos]: position not-reached, going back to label: " .. data[1])
return false
end
end
end)
CaveBot.Editor.registerAction("poscheck", "pos check", {
value=function() return "last" .. "," .. "10" .. "," .. posx() .. "," .. posy() .. "," .. posz() end,
title="Location Check",
description="label name, accepted dist from coordinates, x, y, z",
multiline=false,
})
end

View File

@ -0,0 +1,69 @@
-- auto recording for cavebot
CaveBot.Recorder = {}
local isEnabled = nil
local lastPos = nil
local function setup()
local function addPosition(pos)
CaveBot.addAction("goto", pos.x .. "," .. pos.y .. "," .. pos.z, true)
lastPos = pos
end
local function addStairs(pos)
CaveBot.addAction("goto", pos.x .. "," .. pos.y .. "," .. pos.z .. ",0", true)
lastPos = pos
end
onPlayerPositionChange(function(newPos, oldPos)
if CaveBot.isOn() or not isEnabled then return end
if not lastPos then
-- first step
addPosition(oldPos)
elseif newPos.z ~= oldPos.z or math.abs(oldPos.x - newPos.x) > 1 or math.abs(oldPos.y - newPos.y) > 1 then
-- stairs/teleport
addStairs(oldPos)
elseif math.max(math.abs(lastPos.x - newPos.x), math.abs(lastPos.y - newPos.y)) > 5 then
-- 5 steps from last pos
addPosition(newPos)
end
end)
onUse(function(pos, itemId, stackPos, subType)
if CaveBot.isOn() or not isEnabled then return end
if pos.x ~= 0xFFFF then
lastPos = pos
CaveBot.addAction("use", pos.x .. "," .. pos.y .. "," .. pos.z, true)
end
end)
onUseWith(function(pos, itemId, target, subType)
if CaveBot.isOn() or not isEnabled then return end
if not target:isItem() then return end
local targetPos = target:getPosition()
if targetPos.x == 0xFFFF then return end
lastPos = pos
CaveBot.addAction("usewith", itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z, true)
end)
end
CaveBot.Recorder.isOn = function()
return isEnabled
end
CaveBot.Recorder.enable = function()
CaveBot.setOff()
if isEnabled == nil then
setup()
end
CaveBot.Editor.ui.autoRecording:setOn(true)
isEnabled = true
lastPos = nil
end
CaveBot.Recorder.disable = function()
if isEnabled == true then
isEnabled = false
end
CaveBot.Editor.ui.autoRecording:setOn(false)
CaveBot.save()
end

View File

@ -0,0 +1,76 @@
CaveBot.Extensions.SellAll = {}
local sellAllCap = 0
CaveBot.Extensions.SellAll.setup = function()
CaveBot.registerAction("SellAll", "#C300FF", function(value, retries)
local val = string.split(value, ",")
local wait
-- table formatting
for i, v in ipairs(val) do
v = v:trim()
v = tonumber(v) or v
val[i] = v
end
if table.find(val, "yes", true) then
wait = true
end
local npcName = val[1]
local npc = getCreatureByName(npcName)
if not npc then
print("CaveBot[SellAll]: NPC not found! skipping")
return false
end
if retries > 10 then
print("CaveBot[SellAll]: can't sell, skipping")
return false
end
if freecap() == sellAllCap then
sellAllCap = 0
print("CaveBot[SellAll]: Sold everything, proceeding")
return true
end
delay(800)
if not CaveBot.ReachNPC(npcName) then
return "retry"
end
if not NPC.isTrading() then
CaveBot.OpenNpcTrade()
delay(storage.extras.talkDelay*2)
return "retry"
else
sellAllCap = freecap()
end
storage.cavebotSell = storage.cavebotSell or {}
for i, item in ipairs(storage.cavebotSell) do
local data = type(item) == 'number' and item or item.id
if not table.find(val, data) then
table.insert(val, data)
end
end
table.dump(val)
modules.game_npctrade.sellAll(wait, val)
if wait then
print("CaveBot[SellAll]: Sold All with delay")
else
print("CaveBot[SellAll]: Sold All without delay")
end
return "retry"
end)
CaveBot.Editor.registerAction("sellall", "sell all", {
value="NPC",
title="Sell All",
description="NPC Name, 'yes' if sell with delay, exceptions: id separated by comma",
})
end

View File

@ -0,0 +1,186 @@
CaveBot.Extensions.StandLure = {}
local enable = nil
local function modPos(dir)
local y = 0
local x = 0
if dir == 0 then
y = -1
elseif dir == 1 then
x = 1
elseif dir == 2 then
y = 1
elseif dir == 3 then
x = -1
elseif dir == 4 then
y = -1
x = 1
elseif dir == 5 then
y = 1
x = 1
elseif dir == 6 then
y = 1
x = -1
elseif dir == 7 then
y = -1
x = -1
end
return {x, y}
end
local function reset(delay)
if type(Supplies.hasEnough()) == 'table' then
return
end
delay = delay or 0
CaveBot.delay(delay)
if delay == nil then
enable = nil
end
end
local resetRetries = false
CaveBot.Extensions.StandLure.setup = function()
CaveBot.registerAction(
"rushlure",
"#FF0090",
function(value, retries)
local nextPos = nil
local data = string.split(value, ",")
if not data[1] then
warn("Invalid cavebot lure action value. It should be position (x,y,z), delay(ms) is: " .. value)
return false
end
if type(Supplies.hasEnough()) == 'table' then -- do not execute if no supplies
return false
end
local pos = {x = tonumber(data[1]), y = tonumber(data[2]), z = tonumber(data[3])}
local delayTime = data[4] and tonumber(data[4]) or 1000
if not data[5] then
enable = nil
elseif data[5] == "yes" then
enable = true
else
enable = false
end
delay(100)
if retries > 50 and not resetRetries then
reset()
warn("[Rush Lure] Too many tries, can't reach position")
return false -- can't stand on tile
end
if resetRetries then
resetRetries = false
end
if distanceFromPlayer(pos) > 30 then
reset()
return false -- not reachable
end
local playerPos = player:getPosition()
local pathWithoutMonsters = findPath(playerPos, pos, 30, { ignoreFields = true, ignoreNonPathable = true, ignoreCreatures = true, precision = 0})
local pathWithMonsters = findPath(playerPos, pos, maxDist, { ignoreFields = true, ignoreNonPathable = true, ignoreCreatures = false, precision = 0 })
if not pathWithoutMonsters then
reset()
warn("[Rush Lure] No possible path to reach position, skipping.")
return false -- spot is unreachable
elseif pathWithoutMonsters and not pathWithMonsters then
local foundMonster = false
for i, dir in ipairs(pathWithoutMonsters) do
local dirs = modPos(dir)
nextPos = nextPos or playerPos
nextPos.x = nextPos.x + dirs[1]
nextPos.y = nextPos.y + dirs[2]
local tile = g_map.getTile(nextPos)
if tile then
if tile:hasCreature() then
local creature = tile:getCreatures()[1]
local hppc = creature:getHealthPercent()
if creature:isMonster() and (hppc and hppc > 0) and (oldTibia or creature:getType() < 3) then
-- real blocking creature can not meet those conditions - ie. it could be player, so just in case check if the next creature is reachable
local path = findPath(playerPos, creature:getPosition(), 7, { ignoreNonPathable = true, precision = 1 })
if path then
creature:setMarked('#00FF00')
if g_game.getAttackingCreature() ~= creature then
attack(creature)
end
g_game.setChaseMode(1)
resetRetries = true -- reset retries, we are trying to unclog the cavebot
delay(100)
return "retry"
end
end
end
end
end
if not g_game.getAttackingCreature() then
reset()
warn("[Rush Lure] No path, no blocking monster, skipping.")
return false -- no other way
end
end
-- reaching position, delay targetbot in process
if not CaveBot.MatchPosition(pos, 0) then
TargetBot.delay(300)
CaveBot.walkTo(pos, 30, { ignoreCreatures = false, ignoreFields = true, ignoreNonPathable = true, precision = 0})
delay(100)
resetRetries = true
return "retry"
end
TargetBot.setOn()
reset(delayTime)
return true
end
)
CaveBot.Editor.registerAction(
"rushlure",
"rush lure",
{
value = function()
return posx() .. "," .. posy() .. "," .. posz() .. ",1000"
end,
title = "Stand Lure",
description = "Run to position(x,y,z), delay(ms), targetbot on/off (yes/no)",
multiline = false,
validation = [[\d{1,5},\d{1,5},\d{1,2},\d{1,5}(?:,(yes|no)$|$)]]
}
)
end
local next = false
schedule(5, function() -- delay because cavebot.lua is loaded after this file
modules.game_bot.connect(CaveBotList(), {
onChildFocusChange = function(widget, newChild, oldChild)
if oldChild and oldChild.action == "rushlure" then
next = true
return
end
if next then
if enable then
TargetBot.setOn()
elseif enable == false then
TargetBot.setOff()
end
enable = nil -- reset
next = false
end
end})
end)

View File

@ -0,0 +1,137 @@
CaveBot.Extensions.SupplyCheck = {}
local supplyRetries = 0
local missedChecks = 0
local rawRound = 0
local time = now
vBot.CaveBotData =
vBot.CaveBotData or
{
refills = 0,
rounds = 0,
time = {},
lastRefill = os.time(),
refillTime = {}
}
local function setCaveBotData(hunting)
if hunting then
supplyRetries = supplyRetries + 1
else
supplyRetries = 0
table.insert(vBot.CaveBotData.refillTime, os.difftime(os.time() - vBot.CaveBotData.lastRefill))
vBot.CaveBotData.lastRefill = os.time()
vBot.CaveBotData.refills = vBot.CaveBotData.refills + 1
end
table.insert(vBot.CaveBotData.time, rawRound)
vBot.CaveBotData.rounds = vBot.CaveBotData.rounds + 1
missedChecks = 0
end
CaveBot.Extensions.SupplyCheck.setup = function()
CaveBot.registerAction(
"supplyCheck",
"#db5a5a",
function(value)
local data = string.split(value, ",")
local round = 0
rawRound = 0
local label = data[1]:trim()
local pos = nil
if #data == 4 then
pos = {x = tonumber(data[2]), y = tonumber(data[3]), z = tonumber(data[4])}
end
if pos then
if missedChecks >= 4 then
missedChecks = 0
supplyRetries = 0
print("CaveBot[SupplyCheck]: Missed 5 supply checks, proceeding with waypoints")
return true
end
if getDistanceBetween(player:getPosition(), pos) > 10 then
missedChecks = missedChecks + 1
print("CaveBot[SupplyCheck]: Missed supply check! " .. 5 - missedChecks .. " tries left before skipping.")
return CaveBot.gotoLabel(label)
end
end
if time then
rawRound = math.ceil((now - time) / 1000)
round = rawRound .. "s"
else
round = ""
end
time = now
local softCount = itemAmount(6529) + itemAmount(3549)
local supplyData = Supplies.hasEnough()
local supplyInfo = Supplies.getAdditionalData()
if storage.caveBot.forceRefill then
print("CaveBot[SupplyCheck]: User forced, going back on refill. Last round took: " .. round)
storage.caveBot.forceRefill = false
supplyRetries = 0
missedChecks = 0
return false
elseif storage.caveBot.backStop then
print("CaveBot[SupplyCheck]: User forced, going back to city and turning off CaveBot. Last round took: " .. round)
supplyRetries = 0
missedChecks = 0
return false
elseif storage.caveBot.backTrainers then
print("CaveBot[SupplyCheck]: User forced, going back to city, then on trainers. Last round took: " .. round)
supplyRetries = 0
missedChecks = 0
return false
elseif storage.caveBot.backOffline then
print("CaveBot[SupplyCheck]: User forced, going back to city, then on offline training. Last round took: " .. round)
supplyRetries = 0
missedChecks = 0
return false
elseif supplyRetries > (storage.extras.huntRoutes or 50) then
print("CaveBot[SupplyCheck]: Round limit reached, going back on refill. Last round took: " .. round)
setCaveBotData()
return false
elseif (supplyInfo.imbues.enabled and player:getSkillLevel(11) == 0) then
print("CaveBot[SupplyCheck]: Imbues ran out. Going on refill. Last round took: " .. round)
setCaveBotData()
return false
elseif (supplyInfo.stamina.enabled and stamina() < tonumber(supplyInfo.stamina.value)) then
print("CaveBot[SupplyCheck]: Stamina ran out. Going on refill. Last round took: " .. round)
setCaveBotData()
return false
elseif (supplyInfo.softBoots.enabled and softCount < 1) then
print("CaveBot[SupplyCheck]: No soft boots left. Going on refill. Last round took: " .. round)
setCaveBotData()
return false
elseif type(supplyData) == "table" then
print("CaveBot[SupplyCheck]: Not enough item: " .. supplyData.id .. "(only " .. supplyData.amount .. " left). Going on refill. Last round took: " .. round)
setCaveBotData()
return false
elseif (supplyInfo.capacity.enabled and freecap() < tonumber(supplyInfo.capacity.value)) then
print("CaveBot[SupplyCheck]: Not enough capacity. Going on refill. Last round took: " .. round)
setCaveBotData()
return false
else
print("CaveBot[SupplyCheck]: Enough supplies. Hunting. Round (" .. supplyRetries .. "/" .. (storage.extras.huntRoutes or 50) .. "). Last round took: " .. round)
setCaveBotData(true)
return CaveBot.gotoLabel(label)
end
end
)
CaveBot.Editor.registerAction(
"supplycheck",
"supply check",
{
value = function()
return "startHunt," .. posx() .. "," .. posy() .. "," .. posz()
end,
title = "Supply check label",
description = "Insert here hunting start label",
validation = [[[^,]+,\d{1,5},\d{1,5},\d{1,2}$]]
}
)
end

View File

@ -0,0 +1,178 @@
CaveBot.Extensions.Tasker = {}
local dataValidationFailed = function()
print("CaveBot[Tasker]: data validation failed! incorrect data, check cavebot/tasker for more info")
return false
end
-- miniconfig
local talkDelay = storage.extras.talkDelay
if not storage.caveBotTasker then
storage.caveBotTasker = {
inProgress = false,
monster = "",
taskName = "",
count = 0,
max = 0
}
end
local resetTaskData = function()
storage.caveBotTasker.inProgress = false
storage.caveBotTasker.monster = ""
storage.caveBotTasker.monster2 = ""
storage.caveBotTasker.taskName = ""
storage.caveBotTasker.count = 0
storage.caveBotTasker.max = 0
end
CaveBot.Extensions.Tasker.setup = function()
CaveBot.registerAction("Tasker", "#FF0090", function(value, retries)
local taskName = ""
local monster = ""
local monster2 = ""
local count = 0
local label1 = ""
local label2 = ""
local task
local data = string.split(value, ",")
if not data or #data < 1 then
dataValidationFailed()
end
local marker = tonumber(data[1])
if not marker then
dataValidationFailed()
resetTaskData()
elseif marker == 1 then
if getNpcs(3) == 0 then
print("CaveBot[Tasker]: no NPC found in range! skipping")
return false
end
if #data ~= 4 and #data ~= 5 then
dataValidationFailed()
resetTaskData()
else
taskName = data[2]:lower():trim()
count = tonumber(data[3]:trim())
monster = data[4]:lower():trim()
if #data == 5 then
monster2 = data[5]:lower():trim()
end
end
elseif marker == 2 then
if #data ~= 3 then
dataValidationFailed()
else
label1 = data[2]:lower():trim()
label2 = data[3]:lower():trim()
end
elseif marker == 3 then
if getNpcs(3) == 0 then
print("CaveBot[Tasker]: no NPC found in range! skipping")
return false
end
if #data ~= 1 then
dataValidationFailed()
end
end
-- let's cover markers now
if marker == 1 then -- starting task
CaveBot.Conversation("hi", "task", taskName, "yes")
delay(talkDelay*4)
storage.caveBotTasker.monster = monster
if monster2 then storage.caveBotTasker.monster2 = monster2 end
storage.caveBotTasker.taskName = taskName
storage.caveBotTasker.inProgress = true
storage.caveBotTasker.max = count
storage.caveBotTasker.count = 0
print("CaveBot[Tasker]: taken task for: " .. monster .. " x" .. count)
return true
elseif marker == 2 then -- only checking
if not storage.caveBotTasker.inProgress then
CaveBot.gotoLabel(label2)
print("CaveBot[Tasker]: there is no task in progress so going to take one.")
return true
end
local max = storage.caveBotTasker.max
local count = storage.caveBotTasker.count
if count >= max then
CaveBot.gotoLabel(label2)
print("CaveBot[Tasker]: task completed: " .. storage.caveBotTasker.taskName)
return true
else
CaveBot.gotoLabel(label1)
print("CaveBot[Tasker]: task in progress, left: " .. max - count .. " " .. storage.caveBotTasker.taskName)
return true
end
elseif marker == 3 then -- reporting task
CaveBot.Conversation("hi", "report", "task")
delay(talkDelay*3)
resetTaskData()
print("CaveBot[Tasker]: task reported, done")
return true
end
end)
CaveBot.Editor.registerAction("tasker", "tasker", {
value=[[ There is 3 scenarios for this extension, as example we will use medusa:
1. start task,
parameters:
- scenario for extension: 1
- task name in gryzzly adams: medusae
- monster count: 500
- monster name to track: medusa
- optional, monster name 2:
2. check status,
to be used on refill to decide whether to go back or spawn or go give task back
parameters:
- scenario for extension: 2
- label if task in progress: skipTask
- label if task done: taskDone
3. report task,
parameters:
- scenario for extension: 3
Strong suggestion, almost mandatory - USE POS CHECK to verify position! this module will only check if there is ANY npc in range!
when begin remove all the text and leave just a single string of parameters
some examples:
2, skipReport, goReport
3
1, drakens, 500, draken warmaster, draken spellweaver
1, medusae, 500, medusa]],
title="Tasker",
multiline = true
})
end
local regex = "Loot of ([a-z])* ([a-z A-Z]*):"
local regex2 = "Loot of ([a-z A-Z]*):"
onTextMessage(function(mode, text)
-- if CaveBot.isOff() then return end
if not text:lower():find("loot of") then return end
if #regexMatch(text, regex) == 1 and #regexMatch(text, regex)[1] == 3 then
monster = regexMatch(text, regex)[1][3]
elseif #regexMatch(text, regex2) == 1 and #regexMatch(text, regex2)[1] == 2 then
monster = regexMatch(text, regex2)[1][2]
end
local m1 = storage.caveBotTasker.monster
local m2 = storage.caveBotTasker.monster2
if monster == m1 or monster == m2 and storage.caveBotTasker.count then
storage.caveBotTasker.count = storage.caveBotTasker.count + 1
end
end)

View File

@ -0,0 +1,40 @@
CaveBot.Extensions.Travel = {}
CaveBot.Extensions.Travel.setup = function()
CaveBot.registerAction("Travel", "#db5a5a", function(value, retries)
local data = string.split(value, ",")
if #data < 2 then
warn("CaveBot[Travel]: incorrect travel value!")
return false
end
local npcName = data[1]:trim()
local dest = data[2]:trim()
if retries > 5 then
print("CaveBot[Travel]: too many tries, can't travel")
return false
end
local npc = getCreatureByName(npcName)
if not npc then
print("CaveBot[Travel]: NPC not found, can't travel")
return false
end
if not CaveBot.ReachNPC(npcName) then
return "retry"
end
CaveBot.Travel(dest)
delay(storage.extras.talkDelay*3)
print("CaveBot[Travel]: travel action finished")
return true
end)
CaveBot.Editor.registerAction("travel", "travel", {
value="NPC name, city",
title="Travel",
description="NPC name, City name, delay in ms(default is 200ms)",
})
end

View File

@ -0,0 +1,93 @@
-- walking
local expectedDirs = {}
local isWalking = {}
local walkPath = {}
local walkPathIter = 0
CaveBot.resetWalking = function()
expectedDirs = {}
walkPath = {}
isWalking = false
end
CaveBot.doWalking = function()
if CaveBot.Config.get("mapClick") then
return false
end
if #expectedDirs == 0 then
return false
end
if #expectedDirs >= 3 then
CaveBot.resetWalking()
end
local dir = walkPath[walkPathIter]
if dir then
g_game.walk(dir, false)
table.insert(expectedDirs, dir)
walkPathIter = walkPathIter + 1
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end
return false
end
-- called when player position has been changed (step has been confirmed by server)
onPlayerPositionChange(function(newPos, oldPos)
if not oldPos or not newPos then return end
local dirs = {{NorthWest, North, NorthEast}, {West, 8, East}, {SouthWest, South, SouthEast}}
local dir = dirs[newPos.y - oldPos.y + 2]
if dir then
dir = dir[newPos.x - oldPos.x + 2]
end
if not dir then
dir = 8 -- 8 is invalid dir, it's fine
end
if not isWalking or not expectedDirs[1] then
-- some other walk action is taking place (for example use on ladder), wait
walkPath = {}
CaveBot.delay(CaveBot.Config.get("ping") + player:getStepDuration(false, dir) + 150)
return
end
if expectedDirs[1] ~= dir then
if CaveBot.Config.get("mapClick") then
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
else
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
return
end
table.remove(expectedDirs, 1)
if CaveBot.Config.get("mapClick") and #expectedDirs > 0 then
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
end)
CaveBot.walkTo = function(dest, maxDist, params)
local path = getPath(player:getPosition(), dest, maxDist, params)
if not path or not path[1] then
return false
end
local dir = path[1]
if CaveBot.Config.get("mapClick") then
local ret = autoWalk(path)
if ret then
isWalking = true
expectedDirs = path
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + math.max(CaveBot.Config.get("ping") + player:getStepDuration(false, dir), player:getStepDuration(false, dir) * 2))
end
return ret
end
g_game.walk(dir, false)
isWalking = true
walkPath = path
walkPathIter = 2
expectedDirs = { dir }
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end

View File

@ -0,0 +1,56 @@
CaveBot.Extensions.Withdraw = {}
CaveBot.Extensions.Withdraw.setup = function()
CaveBot.registerAction("withdraw", "#002FFF", function(value, retries)
-- validation
local data = string.split(value, ",")
if #data ~= 3 then
print("CaveBot[Withdraw]: incorrect data! skipping")
return false
end
-- variables declaration
local source = tonumber(data[1])
local id = tonumber(data[2])
local amount = tonumber(data[3])
-- validation for correct values
if not id or not amount then
print("CaveBot[Withdraw]: incorrect id or amount! skipping")
return false
end
-- check for retries
if retries > 100 then
print("CaveBot[Withdraw]: actions limit reached, proceeding")
for i, container in ipairs(getContainers()) do
if container:getName():lower():find("depot") or container:getName():lower():find("locker") then
g_game.close(container)
end
end
return true
end
-- check for items
if itemAmount(id) >= amount then
print("CaveBot[Withdraw]: enough items, proceeding")
for i, container in ipairs(getContainers()) do
if container:getName():lower():find("depot") or container:getName():lower():find("locker") then
g_game.close(container)
end
end
return true
end
statusMessage("[Withdraw] withdrawing item: " ..id.. " x"..amount)
CaveBot.WithdrawItem(id, amount, source)
CaveBot.PingDelay()
return "retry"
end)
CaveBot.Editor.registerAction("withdraw", "withdraw", {
value="source,id,amount",
title="Withdraw Items",
description="index/inbox, item id and amount",
})
end

View File

@ -0,0 +1,99 @@
TargetBot.Creature = {}
TargetBot.Creature.configsCache = {}
TargetBot.Creature.cached = 0
TargetBot.Creature.resetConfigs = function()
TargetBot.targetList:destroyChildren()
TargetBot.Creature.resetConfigsCache()
end
TargetBot.Creature.resetConfigsCache = function()
TargetBot.Creature.configsCache = {}
TargetBot.Creature.cached = 0
end
TargetBot.Creature.addConfig = function(config, focus)
if type(config) ~= 'table' or type(config.name) ~= 'string' then
return error("Invalid targetbot creature config (missing name)")
end
TargetBot.Creature.resetConfigsCache()
if not config.regex then
config.regex = ""
for part in string.gmatch(config.name, "[^,]+") do
if config.regex:len() > 0 then
config.regex = config.regex .. "|"
end
config.regex = config.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
end
end
local widget = UI.createWidget("TargetBotEntry", TargetBot.targetList)
widget:setText(config.name)
widget.value = config
widget.onDoubleClick = function(entry) -- edit on double click
schedule(20, function() -- schedule to have correct focus
TargetBot.Creature.edit(entry.value, function(newConfig)
entry:setText(newConfig.name)
entry.value = newConfig
TargetBot.Creature.resetConfigsCache()
TargetBot.save()
end)
end)
end
if focus then
widget:focus()
TargetBot.targetList:ensureChildVisible(widget)
end
return widget
end
TargetBot.Creature.getConfigs = function(creature)
if not creature then return {} end
local name = creature:getName():trim():lower()
-- this function may be slow, so it will be using cache
if TargetBot.Creature.configsCache[name] then
return TargetBot.Creature.configsCache[name]
end
local configs = {}
for _, config in ipairs(TargetBot.targetList:getChildren()) do
if regexMatch(name, config.value.regex)[1] then
table.insert(configs, config.value)
end
end
if TargetBot.Creature.cached > 1000 then
TargetBot.Creature.resetConfigsCache() -- too big cache size, reset
end
TargetBot.Creature.configsCache[name] = configs -- add to cache
TargetBot.Creature.cached = TargetBot.Creature.cached + 1
return configs
end
TargetBot.Creature.calculateParams = function(creature, path)
local configs = TargetBot.Creature.getConfigs(creature)
local priority = 0
local danger = 0
local selectedConfig = nil
for _, config in ipairs(configs) do
local config_priority = TargetBot.Creature.calculatePriority(creature, config, path)
if config_priority > priority then
priority = config_priority
danger = TargetBot.Creature.calculateDanger(creature, config, path)
selectedConfig = config
end
end
return {
config = selectedConfig,
creature = creature,
danger = danger,
priority = priority
}
end
TargetBot.Creature.calculateDanger = function(creature, config, path)
-- config is based on creature_editor
return config.danger
end

View File

@ -0,0 +1,245 @@
local targetBotLure = false
local targetCount = 0
local delayValue = 0
local lureMax = 0
local anchorPosition = nil
local lastCall = now
local delayFrom = nil
local dynamicLureDelay = false
function getWalkableTilesCount(position)
local count = 0
for i, tile in pairs(getNearTiles(position)) do
if tile:isWalkable() or tile:hasCreature() then
count = count + 1
end
end
return count
end
function rePosition(minTiles)
minTiles = minTiles or 8
if now - lastCall < 500 then return end
local pPos = player:getPosition()
local tiles = getNearTiles(pPos)
local playerTilesCount = getWalkableTilesCount(pPos)
local tilesTable = {}
if playerTilesCount > minTiles then return end
for i, tile in ipairs(tiles) do
tilesTable[tile] = not tile:hasCreature() and tile:isWalkable() and getWalkableTilesCount(tile:getPosition()) or nil
end
local best = 0
local target = nil
for k,v in pairs(tilesTable) do
if v > best and v > playerTilesCount then
best = v
target = k:getPosition()
end
end
if target then
lastCall = now
return CaveBot.GoTo(target, 0)
end
end
TargetBot.Creature.attack = function(params, targets, isLooting) -- params {config, creature, danger, priority}
if player:isWalking() then
lastWalk = now
end
local config = params.config
local creature = params.creature
if g_game.getAttackingCreature() ~= creature then
g_game.attack(creature)
end
if not isLooting then -- walk only when not looting
TargetBot.Creature.walk(creature, config, targets)
end
-- attacks
local mana = player:getMana()
if config.useGroupAttack and config.groupAttackSpell:len() > 1 and mana > config.minManaGroup then
local creatures = g_map.getSpectatorsInRange(player:getPosition(), false, config.groupAttackRadius, config.groupAttackRadius)
local playersAround = false
local monsters = 0
for _, creature in ipairs(creatures) do
if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then
playersAround = true
elseif creature:isMonster() then
monsters = monsters + 1
end
end
if monsters >= config.groupAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then
if TargetBot.sayAttackSpell(config.groupAttackSpell, config.groupAttackDelay) then
return
end
end
end
if config.useGroupAttackRune and config.groupAttackRune > 100 then
local creatures = g_map.getSpectatorsInRange(creature:getPosition(), false, config.groupRuneAttackRadius, config.groupRuneAttackRadius)
local playersAround = false
local monsters = 0
for _, creature in ipairs(creatures) do
if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then
playersAround = true
elseif creature:isMonster() then
monsters = monsters + 1
end
end
if monsters >= config.groupRuneAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then
if TargetBot.useAttackItem(config.groupAttackRune, 0, creature, config.groupRuneAttackDelay) then
return
end
end
end
if config.useSpellAttack and config.attackSpell:len() > 1 and mana > config.minMana then
if TargetBot.sayAttackSpell(config.attackSpell, config.attackSpellDelay) then
return
end
end
if config.useRuneAttack and config.attackRune > 100 then
if TargetBot.useAttackItem(config.attackRune, 0, creature, config.attackRuneDelay) then
return
end
end
end
TargetBot.Creature.walk = function(creature, config, targets)
local cpos = creature:getPosition()
local pos = player:getPosition()
local isTrapped = true
local pos = player:getPosition()
local dirs = {{-1,1}, {0,1}, {1,1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}
for i=1,#dirs do
local tile = g_map.getTile({x=pos.x-dirs[i][1],y=pos.y-dirs[i][2],z=pos.z})
if tile and tile:isWalkable(false) then
isTrapped = false
end
end
-- data for external dynamic lure
if config.lureMin and config.lureMax and config.dynamicLure then
if config.lureMin >= targets then
targetBotLure = true
elseif targets >= config.lureMax then
targetBotLure = false
end
end
targetCount = targets
delayValue = config.lureDelay
if config.lureMax then
lureMax = config.lureMax
end
dynamicLureDelay = config.dynamicLureDelay
delayFrom = config.delayFrom
-- luring
if config.closeLure and config.closeLureAmount <= getMonsters(1) then
return TargetBot.allowCaveBot(150)
end
if TargetBot.canLure() and (config.lure or config.lureCavebot or config.dynamicLure) and not (creature:getHealthPercent() < (storage.extras.killUnder or 30)) and not isTrapped then
if targetBotLure then
anchorPosition = nil
return TargetBot.allowCaveBot(150)
else
if targets < config.lureCount then
if config.lureCavebot then
anchorPosition = nil
return TargetBot.allowCaveBot(150)
else
local path = findPath(pos, cpos, 5, {ignoreNonPathable=true, precision=2})
if path then
return TargetBot.walkTo(cpos, 10, {marginMin=5, marginMax=6, ignoreNonPathable=true})
end
end
end
end
end
local currentDistance = findPath(pos, cpos, 10, {ignoreCreatures=true, ignoreNonPathable=true, ignoreCost=true})
if (not config.chase or #currentDistance == 1) and not config.avoidAttacks and not config.keepDistance and config.rePosition and (creature:getHealthPercent() >= storage.extras.killUnder) then
return rePosition(config.rePositionAmount or 6)
end
if ((storage.extras.killUnder > 1 and (creature:getHealthPercent() < storage.extras.killUnder)) or config.chase) and not config.keepDistance then
if #currentDistance > 1 then
return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, precision=1})
end
elseif config.keepDistance then
if not anchorPosition or distanceFromPlayer(anchorPosition) > config.anchorRange then
anchorPosition = pos
end
if #currentDistance ~= config.keepDistanceRange and #currentDistance ~= config.keepDistanceRange + 1 then
if config.anchor and anchorPosition and getDistanceBetween(pos, anchorPosition) <= config.anchorRange*2 then
return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1, maxDistanceFrom={anchorPosition, config.anchorRange}})
else
return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1})
end
end
end
--target only movement
if config.avoidAttacks then
local diffx = cpos.x - pos.x
local diffy = cpos.y - pos.y
local candidates = {}
if math.abs(diffx) == 1 and diffy == 0 then
candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x, y=pos.y+1, z=pos.z}}
elseif diffx == 0 and math.abs(diffy) == 1 then
candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}}
end
for _, candidate in ipairs(candidates) do
local tile = g_map.getTile(candidate)
if tile and tile:isWalkable() then
return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true})
end
end
elseif config.faceMonster then
local diffx = cpos.x - pos.x
local diffy = cpos.y - pos.y
local candidates = {}
if diffx == 1 and diffy == 1 then
candidates = {{x=pos.x+1, y=pos.y, z=pos.z}, {x=pos.x, y=pos.y-1, z=pos.z}}
elseif diffx == -1 and diffy == 1 then
candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x, y=pos.y-1, z=pos.z}}
elseif diffx == -1 and diffy == -1 then
candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x-1, y=pos.y, z=pos.z}}
elseif diffx == 1 and diffy == -1 then
candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}}
else
local dir = player:getDirection()
if diffx == 1 and dir ~= 1 then turn(1)
elseif diffx == -1 and dir ~= 3 then turn(3)
elseif diffy == 1 and dir ~= 2 then turn(2)
elseif diffy == -1 and dir ~= 0 then turn(0)
end
end
for _, candidate in ipairs(candidates) do
local tile = g_map.getTile(candidate)
if tile and tile:isWalkable() then
return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true})
end
end
end
end
onPlayerPositionChange(function(newPos, oldPos)
if CaveBot.isOff() then return end
if TargetBot.isOff() then return end
if not lureMax then return end
if storage.TargetBotDelayWhenPlayer then return end
if not dynamicLureDelay then return end
if targetCount < (delayFrom or lureMax/2) or not target() then return end
CaveBot.delay(delayValue or 0)
end)

View File

@ -0,0 +1,106 @@
TargetBot.Creature.edit = function(config, callback) -- callback = function(newConfig)
config = config or {}
local editor = UI.createWindow('TargetBotCreatureEditorWindow')
local values = {} -- (key, function returning value of key)
editor.name:setText(config.name or "")
table.insert(values, {"name", function() return editor.name:getText() end})
local addScrollBar = function(id, title, min, max, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorScrollBar', editor.content.left)
widget.scroll.onValueChange = function(scroll, value)
widget.text:setText(title .. ": " .. value)
end
widget.scroll:setRange(min, max)
if max-min > 1000 then
widget.scroll:setStep(100)
elseif max-min > 100 then
widget.scroll:setStep(10)
end
widget.scroll:setValue(config[id] or defaultValue)
widget.scroll.onValueChange(widget.scroll, widget.scroll:getValue())
table.insert(values, {id, function() return widget.scroll:getValue() end})
end
local addTextEdit = function(id, title, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorTextEdit', editor.content.right)
widget.text:setText(title)
widget.textEdit:setText(config[id] or defaultValue or "")
table.insert(values, {id, function() return widget.textEdit:getText() end})
end
local addCheckBox = function(id, title, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorCheckBox', editor.content.right)
widget.onClick = function()
widget:setOn(not widget:isOn())
end
widget:setText(title)
if config[id] == nil then
widget:setOn(defaultValue)
else
widget:setOn(config[id])
end
table.insert(values, {id, function() return widget:isOn() end})
end
local addItem = function(id, title, defaultItem)
local widget = UI.createWidget('TargetBotCreatureEditorItem', editor.content.right)
widget.text:setText(title)
widget.item:setItemId(config[id] or defaultItem)
table.insert(values, {id, function() return widget.item:getItemId() end})
end
editor.cancel.onClick = function()
editor:destroy()
end
editor.onEscape = editor.cancel.onClick
editor.ok.onClick = function()
local newConfig = {}
for _, value in ipairs(values) do
newConfig[value[1]] = value[2]()
end
if newConfig.name:len() < 1 then return end
newConfig.regex = ""
for part in string.gmatch(newConfig.name, "[^,]+") do
if newConfig.regex:len() > 0 then
newConfig.regex = newConfig.regex .. "|"
end
newConfig.regex = newConfig.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
end
editor:destroy()
callback(newConfig)
end
-- values
addScrollBar("priority", "Priority", 0, 10, 1)
addScrollBar("danger", "Danger", 0, 10, 1)
addScrollBar("maxDistance", "Max distance", 1, 10, 10)
addScrollBar("keepDistanceRange", "Keep distance", 1, 5, 1)
addScrollBar("anchorRange", "Anchoring Range", 1, 10, 3)
addScrollBar("lureCount", "Classic Lure", 0, 5, 1)
addScrollBar("lureMin", "Dynamic lure min", 0, 29, 1)
addScrollBar("lureMax", "Dynamic lure max", 1, 30, 3)
addScrollBar("lureDelay", "Dynamic lure delay", 100, 1000, 250)
addScrollBar("delayFrom", "Start delay when monsters", 1, 29, 2)
addScrollBar("rePositionAmount", "Min tiles to rePosition", 0, 7, 5)
addScrollBar("closeLureAmount", "Close Pull Until", 0, 8, 3)
addCheckBox("chase", "Chase", true)
addCheckBox("keepDistance", "Keep Distance", false)
addCheckBox("anchor", "Anchoring", false)
addCheckBox("dontLoot", "Don't loot", false)
addCheckBox("lure", "Lure", false)
addCheckBox("lureCavebot", "Lure using cavebot", false)
addCheckBox("faceMonster", "Face monsters", false)
addCheckBox("avoidAttacks", "Avoid wave attacks", false)
addCheckBox("dynamicLure", "Dynamic lure", false)
addCheckBox("dynamicLureDelay", "Dynamic lure delay", false)
addCheckBox("diamondArrows", "D-Arrows priority", false)
addCheckBox("rePosition", "rePosition to better tile", false)
addCheckBox("closeLure", "Close Pulling Monsters", false)
addCheckBox("rpSafe", "RP PVP SAFE - (DA)", false)
end

View File

@ -0,0 +1,164 @@
TargetBotCreatureEditorScrollBar < Panel
height: 28
margin-top: 3
Label
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
HorizontalScrollBar
id: scroll
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 10
step: 1
TargetBotCreatureEditorTextEdit < Panel
height: 40
margin-top: 7
Label
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
TextEdit
id: textEdit
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 10
step: 1
TargetBotCreatureEditorItem < Panel
height: 34
margin-top: 7
margin-left: 25
margin-right: 25
Label
id: text
anchors.left: parent.left
anchors.verticalCenter: next.verticalCenter
BotItem
id: item
anchors.top: parent.top
anchors.right: parent.right
TargetBotCreatureEditorCheckBox < BotSwitch
height: 20
margin-top: 7
TargetBotCreatureEditorWindow < MainWindow
text: TargetBot creature editor
width: 500
height: 425
$mobile:
height: 300
Label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
!text: tr('You can use * (any characters) and ? (any character) in target name')
Label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
text-align: center
!text: tr('You can also enter multiple targets, separate them by ,')
TextEdit
id: name
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-left: 90
margin-top: 5
Label
anchors.verticalCenter: prev.verticalCenter
anchors.left: parent.left
text: Target name:
VerticalScrollBar
id: contentScroll
anchors.top: name.bottom
anchors.right: parent.right
anchors.bottom: help.top
step: 28
pixels-scroll: true
margin-right: -10
margin-top: 5
margin-bottom: 5
ScrollablePanel
id: content
anchors.top: name.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: help.top
vertical-scrollbar: contentScroll
margin-bottom: 10
Panel
id: left
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Panel
id: right
anchors.top: parent.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Button
id: help
!text: tr('Help & Tutorials')
anchors.bottom: parent.bottom
anchors.left: parent.left
width: 150
@onClick: g_platform.openUrl("http://bot.otclient.ovh/")
Button
id: ok
!text: tr('Ok')
anchors.bottom: parent.bottom
anchors.right: next.left
margin-right: 10
width: 60
Button
id: cancel
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 60

View File

@ -0,0 +1,61 @@
TargetBot.Creature.calculatePriority = function(creature, config, path)
-- config is based on creature_editor
local priority = 0
local currentTarget = g_game.getAttackingCreature()
-- extra priority if it's current target
if currentTarget == creature then
priority = priority + 1
end
-- check if distance is ok
if #path > config.maxDistance then
if config.rpSafe then
if currentTarget == creature then
g_game.cancelAttackAndFollow() -- if not, stop attack (pvp safe)
end
end
return priority
end
-- add config priority
priority = priority + config.priority
-- extra priority for close distance
local path_length = #path
if path_length == 1 then
priority = priority + 10
elseif path_length <= 3 then
priority = priority + 5
end
-- extra priority for paladin diamond arrows
if config.diamondArrows then
local mobCount = getCreaturesInArea(creature:getPosition(), diamondArrowArea, 2)
priority = priority + (mobCount * 4)
if config.rpSafe then
if getCreaturesInArea(creature:getPosition(), largeRuneArea, 3) > 0 then
if currentTarget == creature then
g_game.cancelAttackAndFollow()
end
return 0 -- pvp safe
end
end
end
-- extra priority for low health
if config.chase and creature:getHealthPercent() < 30 then
priority = priority + 5
elseif creature:getHealthPercent() < 20 then
priority = priority + 2.5
elseif creature:getHealthPercent() < 40 then
priority = priority + 1.5
elseif creature:getHealthPercent() < 60 then
priority = priority + 0.5
elseif creature:getHealthPercent() < 80 then
priority = priority + 0.2
end
return priority
end

View File

@ -0,0 +1,324 @@
TargetBot.Looting = {}
TargetBot.Looting.list = {} -- list of containers to loot
local ui
local items = {}
local containers = {}
local itemsById = {}
local containersById = {}
local dontSave = false
TargetBot.Looting.setup = function()
ui = UI.createWidget("TargetBotLootingPanel")
UI.Container(TargetBot.Looting.onItemsUpdate, true, nil, ui.items)
UI.Container(TargetBot.Looting.onContainersUpdate, true, nil, ui.containers)
ui.everyItem.onClick = function()
ui.everyItem:setOn(not ui.everyItem:isOn())
TargetBot.save()
end
ui.maxDangerPanel.value.onTextChange = function()
local value = tonumber(ui.maxDangerPanel.value:getText())
if not value then
ui.maxDangerPanel.value:setText(0)
end
if dontSave then return end
TargetBot.save()
end
ui.minCapacityPanel.value.onTextChange = function()
local value = tonumber(ui.minCapacityPanel.value:getText())
if not value then
ui.minCapacityPanel.value:setText(0)
end
if dontSave then return end
TargetBot.save()
end
end
TargetBot.Looting.onItemsUpdate = function()
if dontSave then return end
TargetBot.save()
TargetBot.Looting.updateItemsAndContainers()
end
TargetBot.Looting.onContainersUpdate = function()
if dontSave then return end
TargetBot.save()
TargetBot.Looting.updateItemsAndContainers()
end
TargetBot.Looting.update = function(data)
dontSave = true
TargetBot.Looting.list = {}
ui.items:setItems(data['items'] or {})
ui.containers:setItems(data['containers'] or {})
ui.everyItem:setOn(data['everyItem'])
ui.maxDangerPanel.value:setText(data['maxDanger'] or 10)
ui.minCapacityPanel.value:setText(data['minCapacity'] or 100)
TargetBot.Looting.updateItemsAndContainers()
dontSave = false
--vBot
vBot.lootConainers = {}
vBot.lootItems = {}
for i, item in ipairs(ui.containers:getItems()) do
table.insert(vBot.lootConainers, item['id'])
end
for i, item in ipairs(ui.items:getItems()) do
table.insert(vBot.lootItems, item['id'])
end
end
TargetBot.Looting.save = function(data)
data['items'] = ui.items:getItems()
data['containers'] = ui.containers:getItems()
data['maxDanger'] = tonumber(ui.maxDangerPanel.value:getText())
data['minCapacity'] = tonumber(ui.minCapacityPanel.value:getText())
data['everyItem'] = ui.everyItem:isOn()
end
TargetBot.Looting.updateItemsAndContainers = function()
items = ui.items:getItems()
containers = ui.containers:getItems()
itemsById = {}
containersById = {}
for i, item in ipairs(items) do
itemsById[item.id] = 1
end
for i, container in ipairs(containers) do
containersById[container.id] = 1
end
end
local waitTill = 0
local waitingForContainer = nil
local status = ""
local lastFoodConsumption = 0
TargetBot.Looting.getStatus = function()
return status
end
TargetBot.Looting.process = function(targets, dangerLevel)
if (not items[1] and not ui.everyItem:isOn()) or not containers[1] then
status = ""
return false
end
if dangerLevel > tonumber(ui.maxDangerPanel.value:getText()) then
status = "High danger"
return false
end
if player:getFreeCapacity() < tonumber(ui.minCapacityPanel.value:getText()) then
status = "No cap"
TargetBot.Looting.list = {}
return false
end
local loot = storage.extras.lootLast and TargetBot.Looting.list[#TargetBot.Looting.list] or TargetBot.Looting.list[1]
if loot == nil then
status = ""
return false
end
if waitTill > now then
return true
end
local containers = g_game.getContainers()
local lootContainers = TargetBot.Looting.getLootContainers(containers)
-- check if there's container for loot and has empty space for it
if not lootContainers[1] then
-- there's no space, don't loot
status = "No space"
return false
end
status = "Looting"
for index, container in pairs(containers) do
if container.lootContainer then
TargetBot.Looting.lootContainer(lootContainers, container)
return true
end
end
local pos = player:getPosition()
local dist = math.max(math.abs(pos.x-loot.pos.x), math.abs(pos.y-loot.pos.y))
local maxRange = storage.extras.looting or 40
if loot.tries > 30 or loot.pos.z ~= pos.z or dist > maxRange then
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
return true
end
local tile = g_map.getTile(loot.pos)
if dist >= 3 or not tile then
loot.tries = loot.tries + 1
TargetBot.walkTo(loot.pos, 20, { ignoreNonPathable = true, precision = 2 })
return true
end
local container = tile:getTopUseThing()
if not container or not container:isContainer() then
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
return true
end
g_game.open(container)
waitTill = now + (storage.extras.lootDelay or 200)
waitingForContainer = container:getId()
return true
end
TargetBot.Looting.getLootContainers = function(containers)
local lootContainers = {}
local openedContainersById = {}
local toOpen = nil
for index, container in pairs(containers) do
openedContainersById[container:getContainerItem():getId()] = 1
if containersById[container:getContainerItem():getId()] and not container.lootContainer then
if container:getItemsCount() < container:getCapacity() or container:hasPages() then
table.insert(lootContainers, container)
else -- it's full, open next container if possible
for slot, item in ipairs(container:getItems()) do
if item:isContainer() and containersById[item:getId()] then
toOpen = {item, container}
break
end
end
end
end
end
if not lootContainers[1] then
if toOpen then
g_game.open(toOpen[1], toOpen[2])
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
-- check containers one more time, maybe there's any loot container
for index, container in pairs(containers) do
if not containersById[container:getContainerItem():getId()] and not container.lootContainer then
for slot, item in ipairs(container:getItems()) do
if item:isContainer() and containersById[item:getId()] then
g_game.open(item)
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
end
end
end
-- can't find any lootContainer, let's check slots, maybe there's one
for slot = InventorySlotFirst, InventorySlotLast do
local item = getInventoryItem(slot)
if item and item:isContainer() and not openedContainersById[item:getId()] then
-- container which is not opened yet, let's open it
g_game.open(item)
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
end
end
return lootContainers
end
TargetBot.Looting.lootContainer = function(lootContainers, container)
-- loot items
local nextContainer = nil
for i, item in ipairs(container:getItems()) do
if item:isContainer() and not itemsById[item:getId()] then
nextContainer = item
elseif itemsById[item:getId()] or (ui.everyItem:isOn() and not item:isContainer()) then
item.lootTries = (item.lootTries or 0) + 1
if item.lootTries < 5 then -- if can't be looted within 0.5s then skip it
return TargetBot.Looting.lootItem(lootContainers, item)
end
elseif storage.foodItems and storage.foodItems[1] and lastFoodConsumption + 5000 < now then
for _, food in ipairs(storage.foodItems) do
if item:getId() == food.id then
g_game.use(item)
lastFoodConsumption = now
return
end
end
end
end
-- no more items to loot, open next container
if nextContainer then
nextContainer.lootTries = (nextContainer.lootTries or 0) + 1
if nextContainer.lootTries < 2 then -- max 0.6s to open it
g_game.open(nextContainer, container)
waitTill = now + 300 -- give it 0.3s to open
waitingForContainer = nextContainer:getId()
return
end
end
-- looting finished, remove container from list
container.lootContainer = false
g_game.close(container)
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
end
onTextMessage(function(mode, text)
if TargetBot.isOff() then return end
if #TargetBot.Looting.list == 0 then return end
if string.find(text:lower(), "you are not the owner") then -- if we are not the owners of corpse then its a waste of time to try to loot it
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
end
end)
TargetBot.Looting.lootItem = function(lootContainers, item)
if item:isStackable() then
local count = item:getCount()
for _, container in ipairs(lootContainers) do
for slot, citem in ipairs(container:getItems()) do
if item:getId() == citem:getId() and citem:getCount() < 100 then
g_game.move(item, container:getSlotPosition(slot - 1), count)
waitTill = now + 300 -- give it 0.3s to move item
return
end
end
end
end
local container = lootContainers[1]
g_game.move(item, container:getSlotPosition(container:getItemsCount()), 1)
waitTill = now + 300 -- give it 0.3s to move item
end
onContainerOpen(function(container, previousContainer)
if container:getContainerItem():getId() == waitingForContainer then
container.lootContainer = true
waitingForContainer = nil
end
end)
onCreatureDisappear(function(creature)
if isInPz() then return end
if not TargetBot.isOn() then return end
if not creature:isMonster() then return end
local config = TargetBot.Creature.calculateParams(creature, {}) -- return {craeture, config, danger, priority}
if not config.config or config.config.dontLoot then
return
end
local pos = player:getPosition()
local mpos = creature:getPosition()
local name = creature:getName()
if pos.z ~= mpos.z or math.max(math.abs(pos.x-mpos.x), math.abs(pos.y-mpos.y)) > 6 then return end
schedule(20, function() -- check in 20ms if there's container (dead body) on that tile
if not containers[1] then return end
if TargetBot.Looting.list[20] then return end -- too many items to loot
local tile = g_map.getTile(mpos)
if not tile then return end
local container = tile:getTopUseThing()
if not container or not container:isContainer() then return end
if not findPath(player:getPosition(), mpos, 6, {ignoreNonPathable=true, ignoreCreatures=true, ignoreCost=true}) then return end
table.insert(TargetBot.Looting.list, {pos=mpos, creature=name, container=container:getId(), added=now, tries=0})
table.sort(TargetBot.Looting.list, function(a,b)
a.dist = distanceFromPlayer(a.pos)
b.dist = distanceFromPlayer(b.pos)
return a.dist > b.dist
end)
container:setMarked('#000088')
end)
end)

View File

@ -0,0 +1,69 @@
TargetBotLootingPanel < Panel
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 5
Label
margin-top: 5
text: Items to loot
text-align: center
BotContainer
id: items
margin-top: 3
BotSwitch
id: everyItem
!text: tr("Loot every item")
margin-top: 2
Label
margin-top: 5
text: Containers for loot
text-align: center
BotContainer
id: containers
margin-top: 3
height: 45
Panel
id: maxDangerPanel
height: 20
margin-top: 5
BotTextEdit
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 6
width: 80
Label
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
text: Max. danger:
margin-left: 5
Panel
id: minCapacityPanel
height: 20
margin-top: 3
BotTextEdit
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 6
width: 80
Label
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
text: Min. capacity:
margin-left: 5

View File

@ -0,0 +1,328 @@
local targetbotMacro = nil
local config = nil
local lastAction = 0
local cavebotAllowance = 0
local lureEnabled = true
local dangerValue = 0
local looterStatus = ""
-- ui
local configWidget = UI.Config()
local ui = UI.createWidget("TargetBotPanel")
ui.list = ui.listPanel.list -- shortcut
TargetBot.targetList = ui.list
TargetBot.Looting.setup()
ui.status.left:setText("Status:")
ui.status.right:setText("Off")
ui.target.left:setText("Target:")
ui.target.right:setText("-")
ui.config.left:setText("Config:")
ui.config.right:setText("-")
ui.danger.left:setText("Danger:")
ui.danger.right:setText("0")
ui.editor.debug.onClick = function()
local on = ui.editor.debug:isOn()
ui.editor.debug:setOn(not on)
if on then
for _, spec in ipairs(getSpectators()) do
spec:clearText()
end
end
end
local oldTibia = g_game.getClientVersion() < 960
-- main loop, controlled by config
targetbotMacro = macro(100, function()
local pos = player:getPosition()
local specs = g_map.getSpectatorsInRange(pos, false, 6, 6) -- 12x12 area
local creatures = 0
for i, spec in ipairs(specs) do
if spec:isMonster() then
creatures = creatures + 1
end
end
if creatures > 10 then -- if there are too many monsters around, limit area
creatures = g_map.getSpectatorsInRange(pos, false, 3, 3) -- 6x6 area
else
creatures = specs
end
local highestPriority = 0
local dangerLevel = 0
local targets = 0
local highestPriorityParams = nil
for i, creature in ipairs(creatures) do
local hppc = creature:getHealthPercent()
if hppc and hppc > 0 then
local path = findPath(player:getPosition(), creature:getPosition(), 7, {ignoreLastCreature=true, ignoreNonPathable=true, ignoreCost=true, ignoreCreatures=true})
if creature:isMonster() and (oldTibia or creature:getType() < 3) and path then
local params = TargetBot.Creature.calculateParams(creature, path) -- return {craeture, config, danger, priority}
dangerLevel = dangerLevel + params.danger
if params.priority > 0 then
targets = targets + 1
if params.priority > highestPriority then
highestPriority = params.priority
highestPriorityParams = params
end
if ui.editor.debug:isOn() then
creature:setText(params.config.name .. "\n" .. params.priority)
end
end
end
end
end
-- reset walking
TargetBot.walkTo(nil)
-- looting
local looting = TargetBot.Looting.process(targets, dangerLevel)
local lootingStatus = TargetBot.Looting.getStatus()
looterStatus = TargetBot.Looting.getStatus()
dangerValue = dangerLevel
ui.danger.right:setText(dangerLevel)
if highestPriorityParams and not isInPz() then
ui.target.right:setText(highestPriorityParams.creature:getName())
ui.config.right:setText(highestPriorityParams.config.name)
TargetBot.Creature.attack(highestPriorityParams, targets, looting)
if lootingStatus:len() > 0 then
TargetBot.setStatus("Attack & " .. lootingStatus)
elseif cavebotAllowance > now then
TargetBot.setStatus("Luring using CaveBot")
else
TargetBot.setStatus("Attacking")
if not lureEnabled then
TargetBot.setStatus("Attacking (luring off)")
end
end
TargetBot.walk()
lastAction = now
return
end
ui.target.right:setText("-")
ui.config.right:setText("-")
if looting then
TargetBot.walk()
lastAction = now
end
if lootingStatus:len() > 0 then
TargetBot.setStatus(lootingStatus)
else
TargetBot.setStatus("Waiting")
end
end)
-- config, its callback is called immediately, data can be nil
config = Config.setup("targetbot_configs", configWidget, "json", function(name, enabled, data)
if not data then
ui.status.right:setText("Off")
return targetbotMacro.setOff()
end
TargetBot.Creature.resetConfigs()
for _, value in ipairs(data["targeting"] or {}) do
TargetBot.Creature.addConfig(value)
end
TargetBot.Looting.update(data["looting"] or {})
-- add configs
if enabled then
ui.status.right:setText("On")
else
ui.status.right:setText("Off")
end
targetbotMacro.setOn(enabled)
targetbotMacro.delay = nil
lureEnabled = true
end)
-- setup ui
ui.editor.buttons.add.onClick = function()
TargetBot.Creature.edit(nil, function(newConfig)
TargetBot.Creature.addConfig(newConfig, true)
TargetBot.save()
end)
end
ui.editor.buttons.edit.onClick = function()
local entry = ui.list:getFocusedChild()
if not entry then return end
TargetBot.Creature.edit(entry.value, function(newConfig)
entry:setText(newConfig.name)
entry.value = newConfig
TargetBot.Creature.resetConfigsCache()
TargetBot.save()
end)
end
ui.editor.buttons.remove.onClick = function()
local entry = ui.list:getFocusedChild()
if not entry then return end
entry:destroy()
TargetBot.Creature.resetConfigsCache()
TargetBot.save()
end
-- public function, you can use them in your scripts
TargetBot.isActive = function() -- return true if attacking or looting takes place
return lastAction + 300 > now
end
TargetBot.isCaveBotActionAllowed = function()
return cavebotAllowance > now
end
TargetBot.setStatus = function(text)
return ui.status.right:setText(text)
end
TargetBot.getStatus = function()
return ui.status.right:getText()
end
TargetBot.isOn = function()
return config.isOn()
end
TargetBot.isOff = function()
return config.isOff()
end
TargetBot.setOn = function(val)
if val == false then
return TargetBot.setOff(true)
end
config.setOn()
end
TargetBot.setOff = function(val)
if val == false then
return TargetBot.setOn(true)
end
config.setOff()
end
TargetBot.getCurrentProfile = function()
return storage._configs.targetbot_configs.selected
end
local botConfigName = modules.game_bot.contentsPanel.config:getCurrentOption().text
TargetBot.setCurrentProfile = function(name)
if not g_resources.fileExists("/bot/"..botConfigName.."/targetbot_configs/"..name..".json") then
return warn("there is no targetbot profile with that name!")
end
TargetBot.setOff()
storage._configs.targetbot_configs.selected = name
TargetBot.setOn()
end
TargetBot.delay = function(value)
targetbotMacro.delay = now + value
end
TargetBot.save = function()
local data = {targeting={}, looting={}}
for _, entry in ipairs(ui.list:getChildren()) do
table.insert(data.targeting, entry.value)
end
TargetBot.Looting.save(data.looting)
config.save(data)
end
TargetBot.allowCaveBot = function(time)
cavebotAllowance = now + time
end
TargetBot.disableLuring = function()
lureEnabled = false
end
TargetBot.enableLuring = function()
lureEnabled = true
end
TargetBot.Danger = function()
return dangerValue
end
TargetBot.lootStatus = function()
return looterStatus
end
-- attacks
local lastSpell = 0
local lastAttackSpell = 0
TargetBot.saySpell = function(text, delay)
if type(text) ~= 'string' or text:len() < 1 then return end
if not delay then delay = 500 end
if g_game.getProtocolVersion() < 1090 then
lastAttackSpell = now -- pause attack spells, healing spells are more important
end
if lastSpell + delay < now then
say(text)
lastSpell = now
return true
end
return false
end
TargetBot.sayAttackSpell = function(text, delay)
if type(text) ~= 'string' or text:len() < 1 then return end
if not delay then delay = 2000 end
if lastAttackSpell + delay < now then
say(text)
lastAttackSpell = now
return true
end
return false
end
local lastItemUse = 0
local lastRuneAttack = 0
TargetBot.useItem = function(item, subType, target, delay)
if not delay then delay = 200 end
if lastItemUse + delay < now then
local thing = g_things.getThingType(item)
if not thing or not thing:isFluidContainer() then
subType = g_game.getClientVersion() >= 860 and 0 or 1
end
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(item, subType)
if not tmpItem then return end
g_game.useWith(tmpItem, target, subType) -- using item from bp
else
g_game.useInventoryItemWith(item, target, subType) -- hotkey
end
lastItemUse = now
end
end
TargetBot.useAttackItem = function(item, subType, target, delay)
if not delay then delay = 2000 end
if lastRuneAttack + delay < now then
local thing = g_things.getThingType(item)
if not thing or not thing:isFluidContainer() then
subType = g_game.getClientVersion() >= 860 and 0 or 1
end
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(item, subType)
if not tmpItem then return end
g_game.useWith(tmpItem, target, subType) -- using item from bp
else
g_game.useInventoryItemWith(item, target, subType) -- hotkey
end
lastRuneAttack = now
end
end
TargetBot.canLure = function()
return lureEnabled
end

View File

@ -0,0 +1,115 @@
TargetBotEntry < Label
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #00000055
TargetBotDualLabel < Panel
height: 18
margin-left: 3
margin-right: 4
Label
id: left
anchors.top: parent.top
anchors.left: parent.left
text-auto-resize: true
Label
id: right
anchors.top: parent.top
anchors.right: parent.right
text-auto-resize: true
TargetBotPanel < Panel
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 2
margin-bottom: 5
TargetBotDualLabel
id: status
TargetBotDualLabel
id: target
TargetBotDualLabel
id: config
TargetBotDualLabel
id: danger
Panel
id: listPanel
height: 40
TextList
id: list
anchors.fill: parent
vertical-scrollbar: listScrollbar
margin-right: 15
focusable: false
auto-focus: first
VerticalScrollBar
id: listScrollbar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
pixels-scroll: true
step: 10
BotSwitch
id: configButton
@onClick: |
self:setOn(not self:isOn())
self:getParent().listPanel:setHeight(self:isOn() and 100 or 40)
self:getParent().editor:setVisible(self:isOn())
$on:
text: Hide target editor
$!on:
text: Show target editor
Panel
id: editor
visible: false
layout:
type: verticalBox
fit-children: true
Panel
id: buttons
height: 20
margin-top: 2
Button
id: add
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
text: Add
width: 56
Button
id: edit
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 56
Button
id: remove
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
text: Remove
width: 56
BotSwitch
id: debug
text: Show target priority

View File

@ -0,0 +1,28 @@
local dest
local maxDist
local params
TargetBot.walkTo = function(_dest, _maxDist, _params)
dest = _dest
maxDist = _maxDist
params = _params
end
-- called every 100ms if targeting or looting is active
TargetBot.walk = function()
if not dest then return end
if player:isWalking() then return end
local pos = player:getPosition()
if pos.z ~= dest.z then return end
local dist = math.max(math.abs(pos.x-dest.x), math.abs(pos.y-dest.y))
if params.precision and params.precision >= dist then return end
if params.marginMin and params.marginMax then
if dist >= params.marginMin and dist <= params.marginMax then
return
end
end
local path = getPath(pos, dest, maxDist, params)
if path then
walk(path[1])
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,624 @@
AttackEntry < UIWidget
background-color: alpha
text-offset: 35 1
focusable: true
height: 16
font: verdana-11px-rounded
text-align: left
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
UIItem
id: id
anchors.left: prev.right
anchors.verticalCenter: parent.verticalCenter
size: 16 16
focusable: false
visible: false
UIWidget
id: spell
anchors.left: enabled.right
anchors.verticalCenter: parent.verticalCenter
size: 12 12
margin-left: 1
image-source: /images/game/dangerous
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
width: 15
height: 15
AttackBotBotPanel < Panel
height: 38
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('AttackBot')
Button
id: settings
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
Button
id: 1
anchors.top: prev.bottom
anchors.left: parent.left
text: 1
margin-right: 2
margin-top: 4
size: 17 17
Button
id: 2
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 2
margin-left: 4
size: 17 17
Button
id: 3
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 3
margin-left: 4
size: 17 17
Button
id: 4
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 4
margin-left: 4
size: 17 17
Button
id: 5
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 5
margin-left: 4
size: 17 17
Label
id: name
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
anchors.right: parent.right
text-align: center
margin-left: 4
height: 17
text: Profile #1
background: #292A2A
CategoryLabel < Panel
size: 315 15
image-source: /images/ui/panel_flat
image-border: 5
padding: 1
Label
id: description
anchors.fill: parent
text-align: center
text: Area Rune (avalanche, great fireball, etc)
font: verdana-11px-rounded
background: #363636
SourceLabel < Panel
size: 105 15
image-source: /images/ui/panel_flat
image-border: 5
padding: 1
Label
id: description
anchors.fill: parent
text-align: center
text: Monster Name
font: verdana-11px-rounded
background: #363636
RangeLabel < Panel
size: 323 15
image-source: /images/ui/panel_flat
image-border: 5
padding: 1
Label
id: description
anchors.fill: parent
text-align: center
text: 5 Sqm
font: verdana-11px-rounded
background: #363636
PreButton < PreviousButton
background: #363636
height: 15
NexButton < NextButton
background: #363636
height: 15
AttackBotPanel < Panel
size: 500 200
image-source: /images/ui/panel_flat
image-border: 5
padding: 5
TextList
id: entryList
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 3
size: 430 100
vertical-scrollbar: entryListScrollBar
VerticalScrollBar
id: entryListScrollBar
anchors.top: entryList.top
anchors.bottom: entryList.bottom
anchors.right: entryList.right
step: 14
pixels-scroll: true
PreButton
id: previousCategory
anchors.left: entryList.left
anchors.top: entryList.bottom
margin-top: 8
NexButton
id: nextCategory
anchors.left: category.right
anchors.top: entryList.bottom
margin-top: 8
margin-left: 2
CategoryLabel
id: category
anchors.top: entryList.bottom
anchors.left: previousCategory.right
anchors.verticalCenter: previousCategory.verticalCenter
margin-left: 3
PreButton
id: previousSource
anchors.left: entryList.left
anchors.top: category.bottom
margin-top: 8
NexButton
id: nextSource
anchors.left: source.right
anchors.top: category.bottom
margin-top: 8
margin-left: 2
SourceLabel
id: source
anchors.top: category.bottom
anchors.left: previousSource.right
anchors.verticalCenter: previousSource.verticalCenter
margin-left: 3
PreButton
id: previousRange
anchors.left: nextSource.right
anchors.verticalCenter: nextSource.verticalCenter
margin-left: 8
NexButton
id: nextRange
anchors.left: range.right
anchors.verticalCenter: range.verticalCenter
margin-left: 2
RangeLabel
id: range
anchors.left: previousRange.right
anchors.verticalCenter: previousRange.verticalCenter
margin-left: 3
TextEdit
id: monsters
anchors.left: entryList.left
anchors.top: range.bottom
margin-top: 5
size: 405 15
text: monster names
font: cipsoftFont
background: #363636
Label
anchors.left: prev.left
anchors.top: prev.bottom
margin-top: 6
margin-left: 3
text-align: center
text: Mana%:
font: verdana-11px-rounded
SpinBox
id: manaPercent
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 4
size: 30 20
minimum: 0
maximum: 99
step: 1
editable: true
focusable: true
Label
anchors.left: prev.right
margin-left: 7
anchors.verticalCenter: prev.verticalCenter
text: Creatures:
font: verdana-11px-rounded
SpinBox
id: creatures
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 4
size: 30 20
minimum: 1
maximum: 99
step: 1
editable: true
focusable: true
CheckBox
id: orMore
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
tooltip: or more creatures
Label
anchors.left: prev.right
margin-left: 7
anchors.verticalCenter: prev.verticalCenter
text: HP:
font: verdana-11px-rounded
SpinBox
id: minHp
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 4
size: 40 20
minimum: 0
maximum: 99
value: 0
editable: true
focusable: true
Label
anchors.left: prev.right
margin-left: 4
anchors.verticalCenter: prev.verticalCenter
text: -
font: verdana-11px-rounded
SpinBox
id: maxHp
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 4
size: 40 20
minimum: 1
maximum: 100
value: 100
editable: true
focusable: true
Label
anchors.left: prev.right
margin-left: 7
anchors.verticalCenter: prev.verticalCenter
text: CD:
font: verdana-11px-rounded
SpinBox
id: cooldown
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 4
size: 60 20
minimum: 0
maximum: 999999
step: 100
value: 0
editable: true
focusable: true
Button
id: up
anchors.right: parent.right
anchors.top: entryList.bottom
size: 60 17
text: Move Up
text-align: center
font: cipsoftFont
margin-top: 7
margin-right: 8
Button
id: down
anchors.right: prev.left
anchors.verticalCenter: prev.verticalCenter
size: 60 17
margin-right: 5
text: Move Down
text-align: center
font: cipsoftFont
Button
id: addEntry
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 40 19
text-align: center
text: New
font: cipsoftFont
BotItem
id: itemId
anchors.right: addEntry.left
margin-right: 5
anchors.bottom: parent.bottom
margin-bottom: 2
tooltip: drag item here on press to open window
TextEdit
id: spellName
anchors.top: monsters.top
anchors.left: monsters.right
anchors.right: parent.right
margin-left: 5
height: 15
text: spell name
background: #363636
font: cipsoftFont
visible: false
SettingsPanel < Panel
size: 500 200
image-source: /images/ui/panel_flat
image-border: 5
padding: 10
VerticalSeparator
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: Visible.right
margin-left: 10
margin-top: 5
margin-bottom: 5
Label
anchors.top: parent.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 10
text-align: center
font: verdana-11px-rounded
text: Profile:
TextEdit
id: profileName
anchors.top: prev.bottom
margin-top: 3
anchors.left: prev.left
anchors.right: prev.right
margin-left: 20
margin-right: 20
Button
id: resetSettings
anchors.right: parent.right
anchors.bottom: parent.bottom
text-align: center
text: Reset Settings
CheckBox
id: IgnoreMana
anchors.top: parent.top
anchors.left: parent.left
margin-top: 5
width: 200
text: Check RL Tibia conditions
CheckBox
id: Kills
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 200
height: 22
text: Don't use area attacks if less than kills to red skull
text-wrap: true
text-align: left
SpinBox
id: KillsAmount
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.left: prev.right
text-align: left
width: 30
minimum: 1
maximum: 10
focusable: true
margin-left: 5
CheckBox
id: Rotate
anchors.top: Kills.bottom
anchors.left: Kills.left
margin-top: 8
width: 220
text: Turn to side with most monsters
CheckBox
id: Cooldown
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 220
text: Check spell cooldowns
CheckBox
id: Visible
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 245
text: Items must be visible (recommended)
CheckBox
id: PvpMode
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 245
text: PVP mode
CheckBox
id: PvpSafe
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 245
text: PVP safe
CheckBox
id: Training
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 245
text: Stop when attacking trainers
CheckBox
id: BlackListSafe
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 8
width: 200
height: 18
text: Stop if Anti-RS player in range
SpinBox
id: AntiRsRange
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.left: prev.right
text-align: center
width: 50
minimum: 1
maximum: 10
focusable: true
margin-left: 5
AttackBotWindow < MainWindow
size: 535 300
padding: 15
text: AttackBot v2
@onEscape: self:hide()
Label
id: mainLabel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 10
margin-left: 2
!text: tr('More important methods come first (Example: Exori gran above Exori)')
text-align: left
font: verdana-11px-rounded
color: #aeaeae
SettingsPanel
id: settingsPanel
anchors.top: prev.bottom
margin-top: 10
anchors.left: parent.left
margin-left: 2
Label
id: settingsLabel
anchors.verticalCenter: prev.top
anchors.left: prev.left
margin-left: 3
text: Settings
color: #fe4400
font: verdana-11px-rounded
AttackBotPanel
id: mainPanel
anchors.top: mainLabel.bottom
margin-top: 10
anchors.left: parent.left
margin-left: 2
visible: false
Label
id: shooterLabel
anchors.verticalCenter: prev.top
anchors.left: prev.left
margin-left: 3
text: Spell Shooter
color: #fe4400
font: verdana-11px-rounded
visible: false
HorizontalSeparator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: closeButton.top
margin-bottom: 10
Button
id: closeButton
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
text: Close
font: cipsoftFont
Button
id: settings
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
size: 50 21
font: cipsoftFont
text: Settings

View File

@ -0,0 +1,244 @@
setDefaultTab("Main")
local regex = [["(.*?)"]]
local panelName = "BOTserver"
local ui = setupUI([[
Panel
height: 18
Button
id: botServer
anchors.left: parent.left
anchors.right: parent.right
text-align: center
height: 18
!text: tr('BotServer')
]])
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {
manaInfo = true,
mwallInfo = true,
vocation = true,
outfit = false,
broadcasts = true
}
end
local config = storage[panelName]
config.mwalls = {}
if not storage.BotServerChannel then
math.randomseed(os.time())
storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999))
end
local channel = tostring(storage.BotServerChannel)
if config.enabled then
BotServer.init(name(), channel)
end
vBot.BotServerMembers = {}
rootWidget = g_ui.getRootWidget()
if rootWidget then
botServerWindow = UI.createWindow('BotServerWindow')
botServerWindow:hide()
botServerWindow.enabled:setOn(config.enabled)
botServerWindow.enabled.onClick = function()
config.enabled = not config.enabled
botServerWindow.enabled:setOn(config.enabled)
if config.enabled then
channel = tostring(storage.BotServerChannel)
BotServer.init(name(), channel)
botServerWindow.Data.ServerStatus:setText("CONNECTING...")
ui.botServer:setColor('#FFF380')
botServerWindow.Data.ServerStatus:setColor('#FFF380')
else
if BotServer._websocket then
BotServer.terminate()
end
botServerWindow.Data.ServerStatus:setText("DISCONNECTED")
ui.botServer:setColor('#E3242B')
botServerWindow.Data.ServerStatus:setColor('#E3242B')
botServerWindow.Data.Participants:setText("-")
botServerWindow.Data.Members:setTooltip('')
ServerMembers = {}
serverCount = {}
end
initBotServerListenFunctions()
schedule(2000, updateStatusText)
end
botServerWindow.Data.Channel:setText(storage.BotServerChannel)
botServerWindow.Data.Channel.onTextChange = function(widget, text)
storage.BotServerChannel = text
end
botServerWindow.Data.Random.onClick = function(widget)
storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999))
botServerWindow.Data.Channel:setText(storage.BotServerChannel)
end
botServerWindow.Features.Feature1:setOn(config.manaInfo)
botServerWindow.Features.Feature1.onClick = function(widget)
config.manaInfo = not config.manaInfo
widget:setOn(config.manaInfo)
end
botServerWindow.Features.Feature2:setOn(config.mwallInfo)
botServerWindow.Features.Feature2.onClick = function(widget)
config.mwallInfo = not config.mwallInfo
widget:setOn(config.mwallInfo)
end
botServerWindow.Features.Feature3:setOn(config.vocation)
botServerWindow.Features.Feature3.onClick = function(widget)
config.vocation = not config.vocation
if config.vocation then
BotServer.send("voc", player:getVocation())
end
widget:setOn(config.vocation)
end
botServerWindow.Features.Feature4:setOn(config.outfit)
botServerWindow.Features.Feature4.onClick = function(widget)
config.outfit = not config.outfit
widget:setOn(config.outfit)
end
botServerWindow.Features.Feature5:setOn(config.broadcasts)
botServerWindow.Features.Feature5.onClick = function(widget)
config.broadcasts = not config.broadcasts
widget:setOn(config.broadcasts)
end
botServerWindow.Features.Broadcast.onClick = function(widget)
if BotServer._websocket then
BotServer.send("broadcast", botServerWindow.Features.broadcastText:getText())
end
botServerWindow.Features.broadcastText:setText('')
end
end
function initBotServerListenFunctions()
if not BotServer._websocket then return end
if not config.enabled then return end
-- list
BotServer.listen("list", function(name, data)
serverCount = regexMatch(json.encode(data), regex)
ServerMembers = json.encode(data)
end)
-- mwalls
BotServer.listen("mwall", function(name, message)
if config.mwallInfo then
if not config.mwalls[message["pos"]] or config.mwalls[message["pos"]] < now then
config.mwalls[message["pos"]] = now + message["duration"] - 150 -- 150 is latency correction
end
end
end)
-- mana
BotServer.listen("mana", function(name, message)
if config.manaInfo then
local creature = getPlayerByName(name)
if creature then
creature:setManaPercent(message["mana"])
end
end
end)
-- vocation
BotServer.listen("voc", function(name, message)
if message == "yes" and config.vocation then
BotServer.send("voc", player:getVocation())
else
vBot.BotServerMembers[name] = message
end
end)
-- broadcast
BotServer.listen("broadcast", function(name, message)
if config.broadcasts then
broadcastMessage(name..": "..message)
end
end)
end
initBotServerListenFunctions()
function updateStatusText()
if BotServer._websocket then
botServerWindow.Data.ServerStatus:setText("CONNECTED")
botServerWindow.Data.ServerStatus:setColor('#03AC13')
ui.botServer:setColor('#03AC13')
if serverCount then
botServerWindow.Data.Participants:setText(#serverCount)
if ServerMembers then
local text = ""
local regex = [["([a-z 'A-z-]*)"*]]
local re = regexMatch(ServerMembers, regex)
--re[name][2]
for i=1,#re do
if i == 1 then
text = re[i][2]
else
text = text .. "\n" .. re[i][2]
end
end
botServerWindow.Data.Members:setTooltip(text)
end
end
else
botServerWindow.Data.ServerStatus:setText("DISCONNECTED")
ui.botServer:setColor('#E3242B')
botServerWindow.Data.ServerStatus:setColor('#E3242B')
botServerWindow.Data.Participants:setText("-")
end
end
macro(1000, function()
if BotServer._websocket then
BotServer.send("list")
end
updateStatusText()
delay(9000)
end)
ui.botServer.onClick = function(widget)
botServerWindow:show()
botServerWindow:raise()
botServerWindow:focus()
end
botServerWindow.closeButton.onClick = function(widget)
botServerWindow:hide()
end
onAddThing(function(tile, thing)
if config.mwallInfo and BotServer._websocket then
if thing:isItem() and thing:getId() == 2129 then
local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
if not config.mwalls[pos] or config.mwalls[pos] < now then
config.mwalls[pos] = now + 20000
BotServer.send("mwall", {pos=pos, duration=20000})
end
end
end
end)
-- mana
local lastMana = 0
macro(500, function()
if config.manaInfo and BotServer._websocket then
if manapercent() ~= lastMana then
lastMana = manapercent()
BotServer.send("mana", {mana=lastMana})
end
end
end)
-- vocation
if config.vocation and BotServer._websocket then
BotServer.send("voc", player:getVocation())
BotServer.send("voc", "yes")
end
addSeparator()

View File

@ -0,0 +1,202 @@
BotServerData < Panel
size: 340 70
image-source: /images/ui/window
image-border: 6
padding: 3
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text-align: center
!text: tr("BotServer Data")
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
margin-top: 23
text-align: center
text: Channel Name:
margin-left: 6
TextEdit
id: Channel
anchors.top: parent.top
anchors.left: prev.right
margin-top: 20
width: 150
margin-left: 5
text-align: center
Button
id: Random
anchors.left: prev.right
anchors.top: prev.top
anchors.right: parent.right
text-align: center
text: Randomize
margin-left: 6
margin-right: 6
Label
id: label
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 6
margin-bottom: 4
text-align: center
text: Status:
Label
id: ServerStatus
anchors.left: prev.right
anchors.bottom: parent.bottom
margin-left: 10
width: 150
margin-bottom: 4
text-align: left
Label
id: Participants
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 20
margin-right: 8
margin-bottom: 4
text-align: center
UIWidget
id: Members
anchors.right: Participants.left
anchors.bottom: parent.bottom
size: 80 21
text-align: center
text: Members:
FeaturePanel < Panel
size: 340 150
image-source: /images/ui/panel_flat
image-border: 5
padding: 3
Label
id: title
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text-align: center
text: Features
HorizontalSeparator
id: sep
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
BotSwitch
id: Feature1
anchors.top: prev.bottom
anchors.left: parent.left
margin-left: 3
margin-top: 5
text: Mana info
BotSwitch
id: Feature2
anchors.top: sep.bottom
anchors.left: prev.right
margin-top: 5
margin-left: 5
text: MWall info
BotSwitch
id: Feature3
anchors.top: sep.bottom
anchors.left: prev.right
margin-top: 5
margin-left: 5
text: Send Vocation
BotSwitch
id: Feature4
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 3
margin-left: 3
text: Outfit Vocation
BotSwitch
id: Feature5
anchors.bottom: prev.bottom
anchors.left: prev.right
margin-top: 3
margin-left: 5
text: Broadcasts
TextEdit
id: broadcastText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-left: 3
margin-bottom: 3
margin-right: 80
Button
id: Broadcast
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-right: 3
margin-left: 3
height: 22
text: Broadcast
BotServerWindow < MainWindow
!text: tr('BotServer')
size: 370 310
@onEscape: self:hide()
BotServerData
id: Data
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
FeaturePanel
id: Features
anchors.top: prev.bottom
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 10
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5
BotSwitch
id: enabled
anchors.verticalCenter: prev.verticalCenter
anchors.left: parent.left
margin-left: 5
height: 21
$!on:
text: BotServer: OFF
$on:
text: BotServer: ON

View File

@ -0,0 +1,262 @@
setDefaultTab("HP")
local panelName = "ConditionPanel"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Conditions')
Button
id: conditionList
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(panelName)
if not HealBotConfig[panelName] then
HealBotConfig[panelName] = {
enabled = false,
curePosion = false,
poisonCost = 20,
cureCurse = false,
curseCost = 80,
cureBleed = false,
bleedCost = 45,
cureBurn = false,
burnCost = 30,
cureElectrify = false,
electrifyCost = 22,
cureParalyse = false,
paralyseCost = 40,
paralyseSpell = "utani hur",
holdHaste = false,
hasteCost = 40,
hasteSpell = "utani hur",
holdUtamo = false,
utamoCost = 40,
holdUtana = false,
utanaCost = 440,
holdUtura = false,
uturaType = "",
uturaCost = 100,
ignoreInPz = true,
stopHaste = false
}
end
local config = HealBotConfig[panelName]
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
vBotConfigSave("heal")
end
ui.conditionList.onClick = function(widget)
conditionsWindow:show()
conditionsWindow:raise()
conditionsWindow:focus()
end
local rootWidget = g_ui.getRootWidget()
if rootWidget then
conditionsWindow = UI.createWindow('ConditionsWindow', rootWidget)
conditionsWindow:hide()
conditionsWindow.onVisibilityChange = function(widget, visible)
if not visible then
vBotConfigSave("heal")
end
end
-- text edits
conditionsWindow.Cure.PoisonCost:setText(config.poisonCost)
conditionsWindow.Cure.PoisonCost.onTextChange = function(widget, text)
config.poisonCost = tonumber(text)
end
conditionsWindow.Cure.CurseCost:setText(config.curseCost)
conditionsWindow.Cure.CurseCost.onTextChange = function(widget, text)
config.curseCost = tonumber(text)
end
conditionsWindow.Cure.BleedCost:setText(config.bleedCost)
conditionsWindow.Cure.BleedCost.onTextChange = function(widget, text)
config.bleedCost = tonumber(text)
end
conditionsWindow.Cure.BurnCost:setText(config.burnCost)
conditionsWindow.Cure.BurnCost.onTextChange = function(widget, text)
config.burnCost = tonumber(text)
end
conditionsWindow.Cure.ElectrifyCost:setText(config.electrifyCost)
conditionsWindow.Cure.ElectrifyCost.onTextChange = function(widget, text)
config.electrifyCost = tonumber(text)
end
conditionsWindow.Cure.ParalyseCost:setText(config.paralyseCost)
conditionsWindow.Cure.ParalyseCost.onTextChange = function(widget, text)
config.paralyseCost = tonumber(text)
end
conditionsWindow.Cure.ParalyseSpell:setText(config.paralyseSpell)
conditionsWindow.Cure.ParalyseSpell.onTextChange = function(widget, text)
config.paralyseSpell = text
end
conditionsWindow.Hold.HasteSpell:setText(config.hasteSpell)
conditionsWindow.Hold.HasteSpell.onTextChange = function(widget, text)
config.hasteSpell = text
end
conditionsWindow.Hold.HasteCost:setText(config.hasteCost)
conditionsWindow.Hold.HasteCost.onTextChange = function(widget, text)
config.hasteCost = tonumber(text)
end
conditionsWindow.Hold.UtamoCost:setText(config.utamoCost)
conditionsWindow.Hold.UtamoCost.onTextChange = function(widget, text)
config.utamoCost = tonumber(text)
end
conditionsWindow.Hold.UtanaCost:setText(config.utanaCost)
conditionsWindow.Hold.UtanaCost.onTextChange = function(widget, text)
config.utanaCost = tonumber(text)
end
conditionsWindow.Hold.UturaCost:setText(config.uturaCost)
conditionsWindow.Hold.UturaCost.onTextChange = function(widget, text)
config.uturaCost = tonumber(text)
end
-- combo box
conditionsWindow.Hold.UturaType:setOption(config.uturaType)
conditionsWindow.Hold.UturaType.onOptionChange = function(widget)
config.uturaType = widget:getCurrentOption().text
end
-- checkboxes
conditionsWindow.Cure.CurePoison:setChecked(config.curePoison)
conditionsWindow.Cure.CurePoison.onClick = function(widget)
config.curePoison = not config.curePoison
widget:setChecked(config.curePoison)
end
conditionsWindow.Cure.CureCurse:setChecked(config.cureCurse)
conditionsWindow.Cure.CureCurse.onClick = function(widget)
config.cureCurse = not config.cureCurse
widget:setChecked(config.cureCurse)
end
conditionsWindow.Cure.CureBleed:setChecked(config.cureBleed)
conditionsWindow.Cure.CureBleed.onClick = function(widget)
config.cureBleed = not config.cureBleed
widget:setChecked(config.cureBleed)
end
conditionsWindow.Cure.CureBurn:setChecked(config.cureBurn)
conditionsWindow.Cure.CureBurn.onClick = function(widget)
config.cureBurn = not config.cureBurn
widget:setChecked(config.cureBurn)
end
conditionsWindow.Cure.CureElectrify:setChecked(config.cureElectrify)
conditionsWindow.Cure.CureElectrify.onClick = function(widget)
config.cureElectrify = not config.cureElectrify
widget:setChecked(config.cureElectrify)
end
conditionsWindow.Cure.CureParalyse:setChecked(config.cureParalyse)
conditionsWindow.Cure.CureParalyse.onClick = function(widget)
config.cureParalyse = not config.cureParalyse
widget:setChecked(config.cureParalyse)
end
conditionsWindow.Hold.HoldHaste:setChecked(config.holdHaste)
conditionsWindow.Hold.HoldHaste.onClick = function(widget)
config.holdHaste = not config.holdHaste
widget:setChecked(config.holdHaste)
end
conditionsWindow.Hold.HoldUtamo:setChecked(config.holdUtamo)
conditionsWindow.Hold.HoldUtamo.onClick = function(widget)
config.holdUtamo = not config.holdUtamo
widget:setChecked(config.holdUtamo)
end
conditionsWindow.Hold.HoldUtana:setChecked(config.holdUtana)
conditionsWindow.Hold.HoldUtana.onClick = function(widget)
config.holdUtana = not config.holdUtana
widget:setChecked(config.holdUtana)
end
conditionsWindow.Hold.HoldUtura:setChecked(config.holdUtura)
conditionsWindow.Hold.HoldUtura.onClick = function(widget)
config.holdUtura = not config.holdUtura
widget:setChecked(config.holdUtura)
end
conditionsWindow.Hold.IgnoreInPz:setChecked(config.ignoreInPz)
conditionsWindow.Hold.IgnoreInPz.onClick = function(widget)
config.ignoreInPz = not config.ignoreInPz
widget:setChecked(config.ignoreInPz)
end
conditionsWindow.Hold.StopHaste:setChecked(config.stopHaste)
conditionsWindow.Hold.StopHaste.onClick = function(widget)
config.stopHaste = not config.stopHaste
widget:setChecked(config.stopHaste)
end
-- buttons
conditionsWindow.closeButton.onClick = function(widget)
conditionsWindow:hide()
end
Conditions = {}
Conditions.show = function()
conditionsWindow:show()
conditionsWindow:raise()
conditionsWindow:focus()
end
end
local utanaCast = nil
macro(500, function()
if not config.enabled or modules.game_cooldown.isGroupCooldownIconActive(2) then return end
if hppercent() > 95 then
if config.curePoison and mana() >= config.poisonCost and isPoisioned() then say("exana pox")
elseif config.cureCurse and mana() >= config.curseCost and isCursed() then say("exana mort")
elseif config.cureBleed and mana() >= config.bleedCost and isBleeding() then say("exana kor")
elseif config.cureBurn and mana() >= config.burnCost and isBurning() then say("exana flam")
elseif config.cureElectrify and mana() >= config.electrifyCost and isEnergized() then say("exana vis")
end
end
if (not config.ignoreInPz or not isInPz()) and config.holdUtura and mana() >= config.uturaCost and canCast(config.uturaType) and hppercent() < 90 then say(config.uturaType)
elseif (not config.ignoreInPz or not isInPz()) and config.holdUtana and mana() >= config.utanaCost and (not utanaCast or (now - utanaCast > 120000)) then say("utana vid") utanaCast = now
end
end)
macro(50, function()
if not config.enabled then return end
if (not config.ignoreInPz or not isInPz()) and config.holdUtamo and mana() >= config.utamoCost and not hasManaShield() then say("utamo vita")
elseif ((not config.ignoreInPz or not isInPz()) and standTime() < 5000 and config.holdHaste and mana() >= config.hasteCost and not hasHaste() and not getSpellCoolDown(config.hasteSpell) and (not target() or not config.stopHaste or TargetBot.isCaveBotActionAllowed())) and standTime() < 3000 then say(config.hasteSpell)
elseif config.cureParalyse and mana() >= config.paralyseCost and isParalyzed() and not getSpellCoolDown(config.paralyseSpell) then say(config.paralyseSpell)
end
end)

View File

@ -0,0 +1,463 @@
UturaComboBoxPopupMenu < ComboBoxPopupMenu
UturaComboBoxPopupMenuButton < ComboBoxPopupMenuButton
UturaComboBox < ComboBox
@onSetup: |
self:addOption("Utura")
self:addOption("Utura Gran")
CureConditions < Panel
id: Cure
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 200 190
Label
id: label1
anchors.top: parent.top
anchors.left: parent.left
margin-top: 10
margin-left: 5
text: Poison
color: #ffaa00
font: verdana-11px-rounded
Label
id: label11
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 40
text: Mana:
font: verdana-11px-rounded
TextEdit
id: PoisonCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CurePoison
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 10
text: Curse
color: #ffaa00
font: verdana-11px-rounded
Label
id: label22
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 44
text: Mana:
font: verdana-11px-rounded
TextEdit
id: CurseCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CureCurse
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label3
anchors.left: label2.left
anchors.top: label2.bottom
margin-top: 10
text: Bleed
color: #ffaa00
font: verdana-11px-rounded
Label
id: label33
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 46
text: Mana:
font: verdana-11px-rounded
TextEdit
id: BleedCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CureBleed
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 10
text: Burn
color: #ffaa00
font: verdana-11px-rounded
Label
id: label44
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 50
text: Mana:
font: verdana-11px-rounded
TextEdit
id: BurnCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CureBurn
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label5
anchors.left: label4.left
anchors.top: label4.bottom
margin-top: 10
text: Electify
color: #ffaa00
font: verdana-11px-rounded
Label
id: label55
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 33
text: Mana:
font: verdana-11px-rounded
TextEdit
id: ElectrifyCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CureElectrify
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label6
anchors.left: label5.left
anchors.top: label5.bottom
margin-top: 10
text: Paralyse
color: #ffaa00
font: verdana-11px-rounded
Label
id: label66
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 26
text: Mana:
font: verdana-11px-rounded
TextEdit
id: ParalyseCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: CureParalyse
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label7
anchors.left: label6.left
anchors.top: label6.bottom
margin-top: 10
margin-left: 12
text: Spell:
font: verdana-11px-rounded
TextEdit
id: ParalyseSpell
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
font: verdana-11px-rounded
HoldConditions < Panel
id: Hold
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 200 190
Label
id: label1
anchors.top: parent.top
anchors.left: parent.left
margin-top: 10
margin-left: 5
text: Haste
color: #ffaa00
font: verdana-11px-rounded
Label
id: label11
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 44
text: Mana:
font: verdana-11px-rounded
TextEdit
id: HasteCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: HoldHaste
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 10
margin-left: 12
text: Spell:
font: verdana-11px-rounded
TextEdit
id: HasteSpell
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
font: verdana-11px-rounded
Label
id: label3
anchors.left: label1.left
anchors.top: label2.bottom
margin-top: 10
text: Utana Vid
color: #ffaa00
font: verdana-11px-rounded
Label
id: label33
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 21
text: Mana:
font: verdana-11px-rounded
TextEdit
id: UtanaCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: HoldUtana
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 10
text: Utamo Vita
color: #ffaa00
font: verdana-11px-rounded
Label
id: label44
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 12
text: Mana:
font: verdana-11px-rounded
TextEdit
id: UtamoCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: HoldUtamo
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label5
anchors.left: label4.left
anchors.top: label4.bottom
margin-top: 10
text: Recovery
color: #ffaa00
font: verdana-11px-rounded
Label
id: label55
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 20
text: Mana:
font: verdana-11px-rounded
TextEdit
id: UturaCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
font: verdana-11px-rounded
CheckBox
id: HoldUtura
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label6
anchors.left: label5.left
anchors.top: label5.bottom
margin-top: 10
margin-left: 12
text: Spell:
font: verdana-11px-rounded
UturaComboBox
id: UturaType
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
font: verdana-11px-rounded
CheckBox
id: IgnoreInPz
anchors.left: label5.left
anchors.top: label6.bottom
margin-top: 12
Label
anchors.verticalCenter: IgnoreInPz.verticalCenter
anchors.left: prev.right
margin-top: 3
margin-left: 5
text: Don't Cast in Protection Zones
font: cipsoftFont
CheckBox
id: StopHaste
anchors.horizontalCenter: IgnoreInPz.horizontalCenter
anchors.top: IgnoreInPz.bottom
margin-top: 8
Label
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-top: 3
margin-left: 5
text: Stop Haste if TargetBot Is Active
font: cipsoftFont
ConditionsWindow < MainWindow
!text: tr('Condition Manager')
size: 445 280
@onEscape: self:hide()
CureConditions
id: Cure
anchors.top: parent.top
anchors.left: parent.left
margin-top: 7
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
text: Cure Conditions
color: #88e3dd
margin-left: 10
font: verdana-11px-rounded
HoldConditions
id: Hold
anchors.top: parent.top
anchors.right: parent.right
margin-top: 7
Label
id: label
anchors.top: parent.top
anchors.right: parent.right
text: Hold Conditions
color: #88e3dd
margin-right: 100
font: verdana-11px-rounded
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,674 @@
setDefaultTab("Tools")
local panelName = "renameContainers"
if type(storage[panelName]) ~= "table" then
storage[panelName] = {
enabled = false;
height = 170,
purse = true;
list = {
{
value = "Main Backpack",
enabled = true,
item = 9601,
min = false,
items = { 3081, 3048 }
},
{
value = "Runes",
enabled = true,
item = 2866,
min = true,
items = { 3161, 3180 }
},
{
value = "Money",
enabled = true,
item = 2871,
min = true,
items = { 3031, 3035, 3043 }
},
{
value = "Purse",
enabled = true,
item = 23396,
min = true,
items = {}
},
}
}
end
local config = storage[panelName]
UI.Separator()
local renameContui = setupUI([[
Panel
height: 50
Label
text-align: center
text: Container Panel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
font: verdana-11px-rounded
BotSwitch
id: title
anchors.top: prev.bottom
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Open Minimised')
font: verdana-11px-rounded
Button
id: editContList
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
font: verdana-11px-rounded
Button
id: reopenCont
!text: tr('Reopen All')
anchors.left: parent.left
anchors.top: prev.bottom
anchors.right: parent.horizontalCenter
margin-right: 2
height: 17
margin-top: 3
font: verdana-11px-rounded
Button
id: minimiseCont
!text: tr('Minimise All')
anchors.top: prev.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-right: 2
height: 17
font: verdana-11px-rounded
]])
renameContui:setId(panelName)
g_ui.loadUIFromString([[
BackpackName < Label
background-color: alpha
text-offset: 18 2
focusable: true
height: 17
font: verdana-11px-rounded
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 1
margin-left: 3
$focus:
background-color: #00000055
Button
id: state
!text: tr('M')
anchors.right: remove.left
anchors.verticalCenter: parent.verticalCenter
margin-right: 1
width: 15
height: 15
Button
id: remove
!text: tr('X')
!tooltip: tr('Remove')
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
margin-right: 15
width: 15
height: 15
Button
id: openNext
!text: tr('N')
anchors.right: state.left
anchors.verticalCenter: parent.verticalCenter
margin-right: 1
width: 15
height: 15
tooltip: Open container inside with the same ID.
ContListsWindow < MainWindow
!text: tr('Container Names')
size: 465 170
@onEscape: self:hide()
TextList
id: itemList
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: separator.top
width: 200
margin-bottom: 6
margin-top: 3
margin-left: 3
vertical-scrollbar: itemListScrollBar
VerticalScrollBar
id: itemListScrollBar
anchors.top: itemList.top
anchors.bottom: itemList.bottom
anchors.right: itemList.right
step: 14
pixels-scroll: true
VerticalSeparator
id: sep
anchors.top: parent.top
anchors.left: itemList.right
anchors.bottom: separator.top
margin-top: 3
margin-bottom: 6
margin-left: 10
Label
id: lblName
anchors.left: sep.right
anchors.top: sep.top
width: 70
text: Name:
margin-left: 10
margin-top: 3
font: verdana-11px-rounded
TextEdit
id: contName
anchors.left: lblName.right
anchors.top: sep.top
anchors.right: parent.right
font: verdana-11px-rounded
Label
id: lblCont
anchors.left: lblName.left
anchors.verticalCenter: contId.verticalCenter
width: 70
text: Container:
font: verdana-11px-rounded
BotItem
id: contId
anchors.left: contName.left
anchors.top: contName.bottom
margin-top: 3
BotContainer
id: sortList
anchors.left: prev.left
anchors.right: parent.right
anchors.top: prev.bottom
anchors.bottom: separator.top
margin-bottom: 6
margin-top: 3
Label
anchors.left: lblCont.left
anchors.verticalCenter: sortList.verticalCenter
width: 70
text: Items:
font: verdana-11px-rounded
Button
id: addItem
anchors.right: contName.right
anchors.top: contName.bottom
margin-top: 5
text: Add
width: 40
font: cipsoftFont
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
CheckBox
id: purse
anchors.left: parent.left
anchors.bottom: parent.bottom
text: Open Purse
tooltip: Opens Store/Charm Purse
width: 85
height: 15
margin-top: 2
margin-left: 3
font: verdana-11px-rounded
CheckBox
id: sort
anchors.left: prev.right
anchors.bottom: parent.bottom
text: Sort Items
tooltip: Sort items based on items widget
width: 85
height: 15
margin-top: 2
margin-left: 15
font: verdana-11px-rounded
CheckBox
id: forceOpen
anchors.left: prev.right
anchors.bottom: parent.bottom
text: Keep Open
tooltip: Will keep open containers all the time
width: 85
height: 15
margin-top: 2
margin-left: 15
font: verdana-11px-rounded
CheckBox
id: lootBag
anchors.left: prev.right
anchors.bottom: parent.bottom
text: Loot Bag
tooltip: Open Loot Bag (gunzodus franchaise)
width: 85
height: 15
margin-top: 2
margin-left: 15
font: verdana-11px-rounded
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
ResizeBorder
id: bottomResizeBorder
anchors.fill: separator
height: 3
minimum: 170
maximum: 245
margin-left: 3
margin-right: 3
background: #ffffff88
]])
function findItemsInArray(t, tfind)
local tArray = {}
for x,v in pairs(t) do
if type(v) == "table" then
local aItem = t[x].item
local aEnabled = t[x].enabled
if aItem then
if tfind and aItem == tfind then
return x
elseif not tfind then
if aEnabled then
table.insert(tArray, aItem)
end
end
end
end
end
if not tfind then return tArray end
end
local lstBPs
local openContainer = function(id)
local t = {getRight(), getLeft(), getAmmo()} -- if more slots needed then add them here
for i=1,#t do
local slotItem = t[i]
if slotItem and slotItem:getId() == id then
return g_game.open(slotItem, nil)
end
end
for i, container in pairs(g_game.getContainers()) do
for i, item in ipairs(container:getItems()) do
if item:isContainer() and item:getId() == id then
return g_game.open(item, nil)
end
end
end
end
function reopenBackpacks()
lstBPs = findItemsInArray(config.list)
for _, container in pairs(g_game.getContainers()) do g_game.close(container) end
bpItem = getBack()
if bpItem ~= nil then
g_game.open(bpItem)
end
schedule(500, function()
local delay = 200
if config.purse then
local item = getPurse()
if item then
use(item)
end
end
for i=1,#lstBPs do
schedule(delay, function()
openContainer(lstBPs[i])
end)
delay = delay + 250
end
end)
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
contListWindow = UI.createWindow('ContListsWindow', rootWidget)
contListWindow:hide()
contListWindow.onGeometryChange = function(widget, old, new)
if old.height == 0 then return end
config.height = new.height
end
contListWindow:setHeight(config.height or 170)
renameContui.editContList.onClick = function(widget)
contListWindow:show()
contListWindow:raise()
contListWindow:focus()
end
renameContui.reopenCont.onClick = function(widget)
reopenBackpacks()
end
renameContui.minimiseCont.onClick = function(widget)
for i, container in ipairs(getContainers()) do
local containerWindow = container.window
containerWindow:setContentHeight(34)
end
end
renameContui.title:setOn(config.enabled)
renameContui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
contListWindow.closeButton.onClick = function(widget)
contListWindow:hide()
end
contListWindow.purse.onClick = function(widget)
config.purse = not config.purse
contListWindow.purse:setChecked(config.purse)
end
contListWindow.purse:setChecked(config.purse)
contListWindow.sort.onClick = function(widget)
config.sort = not config.sort
contListWindow.sort:setChecked(config.sort)
end
contListWindow.sort:setChecked(config.sort)
contListWindow.forceOpen.onClick = function(widget)
config.forceOpen = not config.forceOpen
contListWindow.forceOpen:setChecked(config.forceOpen)
end
contListWindow.forceOpen:setChecked(config.forceOpen)
contListWindow.lootBag.onClick = function(widget)
config.lootBag = not config.lootBag
contListWindow.lootBag:setChecked(config.lootBag)
end
contListWindow.lootBag:setChecked(config.lootBag)
local function refreshSortList(k, t)
t = t or {}
UI.Container(function()
t = contListWindow.sortList:getItems()
config.list[k].items = t
end, true, nil, contListWindow.sortList)
contListWindow.sortList:setItems(t)
end
refreshSortList(t)
local refreshContNames = function(tFocus)
local storageVal = config.list
if storageVal and #storageVal > 0 then
for i, child in pairs(contListWindow.itemList:getChildren()) do
child:destroy()
end
for k, entry in pairs(storageVal) do
local label = g_ui.createWidget("BackpackName", contListWindow.itemList)
label.onMouseRelease = function()
contListWindow.contId:setItemId(entry.item)
contListWindow.contName:setText(entry.value)
if not entry.items then
entry.items = {}
end
contListWindow.sortList:setItems(entry.items)
refreshSortList(k, entry.items)
end
label.enabled.onClick = function(widget)
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
label.enabled:setTooltip(entry.enabled and 'Disable' or 'Enable')
label.enabled:setImageColor(entry.enabled and '#00FF00' or '#FF0000')
end
label.remove.onClick = function(widget)
table.removevalue(config.list, entry)
label:destroy()
end
label.state:setChecked(entry.min)
label.state.onClick = function(widget)
entry.min = not entry.min
label.state:setChecked(entry.min)
label.state:setColor(entry.min and '#00FF00' or '#FF0000')
label.state:setTooltip(entry.min and 'Open Minimised' or 'Do not minimise')
end
label.openNext.onClick = function(widget)
entry.openNext = not entry.openNext
label.openNext:setChecked(entry.openNext)
label.openNext:setColor(entry.openNext and '#00FF00' or '#FF0000')
end
label:setText(entry.value)
label.enabled:setChecked(entry.enabled)
label.enabled:setTooltip(entry.enabled and 'Disable' or 'Enable')
label.enabled:setImageColor(entry.enabled and '#00FF00' or '#FF0000')
label.state:setColor(entry.min and '#00FF00' or '#FF0000')
label.state:setTooltip(entry.min and 'Open Minimised' or 'Do not minimise')
label.openNext:setColor(entry.openNext and '#00FF00' or '#FF0000')
if tFocus and entry.item == tFocus then
tFocus = label
end
end
if tFocus then contListWindow.itemList:focusChild(tFocus) end
end
end
contListWindow.addItem.onClick = function(widget)
local id = contListWindow.contId:getItemId()
local trigger = contListWindow.contName:getText()
if id > 100 and trigger:len() > 0 then
local ifind = findItemsInArray(config.list, id)
if ifind then
config.list[ifind] = { item = id, value = trigger, enabled = config.list[ifind].enabled, min = config.list[ifind].min, items = config.list[ifind].items}
else
table.insert(config.list, { item = id, value = trigger, enabled = true, min = false, items = {} })
end
contListWindow.contId:setItemId(0)
contListWindow.contName:setText('')
contListWindow.contName:setColor('white')
contListWindow.contName:setImageColor('#ffffff')
contListWindow.contId:setImageColor('#ffffff')
refreshContNames(id)
else
contListWindow.contId:setImageColor('red')
contListWindow.contName:setImageColor('red')
contListWindow.contName:setColor('red')
end
end
refreshContNames()
end
onContainerOpen(function(container, previousContainer)
if not container.window then return end
local containerWindow = container.window
if not previousContainer then
containerWindow:setContentHeight(34)
end
local storageVal = config.list
if storageVal and #storageVal > 0 then
for _, entry in pairs(storageVal) do
if entry.enabled and string.find(container:getContainerItem():getId(), entry.item) then
if entry.min then
containerWindow:minimize()
end
if renameContui.title:isOn() then
containerWindow:setText(entry.value)
end
if entry.openNext then
for i, item in ipairs(container:getItems()) do
if item:getId() == entry.item then
local time = #storageVal * 250
schedule(time, function()
time = time + 250
g_game.open(item)
end)
end
end
end
end
end
end
end)
local function nameContainersOnLogin()
for i, container in ipairs(getContainers()) do
if renameContui.title:isOn() then
if not container.window then return end
local containerWindow = container.window
local storageVal = config.list
if storageVal and #storageVal > 0 then
for _, entry in pairs(storageVal) do
if entry.enabled and string.find(container:getContainerItem():getId(), entry.item) then
containerWindow:setText(entry.value)
end
end
end
end
end
end
nameContainersOnLogin()
local function moveItem(item, destination)
return g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), item:getCount())
end
local function properTable(t)
local r = {}
for _, entry in pairs(t) do
if type(entry) == "number" then
table.insert(r, entry)
else
table.insert(r, entry.id)
end
end
return r
end
local mainLoop = macro(150, function(macro)
if not config.sort and not config.purse then return end
local storageVal = config.list
for _, entry in pairs(storageVal) do
local dId = entry.item
local items = properTable(entry.items)
-- sorting
if config.sort then
for _, container in pairs(getContainers()) do
local cName = container:getName():lower()
if not cName:find("depot") and not cName:find("depot") and not cName:find("quiver") then
local cId = container:getContainerItem():getId()
for __, item in ipairs(container:getItems()) do
local id = item:getId()
if table.find(items, id) and cId ~= dId then
local destination = getContainerByItem(dId, true)
if destination and not containerIsFull(destination) then
return moveItem(item, destination)
end
end
end
end
end
end
-- keep open / purse 23396
if config.forceOpen then
local container = getContainerByItem(dId)
if not container then
local t = {getBack(), getAmmo(), getFinger(), getNeck(), getLeft(), getRight()}
for i=1,#t do
local slot = t[i]
if slot and slot:getId() == dId then
return g_game.open(slot)
end
end
local cItem = findItem(dId)
if cItem then
return g_game.open(cItem)
end
end
end
end
if config.purse and config.forceOpen and not getContainerByItem(23396) then
return use(getPurse())
end
if config.lootBag and config.forceOpen and not getContainerByItem(23721) then
if findItem(23721) then
g_game.open(findItem(23721), getContainerByItem(23396))
else
return use(getPurse())
end
end
macro:setOff()
end)
onContainerOpen(function(container, previousContainer)
mainLoop:setOn()
end)
onAddItem(function(container, slot, item, oldItem)
mainLoop:setOn()
end)
onPlayerInventoryChange(function(slot, item, oldItem)
mainLoop:setOn()
end)
onContainerClose(function(container)
if not container.lootContainer then
mainLoop:setOn()
end
end)

View File

@ -0,0 +1,146 @@
setDefaultTab("Tools")
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Dropper')
Button
id: edit
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Edit
]])
local edit = setupUI([[
Panel
height: 150
Label
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin-top: 5
text-align: center
text: Trash:
BotContainer
id: TrashItems
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 32
Label
anchors.top: prev.bottom
margin-top: 5
anchors.left: parent.left
anchors.right: parent.right
text-align: center
text: Use:
BotContainer
id: UseItems
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 32
Label
anchors.top: prev.bottom
margin-top: 5
anchors.left: parent.left
anchors.right: parent.right
text-align: center
text: Drop if below 150 cap:
BotContainer
id: CapItems
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 32
]])
edit:hide()
if not storage.dropper then
storage.dropper = {
enabled = false,
trashItems = { 283, 284, 285 },
useItems = { 21203, 14758 },
capItems = { 21175 }
}
end
local config = storage.dropper
local showEdit = false
ui.edit.onClick = function(widget)
showEdit = not showEdit
if showEdit then
edit:show()
else
edit:hide()
end
end
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
ui.title:setOn(config.enabled)
end
UI.Container(function()
config.trashItems = edit.TrashItems:getItems()
end, true, nil, edit.TrashItems)
edit.TrashItems:setItems(config.trashItems)
UI.Container(function()
config.useItems = edit.UseItems:getItems()
end, true, nil, edit.UseItems)
edit.UseItems:setItems(config.useItems)
UI.Container(function()
config.capItems = edit.CapItems:getItems()
end, true, nil, edit.CapItems)
edit.CapItems:setItems(config.capItems)
local function properTable(t)
local r = {}
for _, entry in pairs(t) do
table.insert(r, entry.id)
end
return r
end
macro(200, function()
if not config.enabled then return end
local tables = {properTable(config.capItems), properTable(config.useItems), properTable(config.trashItems)}
local containers = getContainers()
for i=1,3 do
for _, container in pairs(containers) do
for __, item in ipairs(container:getItems()) do
for ___, userItem in ipairs(tables[i]) do
if item:getId() == userItem then
return i == 1 and freecap() < 150 and dropItem(item) or
i == 2 and use(item) or
i == 3 and dropItem(item)
end
end
end
end
end
end)

View File

@ -0,0 +1,775 @@
local panelName = "EquipperPanel"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: switch
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('EQ Manager')
Button
id: setup
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(panelName)
if not storage[panelName] or not storage[panelName].bosses then -- no bosses - old ver
storage[panelName] = {
enabled = false,
rules = {},
bosses = {}
}
end
local config = storage[panelName]
ui.switch:setOn(config.enabled)
ui.switch.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
local conditions = { -- always add new conditions at the bottom
"Item is available and not worn.", -- nothing 1
"Monsters around is more than: ", -- spinbox 2
"Monsters around is less than: ", -- spinbox 3
"Health precent is below:", -- spinbox 4
"Health precent is above:", -- spinbox 5
"Mana precent is below:", -- spinbox 6
"Mana precent is above:", -- spinbox 7
"Target name is:", -- BotTextEdit 8
"Hotkey is being pressed:", -- BotTextEdit 9
"Player is paralyzed", -- nothing 10
"Player is in protection zone", -- nothing 11
"Players around is more than:", -- spinbox 12
"Players around is less than:", -- spinbox 13
"TargetBot Danger is Above:", -- spinbox 14
"Blacklist player in range (sqm)", -- spinbox 15
"Target is Boss", -- nothing 16
"Player is NOT in protection zone", -- nothing 17
"CaveBot is ON, TargetBot is OFF" -- nothing 18
}
local conditionNumber = 1
local optionalConditionNumber = 2
local mainWindow = UI.createWindow("EquipWindow")
mainWindow:hide()
ui.setup.onClick = function()
mainWindow:show()
mainWindow:raise()
mainWindow:focus()
end
local inputPanel = mainWindow.inputPanel
local listPanel = mainWindow.listPanel
local namePanel = mainWindow.profileName
local eqPanel = mainWindow.setup
local bossPanel = mainWindow.bossPanel
local slotWidgets = {eqPanel.head, eqPanel.body, eqPanel.legs, eqPanel.feet, eqPanel.neck, eqPanel["left-hand"], eqPanel["right-hand"], eqPanel.finger, eqPanel.ammo} -- back is disabled
local function setCondition(first, n)
local widget
local spinBox
local textEdit
if first then
widget = inputPanel.condition.description.text
spinBox = inputPanel.condition.spinbox
textEdit = inputPanel.condition.text
else
widget = inputPanel.optionalCondition.description.text
spinBox = inputPanel.optionalCondition.spinbox
textEdit = inputPanel.optionalCondition.text
end
-- reset values after change
spinBox:setValue(0)
textEdit:setText('')
if n == 1 or n == 10 or n == 11 or n == 16 or n == 17 or n == 18 then
spinBox:hide()
textEdit:hide()
elseif n == 9 or n == 8 then
spinBox:hide()
textEdit:show()
if n == 9 then
textEdit:setWidth(75)
else
textEdit:setWidth(200)
end
else
spinBox:show()
textEdit:hide()
end
widget:setText(conditions[n])
end
local function resetFields()
conditionNumber = 1
optionalConditionNumber = 2
setCondition(false, optionalConditionNumber)
setCondition(true, conditionNumber)
for i, widget in ipairs(slotWidgets) do
widget:setItemId(0)
widget:setChecked(false)
end
for i, child in ipairs(listPanel.list:getChildren()) do
child.display = false
end
namePanel.profileName:setText("")
inputPanel.condition.text:setText('')
inputPanel.condition.spinbox:setValue(0)
inputPanel.useSecondCondition:setText('-')
inputPanel.optionalCondition.text:setText('')
inputPanel.optionalCondition.spinbox:setValue(0)
inputPanel.optionalCondition:hide()
bossPanel:hide()
listPanel:show()
mainWindow.bossList:setText('Boss List')
bossPanel.name:setText('')
end
resetFields()
mainWindow.closeButton.onClick = function()
resetFields()
mainWindow:hide()
end
inputPanel.optionalCondition:hide()
inputPanel.useSecondCondition.onOptionChange = function(widget, option, data)
if option ~= "-" then
inputPanel.optionalCondition:show()
else
inputPanel.optionalCondition:hide()
end
end
-- add default text & windows
setCondition(true, 1)
setCondition(false, 2)
-- in/de/crementation buttons
inputPanel.condition.nex.onClick = function()
local max = #conditions
if inputPanel.optionalCondition:isVisible() then
if conditionNumber == max then
if optionalConditionNumber == 1 then
conditionNumber = 2
else
conditionNumber = 1
end
else
local futureNumber = conditionNumber + 1
local safeFutureNumber = conditionNumber + 2 > max and 1 or conditionNumber + 2
conditionNumber = futureNumber ~= optionalConditionNumber and futureNumber or safeFutureNumber
end
else
conditionNumber = conditionNumber == max and 1 or conditionNumber + 1
if optionalConditionNumber == conditionNumber then
optionalConditionNumber = optionalConditionNumber == max and 1 or optionalConditionNumber + 1
setCondition(false, optionalConditionNumber)
end
end
setCondition(true, conditionNumber)
end
inputPanel.condition.pre.onClick = function()
local max = #conditions
if inputPanel.optionalCondition:isVisible() then
if conditionNumber == 1 then
if optionalConditionNumber == max then
conditionNumber = max-1
else
conditionNumber = max
end
else
local futureNumber = conditionNumber - 1
local safeFutureNumber = conditionNumber - 2 < 1 and max or conditionNumber - 2
conditionNumber = futureNumber ~= optionalConditionNumber and futureNumber or safeFutureNumber
end
else
conditionNumber = conditionNumber == 1 and max or conditionNumber - 1
if optionalConditionNumber == conditionNumber then
optionalConditionNumber = optionalConditionNumber == 1 and max or optionalConditionNumber - 1
setCondition(false, optionalConditionNumber)
end
end
setCondition(true, conditionNumber)
end
inputPanel.optionalCondition.nex.onClick = function()
local max = #conditions
if optionalConditionNumber == max then
if conditionNumber == 1 then
optionalConditionNumber = 2
else
optionalConditionNumber = 1
end
else
local futureNumber = optionalConditionNumber + 1
local safeFutureNumber = optionalConditionNumber + 2 > max and 1 or optionalConditionNumber + 2
optionalConditionNumber = futureNumber ~= conditionNumber and futureNumber or safeFutureNumber
end
setCondition(false, optionalConditionNumber)
end
inputPanel.optionalCondition.pre.onClick = function()
local max = #conditions
if optionalConditionNumber == 1 then
if conditionNumber == max then
optionalConditionNumber = max-1
else
optionalConditionNumber = max
end
else
local futureNumber = optionalConditionNumber - 1
local safeFutureNumber = optionalConditionNumber - 2 < 1 and max or optionalConditionNumber - 2
optionalConditionNumber = futureNumber ~= conditionNumber and futureNumber or safeFutureNumber
end
setCondition(false, optionalConditionNumber)
end
listPanel.up.onClick = function(widget)
local focused = listPanel.list:getFocusedChild()
local n = listPanel.list:getChildIndex(focused)
local t = config.rules
t[n], t[n-1] = t[n-1], t[n]
if n-1 == 1 then
widget:setEnabled(false)
end
listPanel.down:setEnabled(true)
listPanel.list:moveChildToIndex(focused, n-1)
listPanel.list:ensureChildVisible(focused)
end
listPanel.down.onClick = function(widget)
local focused = listPanel.list:getFocusedChild()
local n = listPanel.list:getChildIndex(focused)
local t = config.rules
t[n], t[n+1] = t[n+1], t[n]
if n + 1 == listPanel.list:getChildCount() then
widget:setEnabled(false)
end
listPanel.up:setEnabled(true)
listPanel.list:moveChildToIndex(focused, n+1)
listPanel.list:ensureChildVisible(focused)
end
eqPanel.cloneEq.onClick = function(widget)
eqPanel.head:setItemId(getHead() and getHead():getId() or 0)
eqPanel.body:setItemId(getBody() and getBody():getId() or 0)
eqPanel.legs:setItemId(getLeg() and getLeg():getId() or 0)
eqPanel.feet:setItemId(getFeet() and getFeet():getId() or 0)
eqPanel.neck:setItemId(getNeck() and getNeck():getId() or 0)
eqPanel["left-hand"]:setItemId(getLeft() and getLeft():getId() or 0)
eqPanel["right-hand"]:setItemId(getRight() and getRight():getId() or 0)
eqPanel.finger:setItemId(getFinger() and getFinger():getId() or 0)
eqPanel.ammo:setItemId(getAmmo() and getAmmo():getId() or 0)
end
eqPanel.default.onClick = resetFields
-- buttons disabled by default
listPanel.up:setEnabled(false)
listPanel.down:setEnabled(false)
-- correct background image
for i, widget in ipairs(slotWidgets) do
widget:setTooltip("Right click to set as slot to unequip")
widget.onItemChange = function(widget)
local selfId = widget:getItemId()
widget:setOn(selfId > 100)
if widget:isChecked() then
widget:setChecked(selfId < 100)
end
end
widget.onMouseRelease = function(widget, mousePos, mouseButton)
if mouseButton == 2 then
local clearItem = widget:isChecked() == false
widget:setChecked(not widget:isChecked())
if clearItem then
widget:setItemId(0)
end
end
end
end
inputPanel.condition.description.onMouseWheel = function(widget, mousePos, scroll)
if scroll == 1 then
inputPanel.condition.nex.onClick()
else
inputPanel.condition.pre.onClick()
end
end
inputPanel.optionalCondition.description.onMouseWheel = function(widget, mousePos, scroll)
if scroll == 1 then
inputPanel.optionalCondition.nex.onClick()
else
inputPanel.optionalCondition.pre.onClick()
end
end
namePanel.profileName.onTextChange = function(widget, text)
local button = inputPanel.add
text = text:lower()
for i, child in ipairs(listPanel.list:getChildren()) do
local name = child:getText():lower()
button:setText(name == text and "Overwrite" or "Add Rule")
button:setTooltip(name == text and "Overwrite existing rule named: "..name, "Add new rule to the list: "..name)
end
end
local function setupPreview(display, data)
namePanel.profileName:setText('')
if not display then
resetFields()
else
for i, value in ipairs(data) do
local widget = slotWidgets[i]
if value == false then
widget:setChecked(false)
widget:setItemId(0)
elseif value == true then
widget:setChecked(true)
widget:setItemId(0)
else
widget:setChecked(false)
widget:setItemId(value)
end
end
end
end
local function refreshRules()
local list = listPanel.list
list:destroyChildren()
for i,v in ipairs(config.rules) do
local widget = UI.createWidget('Rule', list)
widget:setId(v.name)
widget:setText(v.name)
widget.ruleData = v
widget.remove.onClick = function()
widget:destroy()
table.remove(config.rules, table.find(config.rules, v))
listPanel.up:setEnabled(false)
listPanel.down:setEnabled(false)
refreshRules()
end
widget.visible:setColor(v.visible and "green" or "red")
widget.visible.onClick = function()
v.visible = not v.visible
widget.visible:setColor(v.visible and "green" or "red")
end
widget.enabled:setChecked(v.enabled)
widget.enabled.onClick = function()
v.enabled = not v.enabled
widget.enabled:setChecked(v.enabled)
end
widget.onHoverChange = function(widget, hover)
for i, child in ipairs(list:getChildren()) do
if child.display then return end
end
setupPreview(hover, widget.ruleData.data)
end
widget.onDoubleClick = function(widget)
local ruleData = widget.ruleData
widget.display = true
setupPreview(true, ruleData.data)
conditionNumber = ruleData.mainCondition
optionalConditionNumber = ruleData.optionalCondition
setCondition(false, optionalConditionNumber)
setCondition(true, conditionNumber)
inputPanel.useSecondCondition:setOption(ruleData.relation)
namePanel.profileName:setText(v.name)
if type(ruleData.mainValue) == "string" then
inputPanel.condition.text:setText(ruleData.mainValue)
elseif type(ruleData.mainValue) == "number" then
inputPanel.condition.spinbox:setValue(ruleData.mainValue)
end
if type(ruleData.optValue) == "string" then
inputPanel.optionalCondition.text:setText(ruleData.optValue)
elseif type(ruleData.optValue) == "number" then
inputPanel.optionalCondition.spinbox:setValue(ruleData.optValue)
end
end
widget.onClick = function()
local panel = listPanel
if #panel.list:getChildren() == 1 then
panel.up:setEnabled(false)
panel.down:setEnabled(false)
elseif panel.list:getChildIndex(panel.list:getFocusedChild()) == 1 then
panel.up:setEnabled(false)
panel.down:setEnabled(true)
elseif panel.list:getChildIndex(panel.list:getFocusedChild()) == #panel.list:getChildren() then
panel.up:setEnabled(true)
panel.down:setEnabled(false)
else
panel.up:setEnabled(true)
panel.down:setEnabled(true)
end
end
end
end
refreshRules()
inputPanel.add.onClick = function(widget)
local mainVal
local optVal
local t = {}
local relation = inputPanel.useSecondCondition:getText()
local profileName = namePanel.profileName:getText()
if profileName:len() == 0 then
return warn("Please fill profile name!")
end
for i, widget in ipairs(slotWidgets) do
local checked = widget:isChecked()
local id = widget:getItemId()
if checked then
table.insert(t, true) -- unequip selected slot
elseif id then
table.insert(t, id) -- equip selected item
else
table.insert(t, false) -- ignore slot
end
end
if conditionNumber == 1 then
mainVal = nil
elseif conditionNumber == 8 then
mainVal = inputPanel.condition.text:getText()
if mainVal:len() == 0 then
return warn("[vBot Equipper] Please fill the name of the creature.")
end
elseif conditionNumber == 9 then
mainVal = inputPanel.condition.text:getText()
if mainVal:len() == 0 then
return warn("[vBot Equipper] Please set correct hotkey.")
end
else
mainVal = inputPanel.condition.spinbox:getValue()
end
if relation ~= "-" then
if optionalConditionNumber == 1 then
optVal = nil
elseif optionalConditionNumber == 8 then
optVal = inputPanel.optionalCondition.text:getText()
if optVal:len() == 0 then
return warn("[vBot Equipper] Please fill the name of the creature.")
end
elseif optionalConditionNumber == 9 then
optVal = inputPanel.optionalCondition.text:getText()
if optVal:len() == 0 then
return warn("[vBot Equipper] Please set correct hotkey.")
end
else
optVal = inputPanel.optionalCondition.spinbox:getValue()
end
end
local index
for i, v in ipairs(config.rules) do
if v.name == profileName then
index = i -- search if there's already rule with this name
end
end
local ruleData = {
name = profileName,
data = t,
enabled = true,
visible = true,
mainCondition = conditionNumber,
optionalCondition = optionalConditionNumber,
mainValue = mainVal,
optValue = optVal,
relation = relation,
}
if index then
config.rules[index] = ruleData -- overwrite
else
table.insert(config.rules, ruleData) -- create new one
end
for i, child in ipairs(listPanel.list:getChildren()) do
child.display = false
end
resetFields()
refreshRules()
end
mainWindow.bossList.onClick = function(widget)
if bossPanel:isVisible() then
bossPanel:hide()
listPanel:show()
widget:setText('Boss List')
else
bossPanel:show()
listPanel:hide()
widget:setText('Rule List')
end
end
-- create boss labels
for i, v in ipairs(config.bosses) do
local widget = UI.createWidget("BossLabel", bossPanel.list)
widget:setText(v)
widget.remove.onClick = function()
table.remove(config.bosses, table.find(config.bosses, v))
widget:destroy()
end
end
bossPanel.add.onClick = function()
local name = bossPanel.name:getText()
if name:len() == 0 then
return warn("[Equipped] Please enter boss name!")
elseif table.find(config.bosses, name:lower(), true) then
return warn("[Equipper] Boss already added!")
end
local widget = UI.createWidget("BossLabel", bossPanel.list)
widget:setText(name)
widget.remove.onClick = function()
table.remove(config.bosses, table.find(config.bosses, name))
widget:destroy()
end
table.insert(config.bosses, name)
bossPanel.name:setText('')
end
local function interpreteCondition(n, v)
if n == 1 then
return true
elseif n == 2 then
return getMonsters() > v
elseif n == 3 then
return getMonsters() < v
elseif n == 4 then
return hppercent() < v
elseif n == 5 then
return hppercent() > v
elseif n == 6 then
return manapercent() < v
elseif n == 7 then
return manapercent() > v
elseif n == 8 then
return target() and target():getName():lower() == v:lower() or false
elseif n == 9 then
return g_keyboard.isKeyPressed(v)
elseif n == 10 then
return isParalyzed()
elseif n == 11 then
return isInPz()
elseif n == 12 then
return getPlayers() > v
elseif n == 13 then
return getPlayers() < v
elseif n == 14 then
return TargetBot.Danger() > v and TargetBot.isOn()
elseif n == 15 then
return isBlackListedPlayerInRange(v)
elseif n == 16 then
return target() and table.find(config.bosses, target():getName():lower(), true) and true or false
elseif n == 17 then
return not isInPz()
elseif n == 18 then
return CaveBot.isOn() and TargetBot.isOff()
end
end
local function finalCheck(first,relation,second)
if relation == "-" then
return first
elseif relation == "and" then
return first and second
elseif relation == "or" then
return first or second
end
end
local function isEquipped(id)
local t = {getNeck(), getHead(), getBody(), getRight(), getLeft(), getLeg(), getFeet(), getFinger(), getAmmo()}
local ids = {id, getInactiveItemId(id), getActiveItemId(id)}
for i, slot in pairs(t) do
if slot and table.find(ids, slot:getId()) then
return true
end
end
return false
end
local function unequipItem(table)
local slots = {getHead(), getBody(), getLeg(), getFeet(), getNeck(), getLeft(), getRight(), getFinger(), getAmmo()}
if type(table) ~= "table" then return end
for i, slot in ipairs(table) do
local physicalSlot = slots[i]
if slot == true and physicalSlot then
local id = physicalSlot:getId()
if g_game.getClientVersion() >= 910 then
-- new tibia
g_game.equipItemId(id)
else
-- old tibia
local dest
for i, container in ipairs(getContainers()) do
local cname = container:getName()
if not containerIsFull(container) then
if not cname:find("loot") and (cname:find("backpack") or cname:find("bag") or cname:find("chess")) then
dest = container
end
break
end
end
if not dest then return true end
local pos = dest:getSlotPosition(dest:getItemsCount())
g_game.move(physicalSlot, pos, physicalSlot:getCount())
end
return true
end
end
return false
end
local function equipItem(id, slot)
-- need to correct slots...
if slot == 2 then
slot = 4
elseif slot == 3 then
slot = 7
elseif slot == 8 then
slot = 9
elseif slot == 5 then
slot = 2
elseif slot == 4 then
slot = 8
elseif slot == 9 then
slot = 10
elseif slot == 7 then
slot = 5
end
if g_game.getClientVersion() >= 910 then
-- new tibia
return g_game.equipItemId(id)
else
-- old tibia
local item = findItem(id)
return moveToSlot(item, slot)
end
end
local function markChild(child)
if mainWindow:isVisible() then
for i, child in ipairs(listPanel.list:getChildren()) do
if child ~= widget then
child:setColor('white')
end
end
widget:setColor('green')
end
end
local missingItem = false
local lastRule = false
local correctEq = false
EquipManager = macro(50, function()
if not config.enabled then return end
if #config.rules == 0 then return end
for i, widget in ipairs(listPanel.list:getChildren()) do
local rule = widget.ruleData
if rule.enabled then
-- conditions
local firstCondition = interpreteCondition(rule.mainCondition, rule.mainValue)
local optionalCondition = nil
if rule.relation ~= "-" then
optionalCondition = interpreteCondition(rule.optionalCondition, rule.optValue)
end
-- checks
if finalCheck(firstCondition, rule.relation, optionalCondition) then
-- performance edits, loop reset
local resetLoop = not missingItem and correctEq and lastRule == rule
if resetLoop then return end
-- reset executed rule
-- first check unequip
if unequipItem(rule.data) == true then
delay(200)
return
end
-- equiploop
for slot, item in ipairs(rule.data) do
if type(item) == "number" and item > 100 then
if not isEquipped(item) then
if rule.visible then
if findItem(item) then
missingItem = false
delay(200)
return equipItem(item, slot)
else
missingItem = true
end
else
missingItem = false
delay(200)
return equipItem(item, slot)
end
end
end
end
correctEq = not missingItem and true or false
-- even if nothing was done, exit function to hold rule
return
end
end
end
end)

View File

@ -0,0 +1,712 @@
local standBySpells = false
local standByItems = false
local red = "#ff0800" -- "#ff0800" / #ea3c53 best
local blue = "#7ef9ff"
setDefaultTab("HP")
local healPanelName = "healbot"
local ui = setupUI([[
Panel
height: 38
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('HealBot')
Button
id: settings
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
Button
id: 1
anchors.top: prev.bottom
anchors.left: parent.left
text: 1
margin-right: 2
margin-top: 4
size: 17 17
Button
id: 2
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 2
margin-left: 4
size: 17 17
Button
id: 3
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 3
margin-left: 4
size: 17 17
Button
id: 4
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 4
margin-left: 4
size: 17 17
Button
id: 5
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
text: 5
margin-left: 4
size: 17 17
Label
id: name
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
anchors.right: parent.right
text-align: center
margin-left: 4
height: 17
text: Profile #1
background: #292A2A
]])
ui:setId(healPanelName)
if not HealBotConfig[healPanelName] or not HealBotConfig[healPanelName][1] or #HealBotConfig[healPanelName] ~= 5 then
HealBotConfig[healPanelName] = {
[1] = {
enabled = false,
spellTable = {},
itemTable = {},
name = "Profile #1",
Visible = true,
Cooldown = true,
Interval = true,
Conditions = true,
Delay = true,
MessageDelay = false
},
[2] = {
enabled = false,
spellTable = {},
itemTable = {},
name = "Profile #2",
Visible = true,
Cooldown = true,
Interval = true,
Conditions = true,
Delay = true,
MessageDelay = false
},
[3] = {
enabled = false,
spellTable = {},
itemTable = {},
name = "Profile #3",
Visible = true,
Cooldown = true,
Interval = true,
Conditions = true,
Delay = true,
MessageDelay = false
},
[4] = {
enabled = false,
spellTable = {},
itemTable = {},
name = "Profile #4",
Visible = true,
Cooldown = true,
Interval = true,
Conditions = true,
Delay = true,
MessageDelay = false
},
[5] = {
enabled = false,
spellTable = {},
itemTable = {},
name = "Profile #5",
Visible = true,
Cooldown = true,
Interval = true,
Conditions = true,
Delay = true,
MessageDelay = false
},
}
end
if not HealBotConfig.currentHealBotProfile or HealBotConfig.currentHealBotProfile == 0 or HealBotConfig.currentHealBotProfile > 5 then
HealBotConfig.currentHealBotProfile = 1
end
-- finding correct table, manual unfortunately
local currentSettings
local setActiveProfile = function()
local n = HealBotConfig.currentHealBotProfile
currentSettings = HealBotConfig[healPanelName][n]
end
setActiveProfile()
local activeProfileColor = function()
for i=1,5 do
if i == HealBotConfig.currentHealBotProfile then
ui[i]:setColor("green")
else
ui[i]:setColor("white")
end
end
end
activeProfileColor()
ui.title:setOn(currentSettings.enabled)
ui.title.onClick = function(widget)
currentSettings.enabled = not currentSettings.enabled
widget:setOn(currentSettings.enabled)
vBotConfigSave("heal")
end
ui.settings.onClick = function(widget)
healWindow:show()
healWindow:raise()
healWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
healWindow = UI.createWindow('HealWindow', rootWidget)
healWindow:hide()
healWindow.onVisibilityChange = function(widget, visible)
if not visible then
vBotConfigSave("heal")
healWindow.healer:show()
healWindow.settings:hide()
healWindow.settingsButton:setText("Settings")
end
end
healWindow.settingsButton.onClick = function(widget)
if healWindow.healer:isVisible() then
healWindow.healer:hide()
healWindow.settings:show()
widget:setText("Back")
else
healWindow.healer:show()
healWindow.settings:hide()
widget:setText("Settings")
end
end
local setProfileName = function()
ui.name:setText(currentSettings.name)
end
healWindow.settings.profiles.Name.onTextChange = function(widget, text)
currentSettings.name = text
setProfileName()
end
healWindow.settings.list.Visible.onClick = function(widget)
currentSettings.Visible = not currentSettings.Visible
healWindow.settings.list.Visible:setChecked(currentSettings.Visible)
end
healWindow.settings.list.Cooldown.onClick = function(widget)
currentSettings.Cooldown = not currentSettings.Cooldown
healWindow.settings.list.Cooldown:setChecked(currentSettings.Cooldown)
end
healWindow.settings.list.Interval.onClick = function(widget)
currentSettings.Interval = not currentSettings.Interval
healWindow.settings.list.Interval:setChecked(currentSettings.Interval)
end
healWindow.settings.list.Conditions.onClick = function(widget)
currentSettings.Conditions = not currentSettings.Conditions
healWindow.settings.list.Conditions:setChecked(currentSettings.Conditions)
end
healWindow.settings.list.Delay.onClick = function(widget)
currentSettings.Delay = not currentSettings.Delay
healWindow.settings.list.Delay:setChecked(currentSettings.Delay)
end
healWindow.settings.list.MessageDelay.onClick = function(widget)
currentSettings.MessageDelay = not currentSettings.MessageDelay
healWindow.settings.list.MessageDelay:setChecked(currentSettings.MessageDelay)
end
local refreshSpells = function()
if currentSettings.spellTable then
healWindow.healer.spells.spellList:destroyChildren()
for _, entry in pairs(currentSettings.spellTable) do
local label = UI.createWidget("SpellEntry", healWindow.healer.spells.spellList)
label.enabled:setChecked(entry.enabled)
label.enabled.onClick = function(widget)
standBySpells = false
standByItems = false
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
end
label.remove.onClick = function(widget)
standBySpells = false
standByItems = false
table.removevalue(currentSettings.spellTable, entry)
reindexTable(currentSettings.spellTable)
label:destroy()
end
label:setText("(MP>" .. entry.cost .. ") " .. entry.origin .. entry.sign .. entry.value .. ": " .. entry.spell)
end
end
end
refreshSpells()
local refreshItems = function()
if currentSettings.itemTable then
healWindow.healer.items.itemList:destroyChildren()
for _, entry in pairs(currentSettings.itemTable) do
local label = UI.createWidget("ItemEntry", healWindow.healer.items.itemList)
label.enabled:setChecked(entry.enabled)
label.enabled.onClick = function(widget)
standBySpells = false
standByItems = false
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
end
label.remove.onClick = function(widget)
standBySpells = false
standByItems = false
table.removevalue(currentSettings.itemTable, entry)
reindexTable(currentSettings.itemTable)
label:destroy()
end
label.id:setItemId(entry.item)
label:setText(entry.origin .. entry.sign .. entry.value .. ": " .. entry.item)
end
end
end
refreshItems()
healWindow.healer.spells.MoveUp.onClick = function(widget)
local input = healWindow.healer.spells.spellList:getFocusedChild()
if not input then return end
local index = healWindow.healer.spells.spellList:getChildIndex(input)
if index < 2 then return end
local t = currentSettings.spellTable
t[index],t[index-1] = t[index-1], t[index]
healWindow.healer.spells.spellList:moveChildToIndex(input, index - 1)
healWindow.healer.spells.spellList:ensureChildVisible(input)
end
healWindow.healer.spells.MoveDown.onClick = function(widget)
local input = healWindow.healer.spells.spellList:getFocusedChild()
if not input then return end
local index = healWindow.healer.spells.spellList:getChildIndex(input)
if index >= healWindow.healer.spells.spellList:getChildCount() then return end
local t = currentSettings.spellTable
t[index],t[index+1] = t[index+1],t[index]
healWindow.healer.spells.spellList:moveChildToIndex(input, index + 1)
healWindow.healer.spells.spellList:ensureChildVisible(input)
end
healWindow.healer.items.MoveUp.onClick = function(widget)
local input = healWindow.healer.items.itemList:getFocusedChild()
if not input then return end
local index = healWindow.healer.items.itemList:getChildIndex(input)
if index < 2 then return end
local t = currentSettings.itemTable
t[index],t[index-1] = t[index-1], t[index]
healWindow.healer.items.itemList:moveChildToIndex(input, index - 1)
healWindow.healer.items.itemList:ensureChildVisible(input)
end
healWindow.healer.items.MoveDown.onClick = function(widget)
local input = healWindow.healer.items.itemList:getFocusedChild()
if not input then return end
local index = healWindow.healer.items.itemList:getChildIndex(input)
if index >= healWindow.healer.items.itemList:getChildCount() then return end
local t = currentSettings.itemTable
t[index],t[index+1] = t[index+1],t[index]
healWindow.healer.items.itemList:moveChildToIndex(input, index + 1)
healWindow.healer.items.itemList:ensureChildVisible(input)
end
healWindow.healer.spells.addSpell.onClick = function(widget)
local spellFormula = healWindow.healer.spells.spellFormula:getText():trim()
local manaCost = tonumber(healWindow.healer.spells.manaCost:getText())
local spellTrigger = tonumber(healWindow.healer.spells.spellValue:getText())
local spellSource = healWindow.healer.spells.spellSource:getCurrentOption().text
local spellEquasion = healWindow.healer.spells.spellCondition:getCurrentOption().text
local source
local equasion
if not manaCost then
warn("HealBot: incorrect mana cost value!")
healWindow.healer.spells.spellFormula:setText('')
healWindow.healer.spells.spellValue:setText('')
healWindow.healer.spells.manaCost:setText('')
return
end
if not spellTrigger then
warn("HealBot: incorrect condition value!")
healWindow.healer.spells.spellFormula:setText('')
healWindow.healer.spells.spellValue:setText('')
healWindow.healer.spells.manaCost:setText('')
return
end
if spellSource == "Current Mana" then
source = "MP"
elseif spellSource == "Current Health" then
source = "HP"
elseif spellSource == "Mana Percent" then
source = "MP%"
elseif spellSource == "Health Percent" then
source = "HP%"
else
source = "burst"
end
if spellEquasion == "Above" then
equasion = ">"
elseif spellEquasion == "Below" then
equasion = "<"
else
equasion = "="
end
if spellFormula:len() > 0 then
table.insert(currentSettings.spellTable, {index = #currentSettings.spellTable+1, spell = spellFormula, sign = equasion, origin = source, cost = manaCost, value = spellTrigger, enabled = true})
healWindow.healer.spells.spellFormula:setText('')
healWindow.healer.spells.spellValue:setText('')
healWindow.healer.spells.manaCost:setText('')
end
standBySpells = false
standByItems = false
refreshSpells()
end
healWindow.healer.items.addItem.onClick = function(widget)
local id = healWindow.healer.items.itemId:getItemId()
local trigger = tonumber(healWindow.healer.items.itemValue:getText())
local src = healWindow.healer.items.itemSource:getCurrentOption().text
local eq = healWindow.healer.items.itemCondition:getCurrentOption().text
local source
local equasion
if not trigger then
warn("HealBot: incorrect trigger value!")
healWindow.healer.items.itemId:setItemId(0)
healWindow.healer.items.itemValue:setText('')
return
end
if src == "Current Mana" then
source = "MP"
elseif src == "Current Health" then
source = "HP"
elseif src == "Mana Percent" then
source = "MP%"
elseif src == "Health Percent" then
source = "HP%"
else
source = "burst"
end
if eq == "Above" then
equasion = ">"
elseif eq == "Below" then
equasion = "<"
else
equasion = "="
end
if id > 100 then
table.insert(currentSettings.itemTable, {index = #currentSettings.itemTable+1,item = id, sign = equasion, origin = source, value = trigger, enabled = true})
standBySpells = false
standByItems = false
refreshItems()
healWindow.healer.items.itemId:setItemId(0)
healWindow.healer.items.itemValue:setText('')
end
end
healWindow.closeButton.onClick = function(widget)
healWindow:hide()
end
local loadSettings = function()
ui.title:setOn(currentSettings.enabled)
setProfileName()
healWindow.settings.profiles.Name:setText(currentSettings.name)
refreshSpells()
refreshItems()
healWindow.settings.list.Visible:setChecked(currentSettings.Visible)
healWindow.settings.list.Cooldown:setChecked(currentSettings.Cooldown)
healWindow.settings.list.Delay:setChecked(currentSettings.Delay)
healWindow.settings.list.MessageDelay:setChecked(currentSettings.MessageDelay)
healWindow.settings.list.Interval:setChecked(currentSettings.Interval)
healWindow.settings.list.Conditions:setChecked(currentSettings.Conditions)
end
loadSettings()
local profileChange = function()
setActiveProfile()
activeProfileColor()
loadSettings()
vBotConfigSave("heal")
end
local resetSettings = function()
currentSettings.enabled = false
currentSettings.spellTable = {}
currentSettings.itemTable = {}
currentSettings.Visible = true
currentSettings.Cooldown = true
currentSettings.Delay = true
currentSettings.MessageDelay = false
currentSettings.Interval = true
currentSettings.Conditions = true
currentSettings.name = "Profile #" .. HealBotConfig.currentBotProfile
end
-- profile buttons
for i=1,5 do
local button = ui[i]
button.onClick = function()
HealBotConfig.currentHealBotProfile = i
profileChange()
end
end
healWindow.settings.profiles.ResetSettings.onClick = function()
resetSettings()
loadSettings()
end
-- public functions
HealBot = {} -- global table
HealBot.isOn = function()
return currentSettings.enabled
end
HealBot.isOff = function()
return not currentSettings.enabled
end
HealBot.setOff = function()
currentSettings.enabled = false
ui.title:setOn(currentSettings.enabled)
vBotConfigSave("atk")
end
HealBot.setOn = function()
currentSettings.enabled = true
ui.title:setOn(currentSettings.enabled)
vBotConfigSave("atk")
end
HealBot.getActiveProfile = function()
return HealBotConfig.currentHealBotProfile -- returns number 1-5
end
HealBot.setActiveProfile = function(n)
if not n or not tonumber(n) or n < 1 or n > 5 then
return error("[HealBot] wrong profile parameter! should be 1 to 5 is " .. n)
else
HealBotConfig.currentHealBotProfile = n
profileChange()
end
end
HealBot.show = function()
healWindow:show()
healWindow:raise()
healWindow:focus()
end
end
-- spells
macro(100, function()
if standBySpells then return end
if not currentSettings.enabled then return end
local somethingIsOnCooldown = false
for _, entry in pairs(currentSettings.spellTable) do
if entry.enabled and entry.cost < mana() then
if canCast(entry.spell, not currentSettings.Conditions, not currentSettings.Cooldown) then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and hppercent() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and hppercent() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "HP" then
if entry.sign == "=" and hp() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and hp() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and hp() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "MP%" then
if entry.sign == "=" and manapercent() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and manapercent() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and manapercent() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "MP" then
if entry.sign == "=" and mana() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and mana() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and mana() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "burst" then
if entry.sign == "=" and burstDamageValue() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and burstDamageValue() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and burstDamageValue() <= entry.value then
say(entry.spell)
return
end
end
else
somethingIsOnCooldown = true
end
end
end
if not somethingIsOnCooldown then
standBySpells = true
end
end)
-- items
macro(100, function()
if standByItems then return end
if not currentSettings.enabled or #currentSettings.itemTable == 0 then return end
if currentSettings.Delay and vBot.isUsing then return end
if currentSettings.MessageDelay and vBot.isUsingPotion then return end
if not currentSettings.MessageDelay then
delay(400)
end
if TargetBot.isOn() and TargetBot.Looting.getStatus():len() > 0 and currentSettings.Interval then
if not currentSettings.MessageDelay then
delay(700)
else
delay(200)
end
end
for _, entry in pairs(currentSettings.itemTable) do
local item = findItem(entry.item)
if (not currentSettings.Visible or item) and entry.enabled then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == ">" and hppercent() >= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == "<" and hppercent() <= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
end
elseif entry.origin == "HP" then
if entry.sign == "=" and hp() == tonumberentry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == ">" and hp() >= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == "<" and hp() <= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
end
elseif entry.origin == "MP%" then
if entry.sign == "=" and manapercent() == entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == ">" and manapercent() >= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == "<" and manapercent() <= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
end
elseif entry.origin == "MP" then
if entry.sign == "=" and mana() == entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == ">" and mana() >= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == "<" and mana() <= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
end
elseif entry.origin == "burst" then
if entry.sign == "=" and burstDamageValue() == entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == ">" and burstDamageValue() >= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
elseif entry.sign == "<" and burstDamageValue() <= entry.value then
g_game.useInventoryItemWith(entry.item, player)
return
end
end
end
end
standByItems = true
end)
UI.Separator()
onPlayerHealthChange(function(healthPercent)
standByItems = false
standBySpells = false
end)
onManaChange(function(player, mana, maxMana, oldMana, oldMaxMana)
standByItems = false
standBySpells = false
end)

View File

@ -0,0 +1,492 @@
SettingCheckBox < CheckBox
text-wrap: true
text-auto-resize: true
margin-top: 3
font: verdana-11px-rounded
SpellSourceBoxPopupMenu < ComboBoxPopupMenu
SpellSourceBoxPopupMenuButton < ComboBoxPopupMenuButton
SpellSourceBox < ComboBox
@onSetup: |
self:addOption("Current Mana")
self:addOption("Current Health")
self:addOption("Mana Percent")
self:addOption("Health Percent")
self:addOption("Burst Damage")
SpellConditionBoxPopupMenu < ComboBoxPopupMenu
SpellConditionBoxPopupMenuButton < ComboBoxPopupMenuButton
SpellConditionBox < ComboBox
@onSetup: |
self:addOption("Below")
self:addOption("Above")
self:addOption("Equal To")
SpellEntry < Label
background-color: alpha
text-offset: 18 1
focusable: true
height: 16
font: verdana-11px-rounded
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
text-offset: 1 0
width: 15
height: 15
ItemEntry < Label
background-color: alpha
text-offset: 40 1
focusable: true
height: 16
font: verdana-11px-rounded
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
UIItem
id: id
anchors.left: prev.right
margin-left: 3
anchors.verticalCenter: parent.verticalCenter
size: 15 15
focusable: false
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
text-offset: 1 0
width: 15
height: 15
SpellHealing < FlatPanel
size: 490 130
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
margin-left: 5
text: Spell Healing
color: #269e26
font: verdana-11px-rounded
SpellSourceBox
id: spellSource
anchors.top: spellList.top
anchors.left: spellList.right
margin-left: 80
width: 125
font: verdana-11px-rounded
Label
id: whenSpell
anchors.left: spellList.right
anchors.verticalCenter: prev.verticalCenter
text: When
margin-left: 7
font: verdana-11px-rounded
Label
id: isSpell
anchors.left: spellList.right
anchors.top: whenSpell.bottom
text: Is
margin-top: 9
margin-left: 7
font: verdana-11px-rounded
SpellConditionBox
id: spellCondition
anchors.left: spellSource.left
anchors.top: spellSource.bottom
marin-top: 15
width: 80
font: verdana-11px-rounded
TextEdit
id: spellValue
anchors.left: spellCondition.right
anchors.top: spellCondition.top
anchors.bottom: spellCondition.bottom
anchors.right: spellSource.right
font: verdana-11px-rounded
Label
id: castSpell
anchors.left: isSpell.left
anchors.top: isSpell.bottom
text: Cast
margin-top: 9
font: verdana-11px-rounded
TextEdit
id: spellFormula
anchors.left: spellCondition.left
anchors.top: spellCondition.bottom
anchors.right: spellValue.right
font: verdana-11px-rounded
Label
id: manaSpell
anchors.left: castSpell.left
anchors.top: castSpell.bottom
text: Mana Cost:
margin-top: 8
font: verdana-11px-rounded
TextEdit
id: manaCost
anchors.left: spellFormula.left
anchors.top: spellFormula.bottom
width: 40
font: verdana-11px-rounded
TextList
id: spellList
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.top: parent.top
padding: 1
padding-top: 2
width: 270
margin-bottom: 7
margin-left: 7
margin-top: 10
vertical-scrollbar: spellListScrollBar
VerticalScrollBar
id: spellListScrollBar
anchors.top: spellList.top
anchors.bottom: spellList.bottom
anchors.right: spellList.right
step: 14
pixels-scroll: true
Button
id: addSpell
anchors.right: spellFormula.right
anchors.bottom: spellList.bottom
text: Add
size: 40 17
font: cipsoftFont
Button
id: MoveUp
anchors.right: prev.left
anchors.bottom: prev.bottom
margin-right: 5
text: Move Up
size: 55 17
font: cipsoftFont
Button
id: MoveDown
anchors.right: prev.left
anchors.bottom: prev.bottom
margin-right: 5
text: Move Down
size: 55 17
font: cipsoftFont
ItemHealing < FlatPanel
size: 490 120
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
margin-left: 5
text: Item Healing
color: #ff4513
font: verdana-11px-rounded
SpellSourceBox
id: itemSource
anchors.top: itemList.top
anchors.right: parent.right
margin-right: 10
width: 128
font: verdana-11px-rounded
Label
id: whenItem
anchors.left: itemList.right
anchors.verticalCenter: prev.verticalCenter
text: When
margin-left: 7
font: verdana-11px-rounded
Label
id: isItem
anchors.left: itemList.right
anchors.top: whenItem.bottom
text: Is
margin-top: 9
margin-left: 7
font: verdana-11px-rounded
SpellConditionBox
id: itemCondition
anchors.left: itemSource.left
anchors.top: itemSource.bottom
marin-top: 15
width: 80
font: verdana-11px-rounded
TextEdit
id: itemValue
anchors.left: itemCondition.right
anchors.top: itemCondition.top
anchors.bottom: itemCondition.bottom
width: 49
font: verdana-11px-rounded
Label
id: useItem
anchors.left: isItem.left
anchors.top: isItem.bottom
text: Use
margin-top: 15
font: verdana-11px-rounded
BotItem
id: itemId
anchors.left: itemCondition.left
anchors.top: itemCondition.bottom
TextList
id: itemList
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.top: parent.top
padding: 1
padding-top: 2
width: 270
margin-top: 10
margin-bottom: 7
margin-left: 8
vertical-scrollbar: itemListScrollBar
VerticalScrollBar
id: itemListScrollBar
anchors.top: itemList.top
anchors.bottom: itemList.bottom
anchors.right: itemList.right
step: 14
pixels-scroll: true
Button
id: addItem
anchors.right: itemValue.right
anchors.bottom: itemList.bottom
text: Add
size: 40 17
font: cipsoftFont
Button
id: MoveUp
anchors.right: prev.left
anchors.bottom: prev.bottom
margin-right: 5
text: Move Up
size: 55 17
font: cipsoftFont
Button
id: MoveDown
anchors.right: prev.left
anchors.bottom: prev.bottom
margin-right: 5
text: Move Down
size: 55 17
font: cipsoftFont
HealerPanel < Panel
size: 510 275
SpellHealing
id: spells
anchors.top: parent.top
margin-top: 8
anchors.left: parent.left
ItemHealing
id: items
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 10
HealBotSettingsPanel < Panel
size: 500 267
padding-top: 8
FlatPanel
id: list
anchors.fill: parent
margin-right: 240
padding-left: 6
padding-right: 6
padding-top: 6
layout:
type: verticalBox
Label
text: Additional Settings
text-align: center
font: verdana-11px-rounded
HorizontalSeparator
SettingCheckBox
id: Cooldown
text: Check spell cooldowns
margin-top: 10
SettingCheckBox
id: Visible
text: Items must be visible (recommended)
SettingCheckBox
id: Delay
text: Don't use items when interacting
SettingCheckBox
id: Interval
text: Additional delay when looting corpses
SettingCheckBox
id: Conditions
text: Also check conditions from RL Tibia
SettingCheckBox
id: MessageDelay
text: Cooldown based on "Aaaah..." message
VerticalSeparator
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.left: prev.right
margin-left: 8
FlatPanel
id: profiles
anchors.fill: parent
anchors.left: prev.left
margin-left: 8
margin-right: 8
padding: 8
Label
text: Profile Settings
text-align: center
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
font: verdana-11px-rounded
HorizontalSeparator
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
Label
anchors.top: prev.bottom
margin-top: 30
anchors.left: parent.left
anchors.right: parent.right
text-align: center
font: verdana-11px-rounded
text: Profile Name:
TextEdit
id: Name
anchors.top: prev.bottom
margin-top: 3
anchors.left: parent.left
anchors.right: parent.right
Button
id: ResetSettings
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: Reset Current Profile
text-auto-resize: true
color: #ff4513
HealWindow < MainWindow
!text: tr('Self Healer')
size: 520 360
@onEscape: self:hide()
Label
id: title
anchors.left: parent.left
anchors.top: parent.top
margin-left: 2
!text: tr('More important methods come first (Example: Exura gran above Exura)')
text-align: left
font: verdana-11px-rounded
color: #aeaeae
HealerPanel
id: healer
anchors.top: prev.bottom
anchors.left: parent.left
HealBotSettingsPanel
id: settings
anchors.top: title.bottom
anchors.left: parent.left
visible: false
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-right: 5
Button
id: settingsButton
!text: tr('Settings')
font: cipsoftFont
anchors.left: parent.left
anchors.bottom: parent.bottom
size: 45 21

View File

@ -0,0 +1,252 @@
setDefaultTab("Main")
local panelName = "advancedFriendHealer"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Friend Healer')
Button
id: editList
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]], parent)
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {
minMana = 60,
minFriendHp = 40,
customSpellName = "exura max sio",
customSpell = false,
distance = 8,
itemHeal = false,
id = 3160,
exuraSio = false,
exuraGranSio = false,
exuraMasRes = false,
healEk = false,
healRp = false,
healEd = false,
healMs = false
}
end
local config = storage[panelName]
-- basic elements
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
ui.editList.onClick = function(widget)
sioListWindow:show()
sioListWindow:raise()
sioListWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
sioListWindow = UI.createWindow('SioListWindow', rootWidget)
sioListWindow:hide()
-- TextWindow
sioListWindow.spellName:setText(config.customSpellName)
sioListWindow.spellName.onTextChange = function(widget, text)
config.customSpellName = text
end
-- botswitches
sioListWindow.spell:setOn(config.customSpell)
sioListWindow.spell.onClick = function(widget)
config.customSpell = not config.customSpell
widget:setOn(config.customSpell)
end
sioListWindow.item:setOn(config.itemHeal)
sioListWindow.item.onClick = function(widget)
config.itemHeal = not config.itemHeal
widget:setOn(config.itemHeal)
end
sioListWindow.exuraSio:setOn(config.exuraSio)
sioListWindow.exuraSio.onClick = function(widget)
config.exuraSio = not config.exuraSio
widget:setOn(config.exuraSio)
end
sioListWindow.exuraGranSio:setOn(config.exuraGranSio)
sioListWindow.exuraGranSio.onClick = function(widget)
config.exuraGranSio = not config.exuraGranSio
widget:setOn(config.exuraGranSio)
end
sioListWindow.exuraMasRes:setOn(config.exuraMasRes)
sioListWindow.exuraMasRes.onClick = function(widget)
config.exuraMasRes = not config.exuraMasRes
widget:setOn(config.exuraMasRes)
end
sioListWindow.vocation.ED:setOn(config.healEd)
sioListWindow.vocation.ED.onClick = function(widget)
config.healEd = not config.healEd
widget:setOn(config.healEd)
end
sioListWindow.vocation.MS:setOn(config.healMs)
sioListWindow.vocation.MS.onClick = function(widget)
config.healMs = not config.healMs
widget:setOn(config.healMs)
end
sioListWindow.vocation.EK:setOn(config.healEk)
sioListWindow.vocation.EK.onClick = function(widget)
config.healEk = not config.healEk
widget:setOn(config.healEk)
end
sioListWindow.vocation.RP:setOn(config.healRp)
sioListWindow.vocation.RP.onClick = function(widget)
config.healRp = not config.healRp
widget:setOn(config.healRp)
end
-- functions
local updateMinManaText = function()
sioListWindow.manaInfo:setText("Minimum Mana >= " .. config.minMana .. "%")
end
local updateFriendHpText = function()
sioListWindow.friendHp:setText("Heal Friend Below " .. config.minFriendHp .. "% hp")
end
local updateDistanceText = function()
sioListWindow.distText:setText("Max Distance: " .. config.distance)
end
-- scrollbars and text updates
sioListWindow.Distance:setValue(config.distance)
sioListWindow.Distance.onValueChange = function(scroll, value)
config.distance = value
updateDistanceText()
end
updateDistanceText()
sioListWindow.minMana:setValue(config.minMana)
sioListWindow.minMana.onValueChange = function(scroll, value)
config.minMana = value
updateMinManaText()
end
updateMinManaText()
sioListWindow.minFriendHp:setValue(config.minFriendHp)
sioListWindow.minFriendHp.onValueChange = function(scroll, value)
config.minFriendHp = value
updateFriendHpText()
end
updateFriendHpText()
sioListWindow.itemId:setItemId(config.id)
sioListWindow.itemId.onItemChange = function(widget)
config.id = widget:getItemId()
end
sioListWindow.closeButton.onClick = function(widget)
sioListWindow:hide()
end
end
-- local variables
local newTibia = g_game.getClientVersion() >= 960
local function isValid(name)
if not newTibia then return true end
local voc = vBot.BotServerMembers[name]
if not voc then return true end
if voc == 11 then voc = 1
elseif voc == 12 then voc = 2
elseif voc == 13 then voc = 3
elseif voc == 14 then voc = 4
end
local isOk = false
if voc == 1 and config.healEk then
isOk = true
elseif voc == 2 and config.healRp then
isOk = true
elseif voc == 3 and config.healMs then
isOk = true
elseif voc == 4 and config.healEd then
isOk = true
end
return isOk
end
macro(200, function()
if not config.enabled then return end
if modules.game_cooldown.isGroupCooldownIconActive(2) then return end
--[[
1. custom spell
2. exura gran sio - at 50% of minHpValue
3. exura gran mas res
4. exura sio
5. item healing
--]]
-- exura gran sio & custom spell
if config.customSpell or config.exuraGranSio then
for i, spec in ipairs(getSpectators()) do
if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then
if isFriend(spec) then
if config.customSpell and spec:getHealthPercent() <= config.minFriendHp then
return cast(config.customSpellName .. ' "' .. spec:getName() .. '"', 1000)
end
if config.exuraGranSio and spec:getHealthPercent() <= config.minFriendHp/3 then
if canCast('exura gran sio "' .. spec:getName() ..'"') then
return cast('exura gran sio "' .. spec:getName() ..'"', 60000)
end
end
end
end
end
end
-- exura gran mas res and standard sio
local friends = 0
if config.exuraMasRes then
for i, spec in ipairs(getSpectators(player, largeRuneArea)) do
if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then
if isFriend(spec) and spec:getHealthPercent() <= config.minFriendHp then
friends = friends + 1
end
end
end
if friends > 1 then
return cast('exura gran mas res', 2000)
end
end
if config.exuraSio or config.itemHeal then
for i, spec in ipairs(getSpectators()) do
if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then
if isFriend(spec) then
if spec:getHealthPercent() <= config.minFriendHp then
if config.exuraSio then
return cast('exura sio "' .. spec:getName() .. '"', 1000)
elseif findItem(config.id) and distanceFromPlayer(spec:getPosition()) <= config.distance then
return useWith(config.id, spec)
end
end
end
end
end
end
end)
addSeparator()

View File

@ -0,0 +1,223 @@
local panelName = "alarms"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Alarms')
Button
id: alerts
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Edit
]])
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {}
end
local config = storage[panelName]
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
local window = UI.createWindow("AlarmsWindow")
window:hide()
ui.alerts.onClick = function()
window:show()
window:raise()
window:focus()
end
local widgets =
{
"AlarmCheckBox",
"AlarmCheckBoxAndSpinBox",
"AlarmCheckBoxAndTextEdit"
}
local parents =
{
window.list,
window.settingsList
}
-- type
addAlarm = function(id, title, defaultValue, alarmType, parent, tooltip)
local widget = UI.createWidget(widgets[alarmType], parents[parent])
widget:setId(id)
if type(config[id]) ~= 'table' then
config[id] = {}
end
widget.tick:setText(title)
widget.tick:setChecked(config[id].enabled)
widget.tick:setTooltip(tooltip)
widget.tick.onClick = function()
config[id].enabled = not config[id].enabled
widget.tick:setChecked(config[id].enabled)
end
if alarmType > 1 and type(config[id].value) == 'nil' then
config[id].value = defaultValue
end
if alarmType == 2 then
widget.value:setValue(config[id].value)
widget.value.onValueChange = function(widget, value)
config[id].value = value
end
elseif alarmType == 3 then
widget.text:setText(config[id].value)
widget.text.onTextChange = function(widget, newText)
config[id].value = newText
end
end
end
-- settings
addAlarm("ignoreFriends", "Ignore Friends", true, 1, 2)
addAlarm("flashClient", "Flash Client", true, 1, 2)
-- alarm list
addAlarm("damageTaken", "Damage Taken", false, 1, 1)
addAlarm("lowHealth", "Low Health", 20, 2, 1)
addAlarm("lowMana", "Low Mana", 20, 2, 1)
addAlarm("playerAttack", "Player Attack", false, 1, 1)
UI.Separator(window.list)
addAlarm("privateMsg", "Private Message", false, 1, 1)
addAlarm("defaultMsg", "Default Message", false, 1, 1)
addAlarm("customMessage", "Custom Message:", "", 3, 1, "You can add text, that if found in any incoming message will trigger alert.\n You can add many, just separate them by comma.")
UI.Separator(window.list)
addAlarm("creatureDetected", "Creature Detected", false, 1, 1)
addAlarm("playerDetected", "Player Detected", false, 1, 1)
addAlarm("creatureName", "Creature Name:", "", 3, 1, "You can add a name or part of it, that if found in any visible creature name will trigger alert.\nYou can add many, just separate them by comma.")
local lastCall = now
local function alarm(file, windowText)
if now - lastCall < 2000 then return end -- 2s delay
lastCall = now
if not g_resources.fileExists(file) then
file = "/sounds/alarm.ogg"
lastCall = now + 4000 -- alarm.ogg length is 6s
end
if modules.game_bot.g_app.getOs() == "windows" and config.flashClient.enabled then
g_window.flash()
end
g_window.setTitle(player:getName() .. " - " .. windowText)
playSound(file)
end
-- damage taken & custom message
onTextMessage(function(mode, text)
if not config.enabled then return end
if mode == 22 and config.damageTaken.enabled then
return alarm('/sounds/magnum.ogg', "Damage Received!")
end
if config.customMessage.enabled then
local alertText = config.customMessage.value
if alertText:len() > 0 then
text = text:lower()
local parts = string.split(alertText, ",")
for i=1,#parts do
local part = parts[i]
part = part:trim()
part = part:lower()
if text:find(part) then
return alarm('/sounds/magnum.ogg', "Special Message!")
end
end
end
end
end)
-- default & private message
onTalk(function(name, level, mode, text, channelId, pos)
if not config.enabled then return end
if name == player:getName() then return end -- ignore self messages
if config.ignoreFriends.enabled and isFriend(name) then return end -- ignore friends if enabled
if mode == 1 and config.defaultMsg.enabled then
return alarm("/sounds/magnum.ogg", "Default Message!")
end
if mode == 4 and config.privateMsg.enabled then
return alarm("/sounds/Private_Message.ogg", "Private Message!")
end
end)
-- health & mana
macro(100, function()
if not config.enabled then return end
if config.lowHealth.enabled then
if hppercent() < config.lowHealth.value then
return alarm("/sounds/Low_Health.ogg", "Low Health!")
end
end
if config.lowMana.enabled then
if hppercent() < config.lowMana.value then
return alarm("/sounds/Low_Mana.ogg", "Low Mana!")
end
end
for i, spec in ipairs(getSpectators()) do
if not spec:isLocalPlayer() and not (config.ignoreFriends.enabled and isFriend(spec)) then
if config.creatureDetected.enabled then
return alarm("/sounds/magnum.ogg", "Creature Detected!")
end
if spec:isPlayer() then
if spec:isTimedSquareVisible() and config.playerAttack.enabled then
return alarm("/sounds/Player_Attack.ogg", "Player Attack!")
end
if config.playerDetected.enabled then
return alarm("/sounds/Player_Detected.ogg", "Player Detected!")
end
end
if config.creatureName.enabled then
local name = spec:getName():lower()
local fragments = string.split(config.creatureName.value, ",")
for i=1,#fragments do
local frag = fragments[i]:trim():lower()
if name:lower():find(frag) then
return alarm("/sounds/alarm.ogg", "Special Creature Detected!")
end
end
end
end
end
end)

View File

@ -0,0 +1,135 @@
AlarmCheckBox < Panel
height: 20
margin-top: 2
CheckBox
id: tick
anchors.fill: parent
margin-top: 4
font: verdana-11px-rounded
text: Player Attack
text-offset: 17 -3
AlarmCheckBoxAndSpinBox < Panel
height: 20
margin-top: 2
CheckBox
id: tick
anchors.fill: parent
anchors.right: next.left
margin-top: 4
font: verdana-11px-rounded
text: Player Attack
text-offset: 17 -3
SpinBox
id: value
anchors.top: parent.top
margin-top: 1
margin-bottom: 1
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 40
minimum: 0
maximum: 100
step: 1
editable: true
focusable: true
AlarmCheckBoxAndTextEdit < Panel
height: 20
margin-top: 2
CheckBox
id: tick
anchors.fill: parent
anchors.right: next.left
margin-top: 4
font: verdana-11px-rounded
text: Creature Name
text-offset: 17 -3
BotTextEdit
id: text
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 150
font: terminus-10px
margin-top: 1
margin-bottom: 1
AlarmsWindow < MainWindow
!text: tr('Alarms')
size: 330 400
padding: 15
@onEscape: self:hide()
FlatPanel
id: list
anchors.fill: parent
anchors.bottom: settingsList.top
margin-bottom: 20
margin-top: 10
layout: verticalBox
padding: 10
padding-top: 5
FlatPanel
id: settingsList
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: separator.top
margin-bottom: 5
margin-top: 10
padding: 5
padding-left: 10
layout:
type: verticalBox
fit-children: true
Label
anchors.verticalCenter: settingsList.top
anchors.left: settingsList.left
margin-left: 5
width: 200
text: Alarms Settings
font: verdana-11px-rounded
color: #9f5031
Label
anchors.verticalCenter: list.top
anchors.left: list.left
margin-left: 5
width: 200
text: Active Alarms
font: verdana-11px-rounded
color: #9f5031
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
ResizeBorder
id: bottomResizeBorder
anchors.fill: separator
height: 3
minimum: 260
maximum: 600
margin-left: 3
margin-right: 3
background: #ffffff88
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-right: 5
@onClick: self:getParent():hide()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,514 @@
BossCreaturePanel < Panel
height: 38
UICreature
id: creature
size: 35 35
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
old-scaling: true
margin-left: 3
Label
id: name
anchors.left: creature.right
margin: 1
margin-left: 5
margin-top: 4
anchors.top: parent.top
anchors.bottom: creature.verticalCenter
anchors.right: parent.right
font: verdana-11px-rounded
color: #FFFFFF
text: Duke Krule
Label
id: cooldown
anchors.left: creature.right
margin: 1
margin-left: 5
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: creature.verticalCenter
font: verdana-11px-rounded
text: 19h 20min
SearchPanel < TextEdit
placeholder: Type to search
margin-top: 1
@onClick: modules.client_textedit.show(self)
Button
id: clear
anchors.right: parent.right
margin-right: -2
anchors.verticalCenter: parent.verticalCenter
size: 18 18
text: X
@onClick: |
self:getParent():setText("")
TrackerItem < Panel
height: 40
BotItem
id: item
anchors.top: parent.top
margin-top: 2
anchors.left: parent.left
image-source:
UIWidget
id: name
anchors.top: prev.top
margin-top: 1
anchors.bottom: prev.verticalCenter
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
text: Set Item to start track.
text-align:left
font: verdana-11px-rounded
color: #FFFFFF
UIWidget
id: drops
anchors.top: prev.bottom
margin-top: 3
anchors.bottom: Item.bottom
anchors.left: prev.left
anchors.right: parent.right
font: verdana-11px-rounded
text-align:left
text: Loot Drops: 0
color: #CCCCCC
DualLabel < Label
height: 15
text-offset: 4 0
font: verdana-11px-rounded
text-align: left
width: 50
Label
id: value
anchors.right: parent.right
margin-right: 4
anchors.verticalCenter: parent.verticalCenter
width: 200
font: verdana-11px-rounded
text-align: right
text: 0
MemberWidget < Panel
height: 85
margin-top: 3
UICreature
id: creature
anchors.top: parent.top
anchors.left: parent.left
anchors.bottom: parent.bottom
size: 28 28
UIWidget
id: name
anchors.left: prev.right
margin-left: 5
anchors.top: parent.top
height: 12
anchors.right: parent.right
text: Player Name
font: verdana-11px-rounded
text-align: left
ProgressBar
id: health
anchors.left: prev.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 2
height: 7
background-color: #00c000
phantom: false
ProgressBar
id: mana
anchors.left: prev.left
anchors.right: parent.right
anchors.top: prev.bottom
height: 7
background-color: #0000FF
phantom: false
DualLabel
id: balance
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 5
text: Balance:
DualLabel
id: damage
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
text: Damage:
DualLabel
id: healing
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
text: Healing:
AnalyzerPriceLabel < Label
background-color: alpha
text-offset: 2 0
focusable: true
height: 16
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
width: 15
height: 15
AnalyzerListPanel < Panel
padding-left: 4
padding-right: 4
layout:
type: verticalBox
fit-children: true
ListLabel < Label
height: 15
font: verdana-11px-rounded
text-offset: 15 0
AnalyzerItemsPanel < Panel
id: List
padding: 2
layout:
type: grid
cell-size: 33 33
cell-spacing: 1
num-columns: 5
fit-children: true
AnalyzerLootItem < UIItem
opacity: 0.87
height: 37
margin-left: 1
virtual: true
background-color: alpha
Label
id: count
font: verdana-11px-rounded
color: white
opacity: 0.87
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 2
text-align: right
text: 0
AnalyzerGraph < UIGraph
height: 140
capacity: 400
line-width: 1
color: red
margin-top: 5
margin-left: 5
margin-right: 5
background-color: #383636
padding: 5
font: verdana-11px-rounded
image-source: /images/ui/graph_background
AnalyzerProgressBar < ProgressBar
background-color: green
height: 5
margin-top: 3
phantom: false
margin-left: 3
margin-right: 3
border: 1 black
AnalyzerButton < Button
height: 22
margin-bottom: 2
font: verdana-11px-rounded
text-offset: 0 4
MainAnalyzerWindow < MiniWindow
id: MainAnalyzerWindow
text: Analytics Selector
height: 293
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-left: 5
padding-right: 5
padding-top: 5
layout: verticalBox
AnalyzerButton
id: HuntingAnalyzer
text: Hunting Analyzer
AnalyzerButton
id: LootAnalyzer
text: Loot Analyzer
AnalyzerButton
id: SupplyAnalyzer
text: Supply Analyzer
AnalyzerButton
id: ImpactAnalyzer
text: Impact Analyzer
AnalyzerButton
id: XPAnalyzer
text: XP Analyzer
AnalyzerButton
id: DropTracker
text: Drop Tracker
AnalyzerButton
id: Stats
text: CaveBot Stats
color: #74B73E
AnalyzerButton
id: PartyHunt
text: Party Hunt
color: #3895D3
AnalyzerButton
id: BossTracker
text: Boss Cooldowns
color: #df3afb
AnalyzerButton
id: Settings
text: Features & Settings
color: #FABD02
AnalyzerButton
id: ResetSession
text: Reset Session
color: #FF0000
HuntingAnalyzer < MiniWindow
id: HuntingAnalyzerWindow
text: Hunt Analyzer
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-top: 3
layout: verticalBox
LootAnalyzer < MiniWindow
id: LootAnalyzerWindow
text: Loot Analyzer
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-top: 3
layout: verticalBox
SupplyAnalyzer < MiniWindow
id: SupplyAnalyzerWindow
text: Supply Analyzer
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-top: 3
layout: verticalBox
ImpactAnalyzer < MiniWindow
id: ImpactAnalyzerWindow
text: Impact Analyzer
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-top: 3
layout: verticalBox
XPAnalyzer < MiniWindow
id: XPAnalyzerWindow
text: XP Analyzer
height: 150
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-top: 3
layout: verticalBox
PartyAnalyzerWindow < MiniWindow
id: PartyAnalyzerWindow
text: Party Hunt
height: 200
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-left: 3
padding-right: 3
padding-top: 1
layout: verticalBox
DropTracker < MiniWindow
id: DropTracker
text: Drop Tracker
height: 200
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-left: 3
padding-right: 3
padding-top: 1
layout: verticalBox
CaveBotStats < MiniWindow
id: CaveBotStats
text: CaveBot Stats
height: 200
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-left: 3
padding-right: 3
padding-top: 1
layout: verticalBox
BossTracker < MiniWindow
id: BossTracker
text: Boss Cooldowns
height: 200
icon: /images/topbuttons/analyzers
MiniWindowContents
padding-left: 3
padding-right: 3
padding-top: 1
layout: verticalBox
SearchPanel
id: search
FeaturesWindow < MainWindow
id: FeaturesWindow
size: 250 370
padding: 15
text: Analyzers Features
@onEscape: self:hide()
TextList
id: CustomPrices
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin-top: 10
padding: 1
height: 220
vertical-scrollbar: CustomPricesScrollBar
VerticalScrollBar
id: CustomPricesScrollBar
anchors.top: CustomPrices.top
anchors.bottom: CustomPrices.bottom
anchors.right: CustomPrices.right
step: 14
pixels-scroll: true
BotItem
id: ID
anchors.left: CustomPrices.left
anchors.top: CustomPrices.bottom
margin-top: 5
SpinBox
id: NewPrice
anchors.left: prev.right
margin-left: 5
anchors.verticalCenter: prev.verticalCenter
width: 100
minimum: 0
maximum: 1000000000
step: 1
text-align: center
focusable: true
Button
id: addItem
anchors.left: prev.right
margin-left: 5
anchors.verticalCenter: prev.verticalCenter
anchors.right: CustomPrices.right
text: Add
font: verdana-11px-rounded
HorizontalSeparator
anchors.left: ID.right
margin-left: 5
anchors.right: CustomPrices.right
anchors.verticalCenter: ID.top
HorizontalSeparator
id: secondSeparator
anchors.left: ID.right
margin-left: 5
anchors.right: CustomPrices.right
anchors.bottom: ID.bottom
BotSwitch
id: LootChannel
anchors.left: CustomPrices.left
anchors.right: parent.horizontalCenter
margin-right: 2
anchors.top: prev.top
margin-top: 20
text: Loot Channel
font: verdana-11px-rounded
BotSwitch
id: RarityFrames
anchors.left: parent.horizontalCenter
margin-left: 2
anchors.right: CustomPrices.right
anchors.top: secondSeparator.top
margin-top: 20
text: Rarity Frames
font: verdana-11px-rounded
HorizontalSeparator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,33 @@
setDefaultTab("Tools")
g_game.cancelAttackAndFollow()
local frags = 0
local unequip = false
local m = macro(50, "AntiRS & Msg", function() end)
function safeExit()
CaveBot.setOff()
TargetBot.setOff()
g_game.cancelAttackAndFollow()
g_game.cancelAttackAndFollow()
g_game.cancelAttackAndFollow()
modules.game_interface.forceExit()
end
onTextMessage(function(mode, text)
if not m.isOn() then return end
if not text:find("Warning! The murder of") then return end
frags = frags + 1
if killsToRs() < 6 or frags > 1 then
EquipManager.setOff()
schedule(100, function()
local id = getLeft() and getLeft():getId()
if id and not unequip then
unequip = true
g_game.equipItemId(id)
end
safeExit()
end)
end
end)

View File

@ -0,0 +1,22 @@
setDefaultTab("HP")
if voc() ~= 1 and voc() ~= 11 then
if storage.foodItems then
local t = {}
for i, v in pairs(storage.foodItems) do
if not table.find(t, v.id) then
table.insert(t, v.id)
end
end
local foodItems = { 3607, 3585, 3592, 3600, 3601 }
for i, item in pairs(foodItems) do
if not table.find(t, item) then
table.insert(storage.foodItems, item)
end
end
end
macro(500, "Cast Food", function()
if player:getRegenerationTime() <= 400 then
cast("exevo pan", 5000)
end
end)
end

View File

@ -0,0 +1,53 @@
-- Cavebot by otclient@otclient.ovh
-- visit http://bot.otclient.ovh/
local cavebotTab = "Cave"
local targetingTab = storage.extras.joinBot and "Cave" or "Target"
setDefaultTab(cavebotTab)
CaveBot.Extensions = {}
importStyle("/cavebot/cavebot.otui")
importStyle("/cavebot/config.otui")
importStyle("/cavebot/editor.otui")
dofile("/cavebot/actions.lua")
dofile("/cavebot/config.lua")
dofile("/cavebot/editor.lua")
dofile("/cavebot/example_functions.lua")
dofile("/cavebot/recorder.lua")
dofile("/cavebot/walking.lua")
dofile("/cavebot/minimap.lua")
-- in this section you can add extensions, check extension_template.lua
--dofile("/cavebot/extension_template.lua")
dofile("/cavebot/sell_all.lua")
dofile("/cavebot/depositor.lua")
dofile("/cavebot/buy_supplies.lua")
dofile("/cavebot/d_withdraw.lua")
dofile("/cavebot/supply_check.lua")
dofile("/cavebot/travel.lua")
dofile("/cavebot/doors.lua")
dofile("/cavebot/pos_check.lua")
dofile("/cavebot/withdraw.lua")
dofile("/cavebot/inbox_withdraw.lua")
dofile("/cavebot/lure.lua")
dofile("/cavebot/bank.lua")
dofile("/cavebot/clear_tile.lua")
dofile("/cavebot/tasker.lua")
dofile("/cavebot/imbuing.lua")
dofile("/cavebot/stand_lure.lua")
-- main cavebot file, must be last
dofile("/cavebot/cavebot.lua")
setDefaultTab(targetingTab)
if storage.extras.joinBot then UI.Label("-- [[ TargetBot ]] --") end
TargetBot = {} -- global namespace
importStyle("/targetbot/looting.otui")
importStyle("/targetbot/target.otui")
importStyle("/targetbot/creature_editor.otui")
dofile("/targetbot/creature.lua")
dofile("/targetbot/creature_attack.lua")
dofile("/targetbot/creature_editor.lua")
dofile("/targetbot/creature_priority.lua")
dofile("/targetbot/looting.lua")
dofile("/targetbot/walking.lua")
-- main targetbot file, must be last
dofile("/targetbot/target.lua")

View File

@ -0,0 +1,63 @@
setDefaultTab("Cave")
g_ui.loadUIFromString([[
CaveBotControlPanel < Panel
margin-top: 5
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
Label
text-align: center
text: CaveBot Control Panel
font: verdana-11px-rounded
margin-top: 3
HorizontalSeparator
Panel
id: buttons
margin-top: 2
layout:
type: grid
cell-size: 86 20
cell-spacing: 1
flow: true
fit-children: true
HorizontalSeparator
margin-top: 3
]])
local panel = UI.createWidget("CaveBotControlPanel")
storage.caveBot = {
forceRefill = false,
backStop = false,
backTrainers = false,
backOffline = false
}
-- [[ B U T T O N S ]] --
local forceRefill = UI.Button("Force Refill", function(widget)
storage.caveBot.forceRefill = true
print("[CaveBot] Going back on refill on next supply check.")
end, panel.buttons)
local backStop = UI.Button("Back & Stop", function(widget)
storage.caveBot.backStop = true
print("[CaveBot] Going back to city on next supply check and turning off CaveBot on depositer action.")
end, panel.buttons)
local backTrainers = UI.Button("To Trainers", function(widget)
storage.caveBot.backTrainers = true
print("[CaveBot] Going back to city on next supply check and going to label 'toTrainers' on depositer action.")
end, panel.buttons)
local backOffline = UI.Button("Offline", function(widget)
storage.caveBot.backOffline = true
print("[CaveBot] Going back to city on next supply check and going to label 'toOfflineTraining' on depositer action.")
end, panel.buttons)

View File

@ -0,0 +1,443 @@
setDefaultTab("Main")
local panelName = "combobot"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('ComboBot')
Button
id: combos
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {
enabled = false,
onSayEnabled = false,
onShootEnabled = false,
onCastEnabled = false,
followLeaderEnabled = false,
attackLeaderTargetEnabled = false,
attackSpellEnabled = false,
attackItemToggle = false,
sayLeader = "",
shootLeader = "",
castLeader = "",
sayPhrase = "",
spell = "",
serverLeader = "",
item = 3155,
attack = "",
follow = "",
commandsEnabled = true,
serverEnabled = false,
serverLeaderTarget = false,
serverTriggers = true
}
end
local config = storage[panelName]
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
ui.combos.onClick = function(widget)
comboWindow:show()
comboWindow:raise()
comboWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
comboWindow = UI.createWindow('ComboWindow', rootWidget)
comboWindow:hide()
-- bot item
comboWindow.actions.attackItem:setItemId(config.item)
comboWindow.actions.attackItem.onItemChange = function(widget)
config.item = widget:getItemId()
end
-- switches
comboWindow.actions.commandsToggle:setOn(config.commandsEnabled)
comboWindow.actions.commandsToggle.onClick = function(widget)
config.commandsEnabled = not config.commandsEnabled
widget:setOn(config.commandsEnabled)
end
comboWindow.server.botServerToggle:setOn(config.serverEnabled)
comboWindow.server.botServerToggle.onClick = function(widget)
config.serverEnabled = not config.serverEnabled
widget:setOn(config.serverEnabled)
end
comboWindow.server.Triggers:setOn(config.serverTriggers)
comboWindow.server.Triggers.onClick = function(widget)
config.serverTriggers = not config.serverTriggers
widget:setOn(config.serverTriggers)
end
comboWindow.server.targetServerLeaderToggle:setOn(config.serverLeaderTarget)
comboWindow.server.targetServerLeaderToggle.onClick = function(widget)
config.serverLeaderTarget = not config.serverLeaderTarget
widget:setOn(config.serverLeaderTarget)
end
-- buttons
comboWindow.closeButton.onClick = function(widget)
comboWindow:hide()
end
-- combo boxes
comboWindow.actions.followLeader:setOption(config.follow)
comboWindow.actions.followLeader.onOptionChange = function(widget)
config.follow = widget:getCurrentOption().text
end
comboWindow.actions.attackLeaderTarget:setOption(config.attack)
comboWindow.actions.attackLeaderTarget.onOptionChange = function(widget)
config.attack = widget:getCurrentOption().text
end
-- checkboxes
comboWindow.trigger.onSayToggle:setChecked(config.onSayEnabled)
comboWindow.trigger.onSayToggle.onClick = function(widget)
config.onSayEnabled = not config.onSayEnabled
widget:setChecked(config.onSayEnabled)
end
comboWindow.trigger.onShootToggle:setChecked(config.onShootEnabled)
comboWindow.trigger.onShootToggle.onClick = function(widget)
config.onShootEnabled = not config.onShootEnabled
widget:setChecked(config.onShootEnabled)
end
comboWindow.trigger.onCastToggle:setChecked(config.onCastEnabled)
comboWindow.trigger.onCastToggle.onClick = function(widget)
config.onCastEnabled = not config.onCastEnabled
widget:setChecked(config.onCastEnabled)
end
comboWindow.actions.followLeaderToggle:setChecked(config.followLeaderEnabled)
comboWindow.actions.followLeaderToggle.onClick = function(widget)
config.followLeaderEnabled = not config.followLeaderEnabled
widget:setChecked(config.followLeaderEnabled)
end
comboWindow.actions.attackLeaderTargetToggle:setChecked(config.attackLeaderTargetEnabled)
comboWindow.actions.attackLeaderTargetToggle.onClick = function(widget)
config.attackLeaderTargetEnabled = not config.attackLeaderTargetEnabled
widget:setChecked(config.attackLeaderTargetEnabled)
end
comboWindow.actions.attackSpellToggle:setChecked(config.attackSpellEnabled)
comboWindow.actions.attackSpellToggle.onClick = function(widget)
config.attackSpellEnabled = not config.attackSpellEnabled
widget:setChecked(config.attackSpellEnabled)
end
comboWindow.actions.attackItemToggle:setChecked(config.attackItemEnabled)
comboWindow.actions.attackItemToggle.onClick = function(widget)
config.attackItemEnabled = not config.attackItemEnabled
widget:setChecked(config.attackItemEnabled)
end
-- text edits
comboWindow.trigger.onSayLeader:setText(config.sayLeader)
comboWindow.trigger.onSayLeader.onTextChange = function(widget, text)
config.sayLeader = text
end
comboWindow.trigger.onShootLeader:setText(config.shootLeader)
comboWindow.trigger.onShootLeader.onTextChange = function(widget, text)
config.shootLeader = text
end
comboWindow.trigger.onCastLeader:setText(config.castLeader)
comboWindow.trigger.onCastLeader.onTextChange = function(widget, text)
config.castLeader = text
end
comboWindow.trigger.onSayPhrase:setText(config.sayPhrase)
comboWindow.trigger.onSayPhrase.onTextChange = function(widget, text)
config.sayPhrase = text
end
comboWindow.actions.attackSpell:setText(config.spell)
comboWindow.actions.attackSpell.onTextChange = function(widget, text)
config.spell = text
end
comboWindow.server.botServerLeader:setText(config.serverLeader)
comboWindow.server.botServerLeader.onTextChange = function(widget, text)
config.serverLeader = text
end
end
-- bot server
-- [[ join party made by Frosty ]] --
local shouldCloseWindow = false
local firstInvitee = true
local isInComboTeam = false
macro(10, function()
if shouldCloseWindow and config.serverEnabled and config.enabled then
local channelsWindow = modules.game_console.channelsWindow
if channelsWindow then
local child = channelsWindow:getChildById("buttonCancel")
if child then
child:onClick()
shouldCloseWindow = false
isInComboTeam = true
end
end
end
end)
comboWindow.server.partyButton.onClick = function(widget)
if config.serverEnabled and config.enabled then
if config.serverLeader:len() > 0 and storage.BotServerChannel:len() > 0 then
talkPrivate(config.serverLeader, "request invite " .. storage.BotServerChannel)
else
error("Request failed. Lack of data.")
end
end
end
onTextMessage(function(mode, text)
if config.serverEnabled and config.enabled then
if mode == 20 then
if string.find(text, "invited you to") then
local regex = "[a-zA-Z]*"
local regexData = regexMatch(text, regex)
if regexData[1][1]:lower() == config.serverLeader:lower() then
local leader = getCreatureByName(regexData[1][1])
if leader then
g_game.partyJoin(leader:getId())
g_game.requestChannels()
g_game.joinChannel(1)
shouldCloseWindow = true
end
end
end
end
end
end)
onTalk(function(name, level, mode, text, channelId, pos)
if config.serverEnabled and config.enabled then
if mode == 4 then
if string.find(text, "request invite") then
local access = string.match(text, "%d.*")
if access and access == storage.BotServerChannel then
local minion = getCreatureByName(name)
if minion then
g_game.partyInvite(minion:getId())
if firstInvitee then
g_game.requestChannels()
g_game.joinChannel(1)
shouldCloseWindow = true
firstInvitee = false
end
end
else
talkPrivate(name, "Incorrect access key!")
end
end
end
end
-- [[ End of Frosty's Code ]] --
if config.enabled and config.enabled then
if name:lower() == config.sayLeader:lower() and string.find(text, config.sayPhrase) and config.onSayEnabled then
startCombo = true
end
if (config.castLeader and name:lower() == config.castLeader:lower()) and isAttSpell(text) and config.onCastEnabled then
startCombo = true
end
end
if config.enabled and config.commandsEnabled and (config.shootLeader and name:lower() == config.shootLeader:lower()) or (config.sayLeader and name:lower() == config.sayLeader:lower()) or (config.castLeader and name:lower() == config.castLeader:lower()) then
if string.find(text, "ue") then
say(config.spell)
elseif string.find(text, "sd") then
local params = string.split(text, ",")
if #params == 2 then
local target = params[2]:trim()
if getCreatureByName(target) then
useWith(3155, getCreatureByName(target))
end
end
elseif string.find(text, "att") then
local attParams = string.split(text, ",")
if #attParams == 2 then
local atTarget = attParams[2]:trim()
if getCreatureByName(atTarget) and config.attack == "COMMAND TARGET" then
g_game.attack(getCreatureByName(atTarget))
end
end
end
end
if isAttSpell(text) and config.enabled and config.serverEnabled then
BotServer.send("trigger", "start")
end
end)
onMissle(function(missle)
if config.enabled and config.onShootEnabled then
if not config.shootLeader or config.shootLeader:len() == 0 then
return
end
local src = missle:getSource()
if src.z ~= posz() then
return
end
local from = g_map.getTile(src)
local to = g_map.getTile(missle:getDestination())
if not from or not to then
return
end
local fromCreatures = from:getCreatures()
local toCreatures = to:getCreatures()
if #fromCreatures ~= 1 or #toCreatures ~= 1 then
return
end
local c1 = fromCreatures[1]
local t1 = toCreatures[1]
leaderTarget = t1
if c1:getName():lower() == config.shootLeader:lower() then
if config.attackItemEnabled and config.item and config.item > 100 and findItem(config.item) then
useWith(config.item, t1)
end
if config.attackSpellEnabled and config.spell:len() > 1 then
say(config.spell)
end
end
end
end)
macro(10, function()
if not config.enabled or not config.attackLeaderTargetEnabled then return end
if leaderTarget and config.attack == "LEADER TARGET" then
if not getTarget() or (getTarget() and getTarget():getName() ~= leaderTarget:getName()) then
g_game.attack(leaderTarget)
end
end
if config.enabled and config.serverEnabled and config.attack == "SERVER LEADER TARGET" and serverTarget then
if serverTarget and not getTarget() or (getTarget() and getTarget():getname() ~= serverTarget)
then
g_game.attack(serverTarget)
end
end
end)
local toFollow
local toFollowPos = {}
macro(100, function()
toFollow = nil
if not config.enabled or not config.followLeaderEnabled then return end
if leaderTarget and config.follow == "LEADER TARGET" and leaderTarget:isPlayer() then
toFollow = leaderTarget:getName()
elseif config.follow == "SERVER LEADER TARGET" and config.serverLeader:len() ~= 0 then
toFollow = serverTarget
elseif config.follow == "SERVER LEADER" and config.serverLeader:len() ~= 0 then
toFollow = config.serverLeader
elseif config.follow == "LEADER" then
if config.onSayEnabled and config.sayLeader:len() ~= 0 then
toFollow = config.sayLeader
elseif config.onCastEnabled and config.castLeader:len() ~= 0 then
toFollow = config.castLeader
elseif config.onShootEnabled and config.shootLeader:len() ~= 0 then
toFollow = config.shootLeader
end
end
if not toFollow then return end
local target = getCreatureByName(toFollow)
if target then
local tpos = target:getPosition()
toFollowPos[tpos.z] = tpos
end
if player:isWalking() then return end
local p = toFollowPos[posz()]
if not p then return end
if CaveBot.walkTo(p, 20, {ignoreNonPathable=true, precision=1, ignoreStairs=false}) then
delay(100)
end
end)
onCreaturePositionChange(function(creature, oldPos, newPos)
if creature:getName() == toFollow and newPos then
toFollowPos[newPos.z] = newPos
end
end)
local timeout = now
macro(10, function()
if config.enabled and startCombo then
if config.attackItemEnabled and config.item and config.item > 100 and findItem(config.item) then
useWith(config.item, getTarget())
end
if config.attackSpellEnabled and config.spell:len() > 1 then
say(config.spell)
end
startCombo = false
end
-- attack part / server
if BotServer._websocket and config.enabled and config.serverEnabled then
if target() and now - timeout > 500 then
targetPos = target():getName()
BotServer.send("target", targetPos)
timeout = now
end
end
end)
onUseWith(function(pos, itemId, target, subType)
if BotServer._websocket and itemId == 3155 then
BotServer.send("useWith", target:getPosition())
end
end)
if BotServer._websocket and config.enabled and config.serverEnabled then
BotServer.listen("trigger", function(name, message)
if message == "start" and name:lower() ~= player:getName():lower() and name:lower() == config.serverLeader:lower() and config.serverTriggers then
startCombo = true
end
end)
BotServer.listen("target", function(name, message)
if name:lower() ~= player:getName():lower() and name:lower() == config.serverLeader:lower() then
if not target() or target():getName() == getCreatureByName(message) then
if config.serverLeaderTarget then
serverTarget = getCreatureByName(message)
g_game.attack(getCreatureByName(message))
end
end
end
end)
BotServer.listen("useWith", function(name, message)
local tile = g_map.getTile(message)
if config.serverTriggers and name:lower() ~= player:getName():lower() and name:lower() == config.serverLeader:lower() and config.attackItemEnabled and config.item and findItem(config.item) then
useWith(config.item, tile:getTopUseThing())
end
end)
end

View File

@ -0,0 +1,391 @@
AttackComboBoxPopupMenu < ComboBoxPopupMenu
AttackComboBoxPopupMenuButton < ComboBoxPopupMenuButton
AttackComboBox < ComboBox
@onSetup: |
self:addOption("LEADER TARGET")
self:addOption("COMMAND TARGET")
FollowComboBoxPopupMenu < ComboBoxPopupMenu
FollowComboBoxPopupMenuButton < ComboBoxPopupMenuButton
FollowComboBox < ComboBox
@onSetup: |
self:addOption("LEADER TARGET")
self:addOption("SERVER LEADER TARGET")
self:addOption("LEADER")
self:addOption("SERVER LEADER")
ComboTrigger < Panel
id: trigger
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 450 72
Label
id: triggerLabel1
anchors.left: parent.left
anchors.top: parent.top
text: On Say
margin-top: 8
margin-left: 5
color: #ffaa00
Label
id: leaderLabel
anchors.left: triggerLabel1.right
anchors.top: triggerLabel1.top
text: Leader:
margin-left: 35
TextEdit
id: onSayLeader
anchors.left: leaderLabel.right
anchors.top: leaderLabel.top
anchors.bottom: leaderLabel.bottom
margin-left: 5
width: 120
font: cipsoftFont
Label
id: phrase
anchors.left: onSayLeader.right
anchors.top: onSayLeader.top
text: Phrase:
margin-left: 5
TextEdit
id: onSayPhrase
anchors.left: phrase.right
anchors.top: leaderLabel.top
anchors.bottom: leaderLabel.bottom
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onSayToggle
anchors.left: onSayPhrase.right
anchors.top: onSayPhrase.top
margin-top: 1
margin-left: 5
Label
id: triggerLabel2
anchors.left: triggerLabel1.left
anchors.top: triggerLabel1.bottom
text: On Shoot
margin-top: 5
color: #ffaa00
Label
id: leaderLabel1
anchors.left: triggerLabel2.right
anchors.top: triggerLabel2.top
text: Leader:
margin-left: 24
TextEdit
id: onShootLeader
anchors.left: leaderLabel1.right
anchors.top: leaderLabel1.top
anchors.bottom: leaderLabel1.bottom
anchors.right: onSayPhrase.right
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onShootToggle
anchors.left: onShootLeader.right
anchors.top: onShootLeader.top
margin-top: 1
margin-left: 5
Label
id: triggerLabel3
anchors.left: triggerLabel2.left
anchors.top: triggerLabel2.bottom
text: On Cast
margin-top: 5
color: #ffaa00
Label
id: leaderLabel2
anchors.left: triggerLabel3.right
anchors.top: triggerLabel3.top
text: Leader:
margin-left: 32
TextEdit
id: onCastLeader
anchors.left: leaderLabel2.right
anchors.top: leaderLabel2.top
anchors.bottom: leaderLabel2.bottom
anchors.right: onSayPhrase.right
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onCastToggle
anchors.left: onCastLeader.right
anchors.top: onCastLeader.top
margin-top: 1
margin-left: 5
ComboActions < Panel
id: actions
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 220 100
Label
id: label1
anchors.left: parent.left
anchors.top: parent.top
text: Follow:
margin-top: 5
margin-left: 3
height: 15
color: #ffaa00
FollowComboBox
id: followLeader
anchors.left: prev.right
anchors.top: prev.top
margin-left: 7
height: 15
width: 145
font: cipsoftFont
CheckBox
id: followLeaderToggle
anchors.left: followLeader.right
anchors.top: followLeader.top
margin-top: 2
margin-left: 5
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 5
text: Attack:
color: #ffaa00
AttackComboBox
id: attackLeaderTarget
anchors.left: prev.right
anchors.top: prev.top
margin-left: 5
height: 15
width: 145
font: cipsoftFont
CheckBox
id: attackLeaderTargetToggle
anchors.left: attackLeaderTarget.right
anchors.top: attackLeaderTarget.top
margin-top: 2
margin-left: 5
Label
id: label3
anchors.left: label2.left
anchors.top: label2.bottom
margin-top: 5
text: Spell:
color: #ffaa00
TextEdit
id: attackSpell
anchors.left: prev.right
anchors.top: prev.top
anchors.right: attackLeaderTarget.right
margin-left: 17
height: 15
width: 145
font: cipsoftFont
CheckBox
id: attackSpellToggle
anchors.left: attackSpell.right
anchors.top: attackSpell.top
margin-top: 2
margin-left: 5
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 15
text: Attack Item:
color: #ffaa00
BotItem
id: attackItem
anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter
margin-left: 10
CheckBox
id: attackItemToggle
anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
BotSwitch
id: commandsToggle
anchors.left: prev.right
anchors.top: attackItem.top
anchors.right: attackSpellToggle.right
anchors.bottom: attackItem.bottom
margin-left: 5
text: Leader Commands
text-wrap: true
multiline: true
BotServer < Panel
id: server
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 220 100
Label
id: labelX
anchors.left: parent.left
anchors.top: parent.top
text: Leader:
height: 15
color: #ffaa00
margin-left: 3
margin-top: 5
TextEdit
id: botServerLeader
anchors.left: prev.right
anchors.top: prev.top
anchors.right: parent.right
margin-right: 3
margin-left: 9
height: 15
font: cipsoftFont
Button
id: partyButton
anchors.left: labelX.left
anchors.top: botServerLeader.bottom
margin-top: 5
height: 30
text: Join Party
text-wrap: true
multiline: true
BotSwitch
id: botServerToggle
anchors.left: prev.right
anchors.top: botServerLeader.bottom
anchors.right: parent.right
height: 30
margin-left: 3
margin-right: 3
margin-top: 5
text: Server Enabled
BotSwitch
id: targetServerLeaderToggle
anchors.left: partyButton.left
anchors.top: partyButton.bottom
anchors.right: partyButton.right
margin-top: 3
height: 30
text: Leader Targets
BotSwitch
id: Triggers
anchors.left: prev.right
anchors.top: partyButton.bottom
anchors.right: parent.right
margin-top: 3
height: 30
margin-left: 3
margin-right: 3
text: Triggers
ComboWindow < MainWindow
!text: tr('Combo Options')
size: 500 280
@onEscape: self:hide()
ComboTrigger
id: trigger
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 7
Label
id: title
anchors.top: parent.top
anchors.left: parent.left
margin-left: 10
text: Combo Trigger
color: #ff7700
ComboActions
id: actions
anchors.top: trigger.bottom
anchors.left: trigger.left
margin-top: 15
Label
id: title
anchors.top: parent.top
anchors.left: parent.left
margin-left: 10
margin-top: 85
text: Combo Actions
color: #ff7700
BotServer
id: server
anchors.top: actions.top
anchors.left: actions.right
margin-left: 10
Label
id: title
anchors.top: parent.top
anchors.left: server.left
margin-left: 3
margin-top: 85
text: BotServer
color: #ff7700
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5
Button
id: toolsButton
!text: tr('Help')
font: cipsoftFont
anchors.right: closeButton.left
anchors.top: closeButton.top
margin-right: 10
size: 45 21
@onClick: g_platform.openUrl("http://bot.otclient.ovh/books/scripts/page/combobot")

View File

@ -0,0 +1,97 @@
--[[
Configs for modules
Based on Kondrah storage method
--]]
local configName = modules.game_bot.contentsPanel.config:getCurrentOption().text
-- make vBot config dir
if not g_resources.directoryExists("/bot/".. configName .."/vBot_configs/") then
g_resources.makeDir("/bot/".. configName .."/vBot_configs/")
end
-- make profile dirs
for i=1,10 do
local path = "/bot/".. configName .."/vBot_configs/profile_"..i
if not g_resources.directoryExists(path) then
g_resources.makeDir(path)
end
end
local profile = g_settings.getNumber('profile')
HealBotConfig = {}
local healBotFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/HealBot.json"
AttackBotConfig = {}
local attackBotFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/AttackBot.json"
SuppliesConfig = {}
local suppliesFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/Supplies.json"
--healbot
if g_resources.fileExists(healBotFile) then
local status, result = pcall(function()
return json.decode(g_resources.readFileContents(healBotFile))
end)
if not status then
return onError("Error while reading config file (" .. healBotFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result)
end
HealBotConfig = result
end
--attackbot
if g_resources.fileExists(attackBotFile) then
local status, result = pcall(function()
return json.decode(g_resources.readFileContents(attackBotFile))
end)
if not status then
return onError("Error while reading config file (" .. attackBotFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result)
end
AttackBotConfig = result
end
--supplies
if g_resources.fileExists(suppliesFile) then
local status, result = pcall(function()
return json.decode(g_resources.readFileContents(suppliesFile))
end)
if not status then
return onError("Error while reading config file (" .. suppliesFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result)
end
SuppliesConfig = result
end
function vBotConfigSave(file)
-- file can be either
--- heal
--- atk
--- supply
local configFile
local configTable
if not file then return end
file = file:lower()
if file == "heal" then
configFile = healBotFile
configTable = HealBotConfig
elseif file == "atk" then
configFile = attackBotFile
configTable = AttackBotConfig
elseif file == "supply" then
configFile = suppliesFile
configTable = SuppliesConfig
else
return
end
local status, result = pcall(function()
return json.encode(configTable, 2)
end)
if not status then
return onError("Error while saving config. it won't be saved. Details: " .. result)
end
if result:len() > 100 * 1024 * 1024 then
return onError("config file is too big, above 100MB, it won't be saved")
end
g_resources.writeFileContents(configFile, result)
end

View File

@ -0,0 +1,136 @@
setDefaultTab("Cave")
local panelName = "specialDeposit"
local depositerPanel
UI.Button("Stashing Settings", function()
depositerPanel:show()
depositerPanel:raise()
depositerPanel:focus()
end)
if not storage[panelName] then
storage[panelName] = {
items = {},
height = 380
}
end
local config = storage[panelName]
depositerPanel = UI.createWindow('DepositerPanel', rootWidget)
depositerPanel:hide()
-- basic one
depositerPanel.CloseButton.onClick = function()
depositerPanel:hide()
end
depositerPanel:setHeight(config.height or 380)
depositerPanel.onGeometryChange = function(widget, old, new)
if old.height == 0 then return end
config.height = new.height
end
function arabicToRoman(n)
local t = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XI", "XII", "XIV", "XV", "XVI", "XVII"}
return t[n]
end
local function refreshEntries()
depositerPanel.DepositerList:destroyChildren()
for _, entry in ipairs(config.items) do
local panel = g_ui.createWidget("StashItem", depositerPanel.DepositerList)
panel.name:setText(Item.create(entry.id):getMarketData().name)
for i, child in ipairs(panel:getChildren()) do
if child:getId() ~= "slot" then
child:setTooltip("Clear item or double click to remove entry.")
child.onDoubleClick = function(widget)
table.remove(config.items, table.find(entry))
panel:destroy()
end
end
end
panel.item:setItemId(entry.id)
if entry.id > 0 then
panel.item:setImageSource('')
end
panel.item.onItemChange = function(widget)
local id = widget:getItemId()
if id < 100 then
table.remove(config.items, table.find(entry))
panel:destroy()
else
for i, data in ipairs(config.items) do
if data.id == id then
warn("[Depositer Panel] Item already added!")
return
end
end
entry.id = id
panel.item:setImageSource('')
panel.name:setText(Item.create(entry.id):getMarketData().name)
if entry.index == 0 then
local window = modules.client_textedit.show(panel.slot, {
title = "Set depot for "..panel.name:getText(),
description = "Select depot to which item should be stashed, choose between 3 and 17",
validation = [[^([3-9]|1[0-7])$]]
})
window.text:setText(entry.index)
schedule(50, function()
window:raise()
window:focus()
end)
end
end
end
if entry.id > 0 then
panel.slot:setText("Stash to depot: ".. entry.index)
end
panel.slot:setTooltip("Click to set stashing destination.")
panel.slot.onClick = function(widget)
local window = modules.client_textedit.show(widget, {
title = "Set depot for "..panel.name:getText(),
description = "Select depot to which item should be stashed, choose between 3 and 17",
validation = [[^([3-9]|1[0-7])$]]
})
window.text:setText(entry.index)
schedule(50, function()
window:raise()
window:focus()
end)
end
panel.slot.onTextChange = function(widget, text)
local n = tonumber(text)
if n then
entry.index = n
widget:setText("Stash to depot: "..entry.index)
end
end
end
end
refreshEntries()
depositerPanel.title.onDoubleClick = function(widget)
table.insert(config.items, {id=0, index=0})
refreshEntries()
end
function getStashingIndex(id)
for _, v in pairs(config.items) do
if v.id == id then
return v.index - 1
end
end
end
UI.Separator()
UI.Label("Sell Exeptions")
if type(storage.cavebotSell) ~= "table" then
storage.cavebotSell = {23544, 3081}
end
local sellContainer = UI.Container(function(widget, items)
storage.cavebotSell = items
end, true)
sellContainer:setHeight(35)
sellContainer:setItems(storage.cavebotSell)

View File

@ -0,0 +1,98 @@
StashItem < Panel
height: 40
BotItem
id: item
anchors.top: parent.top
margin-top: 2
anchors.left: parent.left
UIWidget
id: name
anchors.top: prev.top
margin-top: 1
anchors.bottom: prev.verticalCenter
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
text-align:left
text: item name
font: verdana-11px-rounded
color: #FFFFFF
UIWidget
id: slot
anchors.top: prev.bottom
margin-top: 3
anchors.bottom: Item.bottom
anchors.left: prev.left
anchors.right: parent.right
font: verdana-11px-rounded
text-align:left
text: Add item to select locker.
color: #CCCCCC
DepositerPanel < MainWindow
size: 230 380
!text: tr('Depositer Panel')
@onEscape: self:hide()
UIWidget
id: title
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: Double click here to add item.
text-align: left
font: verdana-11px-rounded
color: #aeaeae
ScrollablePanel
id: DepositerList
image-source: /images/ui/panel_flat
image-border: 1
anchors.top: prev.bottom
margin-top: 5
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: sep.top
margin-bottom: 10
padding: 2
padding-left: 4
vertical-scrollbar: DepositerScrollBar
layout:
type: verticalBox
VerticalScrollBar
id: DepositerScrollBar
anchors.top: DepositerList.top
anchors.bottom: DepositerList.bottom
anchors.right: DepositerList.right
step: 14
pixels-scroll: true
ResizeBorder
id: bottomResizeBorder
anchors.fill: next
height: 3
minimum: 180
maximum: 800
margin-left: 3
margin-right: 3
background: #ffffff88
HorizontalSeparator
id: sep
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: CloseButton.top
margin-bottom: 8
Button
id: CloseButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-right: 5

View File

@ -0,0 +1,76 @@
-- config
setDefaultTab("Tools")
local defaultBp = "shopping bag"
local id = 21411
-- script
local playerContainer = nil
local depotContainer = nil
local mailContainer = nil
function reopenLootContainer()
for _, container in pairs(getContainers()) do
if container:getName():lower() == defaultBp:lower() then
g_game.close(container)
end
end
local lootItem = findItem(id)
if lootItem then
schedule(500, function() g_game.open(lootItem) end)
end
end
macro(50, "Depot Withdraw", function()
-- set the containers
if not potionsContainer or not runesContainer or not ammoContainer then
for i, container in pairs(getContainers()) do
if container:getName() == defaultBp then
playerContainer = container
elseif string.find(container:getName(), "Depot") then
depotContainer = container
elseif string.find(container:getName(), "your inbox") then
mailContainer = container
end
end
end
if playerContainer and #playerContainer:getItems() == 20 then
for j, item in pairs(playerContainer:getItems()) do
if item:getId() == id then
g_game.open(item, playerContainer)
return
end
end
end
if playerContainer and freecap() >= 200 then
local time = 500
if depotContainer then
for i, container in pairs(getContainers()) do
if string.find(container:getName(), "Depot") then
for j, item in pairs(container:getItems()) do
g_game.move(item, playerContainer:getSlotPosition(playerContainer:getItemsCount()), item:getCount())
return
end
end
end
end
if mailContainer then
for i, container in pairs(getContainers()) do
if string.find(container:getName(), "your inbox") then
for j, item in pairs(container:getItems()) do
g_game.move(item, playerContainer:getSlotPosition(playerContainer:getItemsCount()), item:getCount())
return
end
end
end
end
end
end)

View File

@ -0,0 +1,48 @@
setDefaultTab("HP")
if voc() ~= 1 and voc() ~= 11 then
if storage.foodItems then
local t = {}
for i, v in pairs(storage.foodItems) do
if not table.find(t, v.id) then
table.insert(t, v.id)
end
end
local foodItems = { 3607, 3585, 3592, 3600, 3601 }
for i, item in pairs(foodItems) do
if not table.find(t, item) then
table.insert(storage.foodItems, item)
end
end
end
macro(500, "Cast Food", function()
if player:getRegenerationTime() <= 400 then
cast("exevo pan", 5000)
end
end)
end
UI.Label("Eatable items:")
if type(storage.foodItems) ~= "table" then
storage.foodItems = {3582, 3577}
end
local foodContainer = UI.Container(function(widget, items)
storage.foodItems = items
end, true)
foodContainer:setHeight(35)
foodContainer:setItems(storage.foodItems)
macro(500, "Eat Food", function()
if player:getRegenerationTime() > 400 or not storage.foodItems[1] then return end
-- search for food in containers
for _, container in pairs(g_game.getContainers()) do
for __, item in ipairs(container:getItems()) do
for i, foodItem in ipairs(storage.foodItems) do
if item:getId() == foodItem.id then
return g_game.use(item)
end
end
end
end
end)
UI.Separator()

View File

@ -0,0 +1,36 @@
-- config
setDefaultTab("HP")
local scripts = 2 -- if you want more auto equip panels you can change 2 to higher value
-- script by kondrah, don't edit below unless you know what you are doing
UI.Label("Auto equip")
if type(storage.autoEquip) ~= "table" then
storage.autoEquip = {}
end
for i=1,scripts do
if not storage.autoEquip[i] then
storage.autoEquip[i] = {on=false, title="Auto Equip", item1=i == 1 and 3052 or 0, item2=i == 1 and 3089 or 0, slot=i == 1 and 9 or 0}
end
UI.TwoItemsAndSlotPanel(storage.autoEquip[i], function(widget, newParams)
storage.autoEquip[i] = newParams
end)
end
macro(250, function()
local containers = g_game.getContainers()
for index, autoEquip in ipairs(storage.autoEquip) do
if autoEquip.on then
local slotItem = getSlot(autoEquip.slot)
if not slotItem or (slotItem:getId() ~= autoEquip.item1 and slotItem:getId() ~= autoEquip.item2) then
for _, container in pairs(containers) do
for __, item in ipairs(container:getItems()) do
if item:getId() == autoEquip.item1 or item:getId() == autoEquip.item2 then
g_game.move(item, {x=65535, y=autoEquip.slot, z=0}, item:getCount())
delay(1000) -- don't call it too often
return
end
end
end
end
end
end
end)

View File

@ -0,0 +1,539 @@
SlotBotItem < BotItem
border-width: 0
$on:
image-source: /images/ui/item
$checked:
border-width: 1
border-color: #FF0000
BossLabel < UIWidget
background-color: alpha
text-offset: 3 1
focusable: true
height: 16
font: verdana-11px-rounded
text-align: left
$focus:
background-color: #00000055
Button
id: remove
!text: tr('X')
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 14
height: 14
margin-right: 15
text-align: center
text-offset: 0 1
tooltip: Remove profile from the list.
ConditionBoxPopupMenu < ComboBoxPopupMenu
ConditionBoxPopupMenuButton < ComboBoxPopupMenuButton
ConditionBox < ComboBox
@onSetup: |
self:addOption("-")
self:addOption("and")
self:addOption("or")
PreButton < PreviousButton
background: #363636
height: 15
NexButton < NextButton
background: #363636
height: 15
CondidionLabel < FlatPanel
padding: 1
height: 15
Label
id: text
anchors.fill: parent
text-align: center
font: verdana-11px-rounded
background: #363636
Rule < UIWidget
background-color: alpha
text-offset: 18 2
focusable: true
height: 16
text-align: left
font: verdana-11px-rounded
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
tooltip: Entry enabled/disabled
$focus:
background-color: #00000055
Button
id: remove
text: X
anchors.right: parent.right
margin-right: 15
width: 14
height: 14
text-align: center
tooltip: Remove entry
anchors.verticalCenter: parent.verticalCenter
Button
id: visible
text: V
anchors.right: prev.left
margin-right: 3
width: 14
height: 14
text-align: center
tooltip: Items must be visible
anchors.verticalCenter: parent.verticalCenter
ConditionPanel < Panel
height: 58
NexButton
id: nex
anchors.top: parent.top
margin-top: 5
anchors.right: parent.right
PreButton
id: pre
anchors.top: parent.top
margin-top: 5
anchors.left: parent.left
CondidionLabel
id: description
anchors.top: parent.top
margin-top: 5
anchors.left: prev.right
anchors.right: nex.left
margin-left: 3
margin-right: 3
SpinBox
id: spinbox
anchors.top: description.bottom
margin-top: 10
anchors.horizontalCenter: parent.horizontalCenter
width: 100
text-align: center
minimum: 0
maximum: 100
step: 1
focusable: true
BotTextEdit
id: text
anchors.top: description.bottom
margin-top: 10
anchors.horizontalCenter: parent.horizontalCenter
width: 200
text-align: center
ListPanel < FlatPanel
size: 270 300
padding-left: 10
padding-right: 10
padding-bottom: 10
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
text: Rules List
font: verdana-11px-rounded
color: #FABD02
Label
id: mainLabel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 10
margin-left: 2
!text: tr('More important methods come first.')
text-align: left
font: verdana-11px-rounded
color: #aeaeae
TextList
id: list
anchors.fill: parent
margin-top: 25
margin-bottom: 18
vertical-scrollbar: listScrollBar
padding: 2
VerticalScrollBar
id: listScrollBar
anchors.top: list.top
anchors.bottom: list.bottom
anchors.right: list.right
step: 14
pixels-scroll: true
Button
id: up
anchors.right: parent.right
anchors.top: list.bottom
size: 60 17
text: Move Up
text-align: center
font: cipsoftFont
margin-top: 5
tooltip: Increase priority of selected rule.
Button
id: down
anchors.right: prev.left
anchors.verticalCenter: prev.verticalCenter
size: 60 17
margin-right: 5
text: Move Down
text-align: center
font: cipsoftFont
tooltip: Decrease priority of selected rule.
InputPanel < FlatPanel
size: 270 300
padding-left: 10
padding-right: 10
padding-bottom: 10
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
text: Condition Panel
font: verdana-11px-rounded
color: #FF0000
Label
id: mainLabel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 10
text: Equip selected items when:
text-align: center
font: verdana-11px-rounded
color: #aeaeae
HorizontalSeparator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 4
ConditionPanel
id: condition
anchors.left: parent.left
anchors.right: parent.right
anchors.top: mainLabel.bottom
margin-top: 15
HorizontalSeparator
anchors.verticalCenter: next.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
ConditionBox
id: useSecondCondition
anchors.top: condition.bottom
margin-top: 10
anchors.horizontalCenter: parent.horizontalCenter
width: 50
ConditionPanel
id: optionalCondition
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 10
HorizontalSeparator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
BotButton
id: add
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
margin-bottom: 10
text: Add Rule
EQPanel < FlatPanel
size: 160 230
padding-left: 10
padding-right: 10
padding-bottom: 10
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
text: Equipment Setup
font: verdana-11px-rounded
color: #03C04A
SlotBotItem
id: head
image-source: /images/game/slots/head
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 15
$on:
image-source: /images/ui/item
SlotBotItem
id: body
image-source: /images/game/slots/body
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: legs
image-source: /images/game/slots/legs
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: feet
image-source: /images/game/slots/feet
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: neck
image-source: /images/game/slots/neck
anchors.top: head.top
margin-top: 13
anchors.right: head.left
margin-right: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: left-hand
image-source: /images/game/slots/left-hand
anchors.horizontalCenter: prev.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: finger
image-source: /images/game/slots/finger
anchors.horizontalCenter: prev.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
Item
id: back
image-source: /images/game/slots/back-blessed
anchors.top: head.top
margin-top: 13
anchors.left: head.right
margin-left: 5
tooltip: Main back container modifications are unavailable.
SlotBotItem
id: right-hand
image-source: /images/game/slots/right-hand
anchors.horizontalCenter: prev.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
$on:
image-source: /images/ui/item
SlotBotItem
id: ammo
image-source: /images/game/slots/ammo
anchors.horizontalCenter: prev.horizontalCenter
anchors.top: prev.bottom
margin-top: 5
BotButton
id: cloneEq
anchors.top: feet.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 15
text: Clone Current EQ
font: verdana-11px-rounded
tooltip: Copy currently equipped and non-equipped items.
BotButton
id: default
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
text: Reset fields
font: verdana-11px-rounded
tooltip: Reset all fields to the blank state
Profile < FlatPanel
size: 160 35
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
margin-left: 10
text: Profile Name
font: verdana-11px-rounded
BotTextEdit
id: profileName
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
margin: 5
BossList < FlatPanel
padding-left: 10
padding-right: 10
padding-bottom: 10
Label
id: title
anchors.verticalCenter: parent.top
anchors.left: parent.left
text: Boss List
font: verdana-11px-rounded
color: #FABD02
TextList
id: list
anchors.fill: parent
margin-top: 10
margin-bottom: 20
vertical-scrollbar: listScrollBar
padding: 2
VerticalScrollBar
id: listScrollBar
anchors.top: list.top
anchors.bottom: list.bottom
anchors.right: list.right
step: 14
pixels-scroll: true
BotTextEdit
id: name
anchors.left: list.left
anchors.top: list.bottom
margin-top: 4
anchors.right: next.left
Button
id: add
anchors.right: list.right
anchors.top: list.bottom
margin-top: 3
height: 21
text: Add Boss
text-align: center
font: verdana-11px-rounded
tooltip: Creature with given name will be considered as boss.
EquipWindow < MainWindow
size: 750 350
text: Equipment Manager
@onEscape: self:hide()
ListPanel
id: listPanel
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: bottomSep.top
margin-bottom: 5
margin-left: -2
visible: false
BossList
id: bossPanel
anchors.fill: prev
visible: true
VerticalSeparator
anchors.top: parent.top
anchors.bottom: bottomSep.top
margin-bottom: 5
anchors.left: prev.right
margin-left: 10
Profile
id: profileName
anchors.top: parent.top
anchors.left: prev.right
margin-left: 10
EQPanel
id: setup
anchors.left: prev.left
anchors.top: prev.bottom
anchors.bottom: bottomSep.top
margin-bottom: 5
margin-top: 10
InputPanel
id: inputPanel
anchors.left: prev.right
anchors.top: parent.top
anchors.bottom: bottomSep.top
margin-bottom: 5
margin-left: 5
HorizontalSeparator
id: bottomSep
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
Button
id: bossList
!text: tr('Boss list')
font: cipsoftFont
anchors.left: parent.left
anchors.bottom: parent.bottom
size: 65 21

View File

@ -0,0 +1,27 @@
local voc = player:getVocation()
if voc == 1 or voc == 11 then
setDefaultTab("Cave")
UI.Separator()
local m = macro(100000, "Exeta when low hp", function() end)
local lastCast = now
onCreatureHealthPercentChange(function(creature, healthPercent)
if m.isOff() then return end
if healthPercent > 15 then return end
if CaveBot.isOff() or TargetBot.isOff() then return end
if modules.game_cooldown.isGroupCooldownIconActive(3) then return end
if creature:getPosition() and getDistanceBetween(pos(),creature:getPosition()) > 1 then return end
if canCast("exeta res") and now - lastCast > 6000 then
say("exeta res")
lastCast = now
end
end)
macro(500, "ExetaIfPlayer", function()
if CaveBot.isOff() then return end
if getMonsters(1) >= 1 and getPlayers(6) > 0 then
say("exeta res")
delay(6000)
end
end)
UI.Separator()
end

View File

@ -0,0 +1,644 @@
setDefaultTab("Main")
-- securing storage namespace
local panelName = "extras"
if not storage[panelName] then
storage[panelName] = {}
end
local settings = storage[panelName]
-- basic elements
extrasWindow = UI.createWindow('ExtrasWindow', rootWidget)
extrasWindow:hide()
extrasWindow.closeButton.onClick = function(widget)
extrasWindow:hide()
end
extrasWindow.onGeometryChange = function(widget, old, new)
if old.height == 0 then return end
settings.height = new.height
end
extrasWindow:setHeight(settings.height or 360)
-- available options for dest param
local rightPanel = extrasWindow.content.right
local leftPanel = extrasWindow.content.left
-- objects made by Kondrah - taken from creature editor, minor changes to adapt
local addCheckBox = function(id, title, defaultValue, dest, tooltip)
local widget = UI.createWidget('ExtrasCheckBox', dest)
widget.onClick = function()
widget:setOn(not widget:isOn())
settings[id] = widget:isOn()
if id == "checkPlayer" then
local label = rootWidget.newHealer.targetSettings.vocations.title
if not widget:isOn() then
label:setColor("#d9321f")
label:setTooltip("! WARNING ! \nTurn on check players in extras to use this feature!")
else
label:setColor("#dfdfdf")
label:setTooltip("")
end
end
end
widget:setText(title)
widget:setTooltip(tooltip)
if settings[id] == nil then
widget:setOn(defaultValue)
else
widget:setOn(settings[id])
end
settings[id] = widget:isOn()
end
local addItem = function(id, title, defaultItem, dest, tooltip)
local widget = UI.createWidget('ExtrasItem', dest)
widget.text:setText(title)
widget.text:setTooltip(tooltip)
widget.item:setTooltip(tooltip)
widget.item:setItemId(settings[id] or defaultItem)
widget.item.onItemChange = function(widget)
settings[id] = widget:getItemId()
end
settings[id] = settings[id] or defaultItem
end
local addTextEdit = function(id, title, defaultValue, dest, tooltip)
local widget = UI.createWidget('ExtrasTextEdit', dest)
widget.text:setText(title)
widget.textEdit:setText(settings[id] or defaultValue or "")
widget.text:setTooltip(tooltip)
widget.textEdit.onTextChange = function(widget,text)
settings[id] = text
end
settings[id] = settings[id] or defaultValue or ""
end
local addScrollBar = function(id, title, min, max, defaultValue, dest, tooltip)
local widget = UI.createWidget('ExtrasScrollBar', dest)
widget.text:setTooltip(tooltip)
widget.scroll.onValueChange = function(scroll, value)
widget.text:setText(title .. ": " .. value)
if value == 0 then
value = 1
end
settings[id] = value
end
widget.scroll:setRange(min, max)
widget.scroll:setTooltip(tooltip)
if max-min > 1000 then
widget.scroll:setStep(100)
elseif max-min > 100 then
widget.scroll:setStep(10)
end
widget.scroll:setValue(settings[id] or defaultValue)
widget.scroll.onValueChange(widget.scroll, widget.scroll:getValue())
end
UI.Button("vBot Settings and Scripts", function()
extrasWindow:show()
extrasWindow:raise()
extrasWindow:focus()
end)
UI.Separator()
---- to maintain order, add options right after another:
--- add object
--- add variables for function (optional)
--- add callback (optional)
--- optionals should be addionaly sandboxed (if true then end)
addItem("rope", "Rope Item", 9596, leftPanel, "This item will be used in various bot related scripts as default rope item.")
addItem("shovel", "Shovel Item", 9596, leftPanel, "This item will be used in various bot related scripts as default shovel item.")
addItem("machete", "Machete Item", 9596, leftPanel, "This item will be used in various bot related scripts as default machete item.")
addItem("scythe", "Scythe Item", 9596, leftPanel, "This item will be used in various bot related scripts as default scythe item.")
addCheckBox("pathfinding", "CaveBot Pathfinding", true, leftPanel, "Cavebot will automatically search for first reachable waypoint after missing 10 goto's.")
addScrollBar("talkDelay", "Global NPC Talk Delay", 0, 2000, 1000, leftPanel, "Breaks between each talk action in cavebot (time in miliseconds).")
addScrollBar("looting", "Max Loot Distance", 0, 50, 40, leftPanel, "Every loot corpse futher than set distance (in sqm) will be ignored and forgotten.")
addScrollBar("lootDelay", "Loot Delay", 0, 1000, 200, leftPanel, "Wait time for loot container to open. Lower value means faster looting. \n WARNING if you are having looting issues(e.g. container is locked in closing/opnening), increase this value.")
addScrollBar("huntRoutes", "Hunting Rounds Limit", 0, 300, 50, leftPanel, "Round limit for supply check, if character already made more rounds than set, on next supply check will return to city.")
addScrollBar("killUnder", "Kill monsters below", 0, 100, 1, leftPanel, "Force TargetBot to kill added creatures when they are below set percentage of health - will ignore all other TargetBot settings.")
addScrollBar("gotoMaxDistance", "Max GoTo Distance", 0, 127, 30, leftPanel, "Maximum distance to next goto waypoint for the bot to try to reach.")
addCheckBox("lootLast", "Start loot from last corpse", true, leftPanel, "Looting sequence will be reverted and bot will start looting newest bodies.")
addCheckBox("joinBot", "Join TargetBot and CaveBot", false, leftPanel, "Cave and Target tabs will be joined into one.")
addCheckBox("reachable", "Target only pathable mobs", false, leftPanel, "Ignore monsters that can't be reached.")
addCheckBox("title", "Custom Window Title", true, rightPanel, "Personalize OTCv8 window name according to character specific.")
if true then
local vocText = ""
if voc() == 1 or voc() == 11 then
vocText = "- EK"
elseif voc() == 2 or voc() == 12 then
vocText = "- RP"
elseif voc() == 3 or voc() == 13 then
vocText = "- MS"
elseif voc() == 4 or voc() == 14 then
vocText = "- ED"
end
macro(5000, function()
if settings.title then
if hppercent() > 0 then
g_window.setTitle("Tibia - " .. name() .. " - " .. lvl() .. "lvl " .. vocText)
else
g_window.setTitle("Tibia - " .. name() .. " - DEAD")
end
else
g_window.setTitle("Tibia - " .. name())
end
end)
end
addCheckBox("separatePm", "Open PM's in new Window", false, rightPanel, "PM's will be automatically opened in new tab after receiving one.")
if true then
onTalk(function(name, level, mode, text, channelId, pos)
if mode == 4 and settings.separatePm then
local g_console = modules.game_console
local privateTab = g_console.getTab(name)
if privateTab == nil then
privateTab = g_console.addTab(name, true)
g_console.addPrivateText(g_console.applyMessagePrefixies(name, level, text), g_console.SpeakTypesSettings['private'], name, false, name)
end
return
end
end)
end
addTextEdit("useAll", "Use All Hotkey", "space", rightPanel, "Set hotkey for universal actions - rope, shovel, scythe, use, open doors")
if true then
local useId = { 34847, 1764, 21051, 30823, 6264, 5282, 20453, 20454, 20474, 11708, 11705,
6257, 6256, 2772, 27260, 2773, 1632, 1633, 1948, 435, 6252, 6253, 5007, 4911,
1629, 1630, 5108, 5107, 5281, 1968, 435, 1948, 5542, 31116, 31120, 30742, 31115,
31118, 20474, 5737, 5736, 5734, 5733, 31202, 31228, 31199, 31200, 33262, 30824,
5125, 5126, 5116, 5117, 8257, 8258, 8255, 8256, 5120, 30777, 30776, 23873, 23877,
5736, 6264, 31262, 31130, 31129, 6250, 6249, 5122, 30049, 7131, 7132, 7727 }
local shovelId = { 606, 593, 867, 608 }
local ropeId = { 17238, 12202, 12935, 386, 421, 21966, 14238 }
local macheteId = { 2130, 3696 }
local scytheId = { 3653 }
setDefaultTab("Tools")
-- script
if settings.useAll and settings.useAll:len() > 0 then
hotkey(settings.useAll, function()
if not modules.game_walking.wsadWalking then return end
for _, tile in pairs(g_map.getTiles(posz())) do
if distanceFromPlayer(tile:getPosition()) < 2 then
for _, item in pairs(tile:getItems()) do
-- use
if table.find(useId, item:getId()) then
use(item)
return
elseif table.find(shovelId, item:getId()) then
useWith(settings.shovel, item)
return
elseif table.find(ropeId, item:getId()) then
useWith(settings.rope, item)
return
elseif table.find(macheteId, item:getId()) then
useWith(settings.machete, item)
return
elseif table.find(scytheId, item:getId()) then
useWith(settings.scythe, item)
return
end
end
end
end
end)
end
end
addCheckBox("timers", "MW & WG Timers", true, rightPanel, "Show times for Magic Walls and Wild Growths.")
if true then
local activeTimers = {}
onAddThing(function(tile, thing)
if not settings.timers then return end
if not thing:isItem() then
return
end
local timer = 0
if thing:getId() == 2129 then -- mwall id
timer = 20000 -- mwall time
elseif thing:getId() == 2130 then -- wg id
timer = 45000 -- wg time
else
return
end
local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
if not activeTimers[pos] or activeTimers[pos] < now then
activeTimers[pos] = now + timer
end
tile:setTimer(activeTimers[pos] - now)
end)
onRemoveThing(function(tile, thing)
if not settings.timers then return end
if not thing:isItem() then
return
end
if (thing:getId() == 2129 or thing:getId() == 2130) and tile:getGround() then
local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
activeTimers[pos] = nil
tile:setTimer(0)
end
end)
end
addCheckBox("antiKick", "Anti - Kick", true, rightPanel, "Turn every 10 minutes to prevent kick.")
if true then
macro(600*1000, function()
if not settings.antiKick then return end
local dir = player:getDirection()
turn((dir + 1) % 4)
schedule(50, function() turn(dir) end)
end)
end
addCheckBox("stake", "Skin Monsters", false, leftPanel, "Automatically skin & stake corpses when cavebot is enabled")
if true then
local knifeBodies = {4286, 4272, 4173, 4011, 4025, 4047, 4052, 4057, 4062, 4112, 4212, 4321, 4324, 4327, 10352, 10356, 10360, 10364}
local stakeBodies = {4097, 4137, 8738, 18958}
local fishingBodies = {9582}
macro(500, function()
if not CaveBot.isOn() or not settings.stake then return end
for i, tile in ipairs(g_map.getTiles(posz())) do
local item = tile:getTopThing()
if item and item:isContainer() then
if table.find(knifeBodies, item:getId()) and findItem(5908) then
CaveBot.delay(550)
useWith(5908, item)
return
end
if table.find(stakeBodies, item:getId()) and findItem(5942) then
CaveBot.delay(550)
useWith(5942, item)
return
end
if table.find(fishingBodies, item:getId()) and findItem(3483) then
CaveBot.delay(550)
useWith(3483, item)
return
end
end
end
end)
end
addCheckBox("oberon", "Auto Reply Oberon", true, rightPanel, "Auto reply to Grand Master Oberon talk minigame.")
if true then
onTalk(function(name, level, mode, text, channelId, pos)
if not settings.oberon then return end
if mode == 34 then
if string.find(text, "world will suffer for") then
say("Are you ever going to fight or do you prefer talking?")
elseif string.find(text, "feet when they see me") then
say("Even before they smell your breath?")
elseif string.find(text, "from this plane") then
say("Too bad you barely exist at all!")
elseif string.find(text, "ESDO LO") then
say("SEHWO ASIMO, TOLIDO ESD")
elseif string.find(text, "will soon rule this world") then
say("Excuse me but I still do not get the message!")
elseif string.find(text, "honourable and formidable") then
say("Then why are we fighting alone right now?")
elseif string.find(text, "appear like a worm") then
say("How appropriate, you look like something worms already got the better of!")
elseif string.find(text, "will be the end of mortal") then
say("Then let me show you the concept of mortality before it!")
elseif string.find(text, "virtues of chivalry") then
say("Dare strike up a Minnesang and you will receive your last accolade!")
end
end
end)
end
addCheckBox("autoOpenDoors", "Auto Open Doors", true, rightPanel, "Open doors when trying to step on them.")
if true then
local doorsIds = { 5007, 8265, 1629, 1632, 5129, 6252, 6249, 7715, 7712, 7714,
7719, 6256, 1669, 1672, 5125, 5115, 5124, 17701, 17710, 1642,
6260, 5107, 4912, 6251, 5291, 1683, 1696, 1692, 5006, 2179, 5116,
1632, 11705, 30772, 30774, 6248, 5735, 5732, 5120, 23873, 5736,
6264, 5122, 30049, 30042, 7727 }
function checkForDoors(pos)
local tile = g_map.getTile(pos)
if tile then
local useThing = tile:getTopUseThing()
if useThing and table.find(doorsIds, useThing:getId()) then
g_game.use(useThing)
end
end
end
onKeyPress(function(keys)
local wsadWalking = modules.game_walking.wsadWalking
if not settings.autoOpenDoors then return end
local pos = player:getPosition()
if keys == 'Up' or (wsadWalking and keys == 'W') then
pos.y = pos.y - 1
elseif keys == 'Down' or (wsadWalking and keys == 'S') then
pos.y = pos.y + 1
elseif keys == 'Left' or (wsadWalking and keys == 'A') then
pos.x = pos.x - 1
elseif keys == 'Right' or (wsadWalking and keys == 'D') then
pos.x = pos.x + 1
elseif wsadWalking and keys == "Q" then
pos.y = pos.y - 1
pos.x = pos.x - 1
elseif wsadWalking and keys == "E" then
pos.y = pos.y - 1
pos.x = pos.x + 1
elseif wsadWalking and keys == "Z" then
pos.y = pos.y + 1
pos.x = pos.x - 1
elseif wsadWalking and keys == "C" then
pos.y = pos.y + 1
pos.x = pos.x + 1
end
checkForDoors(pos)
end)
end
addCheckBox("bless", "Buy bless at login", true, rightPanel, "Say !bless at login.")
if true then
local blessed = false
onTextMessage(function(mode,text)
if not settings.bless then return end
text = text:lower()
if text == "you already have all blessings." then
blessed = true
end
end)
if settings.bless then
if player:getBlessings() == 0 then
say("!bless")
schedule(2000, function()
if g_game.getClientVersion() > 1000 then
if not blessed and player:getBlessings() == 0 then
warn("!! Blessings not bought !!")
end
end
end)
end
end
end
addCheckBox("reUse", "Keep Crosshair", false, rightPanel, "Keep crosshair after using with item")
if true then
local excluded = {268, 237, 238, 23373, 266, 236, 239, 7643, 23375, 7642, 23374, 5908, 5942}
onUseWith(function(pos, itemId, target, subType)
if settings.reUse and not table.find(excluded, itemId) then
schedule(50, function()
item = findItem(itemId)
if item then
modules.game_interface.startUseWith(item)
end
end)
end
end)
end
addCheckBox("suppliesControl", "TargetBot off if low supply", false, leftPanel, "Turn off TargetBot if either one of supply amount is below 50% of minimum.")
if true then
macro(500, function()
if not settings.suppliesControl then return end
if TargetBot.isOff() then return end
if CaveBot.isOff() then return end
if type(hasSupplies()) == 'table' then
TargetBot.setOff()
end
end)
end
addCheckBox("holdMwall", "Hold MW/WG", true, rightPanel, "Mark tiles with below hotkeys to automatically use Magic Wall or Wild Growth")
addTextEdit("holdMwHot", "Magic Wall Hotkey: ", "F5", rightPanel)
addTextEdit("holdWgHot", "Wild Growth Hotkey: ", "F6", rightPanel)
if true then
local hold = 0
local mwHot
local wgHot
local candidates = {}
local m = macro(20, function()
mwHot = settings.holdMwHot
wgHot = settings.holdWgHot
if not settings.holdMwall then return end
if #candidates == 0 then return end
for i, pos in pairs(candidates) do
local tile = g_map.getTile(pos)
if tile then
if tile:getText():len() == 0 then
table.remove(candidates, i)
end
local rune = tile:getText() == "HOLD MW" and 3180 or tile:getText() == "HOLD WG" and 3156
if tile:canShoot() and not isInPz() and tile:isWalkable() and tile:getTopUseThing():getId() ~= 2130 then
if math.abs(player:getPosition().x-tile:getPosition().x) < 8 and math.abs(player:getPosition().y-tile:getPosition().y) < 6 then
return useWith(rune, tile:getTopUseThing())
end
end
end
end
end)
onRemoveThing(function(tile, thing)
if not settings.holdMwall then return end
if thing:getId() ~= 2129 then return end
if tile:getText():find("HOLD") then
table.insert(candidates, tile:getPosition())
local rune = tile:getText() == "HOLD MW" and 3180 or tile:getText() == "HOLD WG" and 3156
if math.abs(player:getPosition().x-tile:getPosition().x) < 8 and math.abs(player:getPosition().y-tile:getPosition().y) < 6 then
return useWith(rune, tile:getTopUseThing())
end
end
end)
onAddThing(function(tile, thing)
if not settings.holdMwall then return end
if m.isOff() then return end
if thing:getId() ~= 2129 then return end
if tile:getText():len() > 0 then
table.remove(candidates, table.find(candidates,tile))
end
end)
onKeyDown(function(keys)
local wsadWalking = modules.game_walking.wsadWalking
if not wsadWalking then return end
if not settings.holdMwall then return end
if m.isOff() then return end
if keys ~= mwHot and keys ~= wgHot then return end
hold = now
local tile = getTileUnderCursor()
if not tile then return end
if tile:getText():len() > 0 then
tile:setText("")
else
if keys == mwHot then
tile:setText("HOLD MW")
else
tile:setText("HOLD WG")
end
table.insert(candidates, tile:getPosition())
end
end)
onKeyPress(function(keys)
local wsadWalking = modules.game_walking.wsadWalking
if not wsadWalking then return end
if not settings.holdMwall then return end
if m.isOff() then return end
if keys ~= mwHot and keys ~= wgHot then return end
if (hold - now) < -1000 then
candidates = {}
for i, tile in ipairs(g_map.getTiles(posz())) do
local text = tile:getText()
if text:find("HOLD") then
tile:setText("")
end
end
end
end)
end
addCheckBox("checkPlayer", "Check Players", true, rightPanel, "Auto look on players and mark level and vocation on character model")
if true then
local found
local function checkPlayers()
for i, spec in ipairs(getSpectators()) do
if spec:isPlayer() and spec:getText() == "" and spec:getPosition().z == posz() and spec ~= player then
g_game.look(spec)
found = now
end
end
end
if settings.checkPlayer then
schedule(500, function()
checkPlayers()
end)
end
onPlayerPositionChange(function(x,y)
if not settings.checkPlayer then return end
if x.z ~= y.z then
schedule(20, function() checkPlayers() end)
end
end)
onCreatureAppear(function(creature)
if not settings.checkPlayer then return end
if creature:isPlayer() and creature:getText() == "" and creature:getPosition().z == posz() and creature ~= player then
g_game.look(creature)
found = now
end
end)
local regex = [[You see ([^\(]*) \(Level ([0-9]*)\)((?:.)* of the ([\w ]*),|)]]
onTextMessage(function(mode, text)
if not settings.checkPlayer then return end
local re = regexMatch(text, regex)
if #re ~= 0 then
local name = re[1][2]
local level = re[1][3]
local guild = re[1][5] or ""
if guild:len() > 10 then
guild = guild:sub(1,10) -- change to proper (last) values
guild = guild.."..."
end
local voc
if text:lower():find("sorcerer") then
voc = "MS"
elseif text:lower():find("druid") then
voc = "ED"
elseif text:lower():find("knight") then
voc = "EK"
elseif text:lower():find("paladin") then
voc = "RP"
end
local creature = getCreatureByName(name)
if creature then
creature:setText("\n"..level..voc.."\n"..guild)
end
if found and now - found < 500 then
modules.game_textmessage.clearMessages()
end
end
end)
end
addCheckBox("nextBackpack", "Open Next Loot Container", true, leftPanel, "Auto open next loot container if full - has to have the same ID.")
local function openNextLootContainer()
if not settings.nextBackpack then return end
local containers = getContainers()
local lootCotaniersIds = CaveBot.GetLootContainers()
for i, container in ipairs(containers) do
local cId = container:getContainerItem():getId()
if containerIsFull(container) then
if table.find(lootCotaniersIds, cId) then
for _, item in ipairs(container:getItems()) do
if item:getId() == cId then
return g_game.open(item, container)
end
end
end
end
end
end
if true then
onContainerOpen(function(container, previousContainer)
schedule(100, function()
openNextLootContainer()
end)
end)
onAddItem(function(container, slot, item, oldItem)
schedule(100, function()
openNextLootContainer()
end)
end)
end
addCheckBox("highlightTarget", "Highlight Current Target", true, rightPanel, "Additionaly hightlight current target with red glow")
if true then
local function forceMarked(creature)
if target() == creature then
creature:setMarked("red")
return schedule(333, function() forceMarked(creature) end)
end
end
onAttackingCreatureChange(function(newCreature, oldCreature)
if not settings.highlightTarget then return end
if oldCreature then
oldCreature:setMarked('')
end
if newCreature then
forceMarked(newCreature)
end
end)
end

View File

@ -0,0 +1,158 @@
ExtrasScrollBar < Panel
height: 28
margin-top: 3
UIWidget
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
HorizontalScrollBar
id: scroll
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 10
step: 1
ExtrasTextEdit < Panel
height: 40
margin-top: 7
UIWidget
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
TextEdit
id: textEdit
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
minimum: 0
maximum: 10
step: 1
text-align: center
ExtrasItem < Panel
height: 34
margin-top: 7
margin-left: 25
margin-right: 25
UIWidget
id: text
anchors.left: parent.left
anchors.verticalCenter: next.verticalCenter
BotItem
id: item
anchors.top: parent.top
anchors.right: parent.right
ExtrasCheckBox < BotSwitch
height: 20
margin-top: 7
ExtrasWindow < MainWindow
!text: tr('Extras')
size: 440 360
padding: 25
Label
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: parent.top
text-align: center
text: < CaveBot >
Label
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: parent.top
text-align: center
text: < Miscellaneous >
VerticalScrollBar
id: contentScroll
anchors.top: prev.bottom
margin-top: 3
anchors.right: parent.right
anchors.bottom: separator.top
step: 28
pixels-scroll: true
margin-right: -10
margin-top: 5
margin-bottom: 5
ScrollablePanel
id: content
anchors.top: prev.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: separator.top
vertical-scrollbar: contentScroll
margin-bottom: 10
Panel
id: left
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Panel
id: right
anchors.top: parent.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
VerticalSeparator
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.horizontalCenter
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
ResizeBorder
id: bottomResizeBorder
anchors.fill: separator
height: 3
minimum: 260
maximum: 600
margin-left: 3
margin-right: 3
background: #ffffff88
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-right: 5

View File

@ -0,0 +1,30 @@
setDefaultTab("Tools")
local targetID = nil
-- escape when attacking will reset hold target
onKeyPress(function(keys)
if keys == "Escape" and targetID then
targetID = nil
end
end)
macro(100, "Hold Target", function()
-- if attacking then save it as target, but check pos z in case of marking by mistake on other floor
if target() and target():getPosition().z == posz() and not target():isNpc() then
targetID = target():getId()
elseif not target() then
-- there is no saved data, do nothing
if not targetID then return end
-- look for target
for i, spec in ipairs(getSpectators()) do
local sameFloor = spec:getPosition().z == posz()
local oldTarget = spec:getId() == targetID
if sameFloor and oldTarget then
attack(spec)
end
end
end
end)

View File

@ -0,0 +1,23 @@
setDefaultTab("Tools")
-- allows to test/edit bot lua scripts ingame, you can have multiple scripts like this, just change storage.ingame_lua
UI.Button("Ingame script editor", function(newText)
UI.MultilineEditorWindow(storage.ingame_hotkeys or "", {title="Hotkeys editor", description="You can add your custom scrupts here"}, function(text)
storage.ingame_hotkeys = text
reload()
end)
end)
UI.Separator()
for _, scripts in pairs({storage.ingame_hotkeys}) do
if type(scripts) == "string" and scripts:len() > 3 then
local status, result = pcall(function()
assert(load(scripts, "ingame_editor"))()
end)
if not status then
error("Ingame edior error:\n" .. result)
end
end
end
UI.Separator()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
local version = "4.8"
local currentVersion
local available = false
storage.checkVersion = storage.checkVersion or 0
-- check max once per 12hours
if os.time() > storage.checkVersion + (12 * 60 * 60) then
storage.checkVersion = os.time()
HTTP.get("https://raw.githubusercontent.com/Vithrax/vBot/main/vBot/version.txt", function(data, err)
if err then
warn("[vBot updater]: Unable to check version:\n" .. err)
return
end
currentVersion = data
available = true
end)
end
UI.Label("vBot v".. version .." \n Vithrax#5814")
UI.Button("Official OTCv8 Discord!", function() g_platform.openUrl("https://discord.gg/yhqBE4A") end)
UI.Separator()
schedule(5000, function()
if not available then return end
if currentVersion ~= version then
UI.Separator()
UI.Label("New vBot is available for download! v"..currentVersion)
UI.Button("Go to vBot GitHub Page", function() g_platform.openUrl("https://github.com/Vithrax/vBot") end)
UI.Separator()
end
end)

View File

@ -0,0 +1,518 @@
CaveBot = {} -- global namespace
-------------------------------------------------------------------
-- CaveBot lib 1.0
-- Contains a universal set of functions to be used in CaveBot
----------------------[[ basic assumption ]]-----------------------
-- in general, functions cannot be slowed from within, only externally, by event calls, delays etc.
-- considering that and the fact that there is no while loop, every function return action
-- thus, functions will need to be verified outside themselfs or by another function
-- overall tips to creating extension:
-- - functions return action(nil) or true(done)
-- - extensions are controlled by retries var
-------------------------------------------------------------------
-- local variables, constants and functions, used by global functions
local LOCKERS_LIST = {3497, 3498, 3499, 3500}
local LOCKER_ACCESSTILE_MODIFIERS = {
[3497] = {0,-1},
[3498] = {1,0},
[3499] = {0,1},
[3500] = {-1,0}
}
local function CaveBotConfigParse()
local name = storage["_configs"]["targetbot_configs"]["selected"]
if not name then
return warn("[vBot] Please create a new TargetBot config and reset bot")
end
local file = configDir .. "/targetbot_configs/" .. name .. ".json"
local data = g_resources.readFileContents(file)
return Config.parse(data)['looting']
end
local function getNearTiles(pos)
if type(pos) ~= "table" then
pos = pos:getPosition()
end
local tiles = {}
local dirs = {
{-1, 1},
{0, 1},
{1, 1},
{-1, 0},
{1, 0},
{-1, -1},
{0, -1},
{1, -1}
}
for i = 1, #dirs do
local tile =
g_map.getTile(
{
x = pos.x - dirs[i][1],
y = pos.y - dirs[i][2],
z = pos.z
}
)
if tile then
table.insert(tiles, tile)
end
end
return tiles
end
-- ##################### --
-- [[ Information class ]] --
-- ##################### --
--- global variable to reflect current CaveBot status
CaveBot.Status = "waiting"
--- Parses config and extracts loot list.
-- @return table
function CaveBot.GetLootItems()
local t = CaveBotConfigParse() and CaveBotConfigParse()["items"] or nil
local returnTable = {}
if type(t) == "table" then
for i, item in pairs(t) do
table.insert(returnTable, item["id"])
end
end
return returnTable
end
--- Checks whether player has any visible items to be stashed
-- @return boolean
function CaveBot.HasLootItems()
for _, container in pairs(getContainers()) do
local name = container:getName():lower()
if not name:find("depot") and not name:find("your inbox") then
for _, item in pairs(container:getItems()) do
local id = item:getId()
if table.find(CaveBot.GetLootItems(), id) then
return true
end
end
end
end
end
--- Parses config and extracts loot containers.
-- @return table
function CaveBot.GetLootContainers()
local t = CaveBotConfigParse() and CaveBotConfigParse()["containers"] or nil
local returnTable = {}
if type(t) == "table" then
for i, container in pairs(t) do
table.insert(returnTable, container["id"])
end
end
return returnTable
end
--- Information about open containers.
-- @param amount is boolean
-- @return table or integer
function CaveBot.GetOpenedLootContainers(containerTable)
local containers = CaveBot.GetLootContainers()
local t = {}
for i, container in pairs(getContainers()) do
local containerId = container:getContainerItem():getId()
if table.find(containers, containerId) then
table.insert(t, container)
end
end
return containerTable and t or #t
end
--- Some actions needs to be additionally slowed down in case of high ping.
-- Maximum at 2000ms in case of lag spike.
-- @param multiplayer is integer
-- @return void
function CaveBot.PingDelay(multiplayer)
multiplayer = multiplayer or 1
if ping() and ping() > 150 then -- in most cases ping above 150 affects CaveBot
local value = math.min(ping() * multiplayer, 2000)
return delay(value)
end
end
-- ##################### --
-- [[ Container class ]] --
-- ##################### --
--- Closes any loot container that is open.
-- @return void or boolean
function CaveBot.CloseLootContainer()
local containers = CaveBot.GetLootContainers()
for i, container in pairs(getContainers()) do
local containerId = container:getContainerItem():getId()
if table.find(containers, containerId) then
return g_game.close(container)
end
end
return true
end
function CaveBot.CloseAllLootContainers()
local containers = CaveBot.GetLootContainers()
for i, container in pairs(getContainers()) do
local containerId = container:getContainerItem():getId()
if table.find(containers, containerId) then
g_game.close(container)
end
end
return true
end
--- Opens any loot container that isn't already opened.
-- @return void or boolean
function CaveBot.OpenLootContainer()
local containers = CaveBot.GetLootContainers()
local t = {}
for i, container in pairs(getContainers()) do
local containerId = container:getContainerItem():getId()
table.insert(t, containerId)
end
for _, container in pairs(getContainers()) do
for _, item in pairs(container:getItems()) do
local id = item:getId()
if table.find(containers, id) and not table.find(t, id) then
return g_game.open(item)
end
end
end
return true
end
-- ##################### --
-- [[[ Position class ]] --
-- ##################### --
--- Compares distance between player position and given pos.
-- @param position is table
-- @param distance is integer
-- @return boolean
function CaveBot.MatchPosition(position, distance)
local pPos = player:getPosition()
distance = distance or 1
return getDistanceBetween(pPos, position) <= distance
end
--- Stripped down to take less space.
-- Use only to safe position, like pz movement or reaching npc.
-- Needs to be called between 200-500ms to achieve fluid movement.
-- @param position is table
-- @param distance is integer
-- @return void
function CaveBot.GoTo(position, precision)
if not precision then
precision = 3
end
return CaveBot.walkTo(position, 20, {ignoreCreatures = true, precision = precision})
end
--- Finds position of npc by name and reaches its position.
-- @return void(acion) or boolean
function CaveBot.ReachNPC(name)
name = name:lower()
local npc = nil
for i, spec in pairs(getSpectators()) do
if spec:isNpc() and spec:getName():lower() == name then
npc = spec
end
end
if not CaveBot.MatchPosition(npc:getPosition(), 3) then
CaveBot.GoTo(npc:getPosition())
else
return true
end
end
-- ##################### --
-- [[[[ Depot class ]]]] --
-- ##################### --
--- Reaches closest locker.
-- @return void(acion) or boolean
local depositerLockerTarget = nil
local depositerLockerReachRetries = 0
function CaveBot.ReachDepot()
local pPos = player:getPosition()
local tiles = getNearTiles(player:getPosition())
for i, tile in pairs(tiles) do
for i, item in pairs(tile:getItems()) do
if table.find(LOCKERS_LIST, item:getId()) then
depositerLockerTarget = nil
depositerLockerReachRetries = 0
return true -- if near locker already then return function
end
end
end
if depositerLockerReachRetries > 20 then
depositerLockerTarget = nil
depositerLockerReachRetries = 0
end
local candidates = {}
if not depositerLockerTarget or distanceFromPlayer(depositerLockerTarget, pPos) > 12 then
for i, tile in pairs(g_map.getTiles(posz())) do
local tPos = tile:getPosition()
for i, item in pairs(tile:getItems()) do
if table.find(LOCKERS_LIST, item:getId()) then
local lockerTilePos = tile:getPosition()
lockerTilePos.x = lockerTilePos.x + LOCKER_ACCESSTILE_MODIFIERS[item:getId()][1]
lockerTilePos.y = lockerTilePos.y + LOCKER_ACCESSTILE_MODIFIERS[item:getId()][2]
local lockerTile = g_map.getTile(lockerTilePos)
if not lockerTile:hasCreature() then
if findPath(pos(), tPos, 20, {ignoreNonPathable = false, precision = 1, ignoreCreatures = true}) then
local distance = getDistanceBetween(tPos, pPos)
table.insert(candidates, {pos=tPos, dist=distance})
end
end
end
end
end
if #candidates > 1 then
table.sort(candidates, function(a,b) return a.dist < b.dist end)
end
end
depositerLockerTarget = depositerLockerTarget or candidates[1].pos
if depositerLockerTarget then
if not CaveBot.MatchPosition(depositerLockerTarget) then
depositerLockerReachRetries = depositerLockerReachRetries + 1
return CaveBot.GoTo(depositerLockerTarget, 1)
else
depositerLockerReachRetries = 0
depositerLockerTarget = nil
return true
end
end
end
--- Opens locker item.
-- @return void(acion) or boolean
function CaveBot.OpenLocker()
local pPos = player:getPosition()
local tiles = getNearTiles(player:getPosition())
local locker = getContainerByName("Locker")
if not locker then
for i, tile in pairs(tiles) do
for i, item in pairs(tile:getItems()) do
if table.find(LOCKERS_LIST, item:getId()) then
local topThing = tile:getTopUseThing()
if not topThing:isNotMoveable() then
g_game.move(topThing, pPos, topThing:getCount())
else
return g_game.open(item)
end
end
end
end
else
return true
end
end
--- Opens depot chest.
-- @return void(acion) or boolean
function CaveBot.OpenDepotChest()
local depot = getContainerByName("Depot chest")
if not depot then
local locker = getContainerByName("Locker")
if not locker then
return CaveBot.OpenLocker()
end
for i, item in pairs(locker:getItems()) do
if item:getId() == 3502 then
return g_game.open(item, locker)
end
end
else
return true
end
end
--- Opens inbox inside locker.
-- @return void(acion) or boolean
function CaveBot.OpenInbox()
local inbox = getContainerByName("Your inbox")
if not inbox then
local locker = getContainerByName("Locker")
if not locker then
return CaveBot.OpenLocker()
end
for i, item in pairs(locker:getItems()) do
if item:getId() == 12902 then
return g_game.open(item)
end
end
else
return true
end
end
--- Opens depot box of given number.
-- @param index is integer
-- @return void or boolean
function CaveBot.OpenDepotBox(index)
local depot = getContainerByName("Depot chest")
if not depot then
return CaveBot.ReachAndOpenDepot()
end
local foundParent = false
for i, container in pairs(getContainers()) do
if container:getName():lower():find("depot box") then
foundParent = container
break
end
end
if foundParent then return true end
for i, container in pairs(depot:getItems()) do
if i == index then
return g_game.open(container)
end
end
end
--- Reaches and opens depot.
-- Combined for shorthand usage.
-- @return boolean whether succeed to reach and open depot
function CaveBot.ReachAndOpenDepot()
if CaveBot.ReachDepot() and CaveBot.OpenDepotChest() then
return true
end
return false
end
--- Reaches and opens imbox.
-- Combined for shorthand usage.
-- @return boolean whether succeed to reach and open depot
function CaveBot.ReachAndOpenInbox()
if CaveBot.ReachDepot() and CaveBot.OpenInbox() then
return true
end
return false
end
--- Stripped down function to stash item.
-- @param item is object
-- @param index is integer
-- @param destination is object
-- @return void
function CaveBot.StashItem(item, index, destination)
destination = destination or getContainerByName("Depot chest")
if not destination then return false end
return g_game.move(item, destination:getSlotPosition(index), item:getCount())
end
--- Withdraws item from depot chest or mail inbox.
-- main function for depositer/withdrawer
-- @param id is integer
-- @param amount is integer
-- @param fromDepot is boolean or integer
-- @param destination is object
-- @return void
function CaveBot.WithdrawItem(id, amount, fromDepot, destination)
if destination and type(destination) == "string" then
destination = getContainerByName(destination)
end
local itemCount = itemAmount(id)
local depot
for i, container in pairs(getContainers()) do
if container:getName():lower():find("depot box") or container:getName():lower():find("your inbox") then
depot = container
break
end
end
if not depot then
if fromDepot then
if not CaveBot.OpenDepotBox(fromDepot) then return end
else
return CaveBot.ReachAndOpenInbox()
end
return
end
if not destination then
for i, container in pairs(getContainers()) do
if container:getCapacity() > #container:getItems() and not string.find(container:getName():lower(), "quiver") and not string.find(container:getName():lower(), "depot") and not string.find(container:getName():lower(), "loot") and not string.find(container:getName():lower(), "inbox") then
destination = container
end
end
end
if itemCount >= amount then
return true
end
local toMove = amount - itemCount
for i, item in pairs(depot:getItems()) do
if item:getId() == id then
return g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), math.min(toMove, item:getCount()))
end
end
end
-- ##################### --
-- [[[[[ Talk class ]]]] --
-- ##################### --
--- Controlled by event caller.
-- Simple way to build npc conversations instead of multiline overcopied code.
-- @return void
function CaveBot.Conversation(...)
local expressions = {...}
local delay = storage.extras.talkDelay or 1000
local talkDelay = 0
for i, expr in ipairs(expressions) do
schedule(talkDelay, function() NPC.say(expr) end)
talkDelay = talkDelay + delay
end
end
--- Says hi trade to NPC.
-- Used as shorthand to open NPC trade window.
-- @return void
function CaveBot.OpenNpcTrade()
return CaveBot.Conversation("hi", "trade")
end
--- Says hi destination yes to NPC.
-- Used as shorthand to travel.
-- @param destination is string
-- @return void
function CaveBot.Travel(destination)
return CaveBot.Conversation("hi", destination, "yes")
end

View File

@ -0,0 +1,456 @@
setDefaultTab("Main")
local panelName = "newHealer"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Friend Healer')
Button
id: edit
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(panelName)
-- validate current settings
if not storage[panelName] or not storage[panelName].priorities then
storage[panelName] = nil
end
if not storage[panelName] then
storage[panelName] = {
enabled = false,
customPlayers = {},
vocations = {},
groups = {},
priorities = {
{name="Custom Spell", enabled=false, custom=true},
{name="Exura Gran Sio", enabled=true, strong = true},
{name="Exura Sio", enabled=true, normal = true},
{name="Exura Gran Mas Res", enabled=true, area = true},
{name="Health Item", enabled=true, health=true},
{name="Mana Item", enabled=true, mana=true}
},
settings = {
{type="HealItem", text="Mana Item ", value=268},
{type="HealScroll", text="Item Range: ", value=6},
{type="HealItem", text="Health Item ", value=3160},
{type="HealScroll", text="Mas Res Players: ", value=2},
{type="HealScroll", text="Heal Friend at: ", value=80},
{type="HealScroll", text="Use Gran Sio at: ", value=80},
{type="HealScroll", text="Min Player HP%: ", value=80},
{type="HealScroll", text="Min Player MP%: ", value=50},
},
conditions = {
knights = true,
paladins = true,
druids = false,
sorcerers = false,
party = true,
guild = false,
botserver = false,
friends = false
}
}
end
local config = storage[panelName]
local healerWindow = UI.createWindow('FriendHealer')
healerWindow:hide()
healerWindow:setId(panelName)
ui.title:setOn(config.enabled)
ui.title.onClick = function(widget)
config.enabled = not config.enabled
widget:setOn(config.enabled)
end
ui.edit.onClick = function()
healerWindow:show()
healerWindow:raise()
healerWindow:focus()
end
local conditions = healerWindow.conditions
local targetSettings = healerWindow.targetSettings
local customList = healerWindow.customList
local priority = healerWindow.priority
-- customList
-- create entries on the list
for name, health in pairs(config.customPlayers) do
local widget = UI.createWidget("HealerPlayerEntry", customList.playerList.list)
widget.remove.onClick = function()
config.customPlayers[name] = nil
widget:destroy()
end
widget:setText("["..health.."%] "..name)
end
customList.playerList.onDoubleClick = function()
customList.playerList:hide()
end
local function clearFields()
customList.addPanel.name:setText("friend name")
customList.addPanel.health:setText("1")
customList.playerList:show()
end
local function capitalFistLetter(str)
return (string.gsub(str, "^%l", string.upper))
end
customList.addPanel.add.onClick = function()
local name = ""
local words = string.split(customList.addPanel.name:getText(), " ")
local health = tonumber(customList.addPanel.health:getText())
for i, word in ipairs(words) do
name = name .. " " .. capitalFistLetter(word)
end
if not health then
clearFields()
return warn("[Friend Healer] Please enter health percent value!")
end
if name:len() == 0 or name:lower() == "friend name" then
clearFields()
return warn("[Friend Healer] Please enter friend name to be added!")
end
if config.customPlayers[name] or config.customPlayers[name:lower()] then
clearFields()
return warn("[Friend Healer] Player already added to custom list.")
else
config.customPlayers[name] = health
local widget = UI.createWidget("HealerPlayerEntry", customList.playerList.list)
widget.remove.onClick = function()
config.customPlayers[name] = nil
widget:destroy()
end
widget:setText("["..health.."%] "..name)
end
clearFields()
end
local function validate(widget, category)
local list = widget:getParent()
local label = list:getParent().title
-- 1 - priorities | 2 - vocation
category = category or 0
if category == 2 and not storage.extras.checkPlayer then
label:setColor("#d9321f")
label:setTooltip("! WARNING ! \nTurn on check players in extras to use this feature!")
return
else
label:setColor("#dfdfdf")
label:setTooltip("")
end
local checked = false
for i, child in ipairs(list:getChildren()) do
if category == 1 and child.enabled:isChecked() or child:isChecked() then
checked = true
end
end
if not checked then
label:setColor("#d9321f")
label:setTooltip("! WARNING ! \nNo category selected!")
else
label:setColor("#dfdfdf")
label:setTooltip("")
end
end
-- targetSettings
targetSettings.vocations.box.knights:setChecked(config.conditions.knights)
targetSettings.vocations.box.knights.onClick = function(widget)
config.conditions.knights = not config.conditions.knights
widget:setChecked(config.conditions.knights)
validate(widget, 2)
end
targetSettings.vocations.box.paladins:setChecked(config.conditions.paladins)
targetSettings.vocations.box.paladins.onClick = function(widget)
config.conditions.paladins = not config.conditions.paladins
widget:setChecked(config.conditions.paladins)
validate(widget, 2)
end
targetSettings.vocations.box.druids:setChecked(config.conditions.druids)
targetSettings.vocations.box.druids.onClick = function(widget)
config.conditions.druids = not config.conditions.druids
widget:setChecked(config.conditions.druids)
validate(widget, 2)
end
targetSettings.vocations.box.sorcerers:setChecked(config.conditions.sorcerers)
targetSettings.vocations.box.sorcerers.onClick = function(widget)
config.conditions.sorcerers = not config.conditions.sorcerers
widget:setChecked(config.conditions.sorcerers)
validate(widget, 2)
end
targetSettings.groups.box.friends:setChecked(config.conditions.friends)
targetSettings.groups.box.friends.onClick = function(widget)
config.conditions.friends = not config.conditions.friends
widget:setChecked(config.conditions.friends)
validate(widget)
end
targetSettings.groups.box.party:setChecked(config.conditions.party)
targetSettings.groups.box.party.onClick = function(widget)
config.conditions.party = not config.conditions.party
widget:setChecked(config.conditions.party)
validate(widget)
end
targetSettings.groups.box.guild:setChecked(config.conditions.guild)
targetSettings.groups.box.guild.onClick = function(widget)
config.conditions.guild = not config.conditions.guild
widget:setChecked(config.conditions.guild)
validate(widget)
end
targetSettings.groups.box.botserver:setChecked(config.conditions.botserver)
targetSettings.groups.box.botserver.onClick = function(widget)
config.conditions.botserver = not config.conditions.botserver
widget:setChecked(config.conditions.botserver)
validate(widget)
end
validate(targetSettings.vocations.box.knights)
validate(targetSettings.groups.box.friends)
validate(targetSettings.vocations.box.sorcerers, 2)
-- conditions
for i, setting in ipairs(config.settings) do
local widget = UI.createWidget(setting.type, conditions.box)
local text = setting.text
local val = setting.value
widget.text:setText(text)
if setting.type == "HealScroll" then
widget.text:setText(widget.text:getText()..val)
if not (text:find("Range") or text:find("Mas Res")) then
widget.text:setText(widget.text:getText().."%")
end
widget.scroll:setValue(val)
widget.scroll.onValueChange = function(scroll, value)
setting.value = value
widget.text:setText(text..value)
if not (text:find("Range") or text:find("Mas Res")) then
widget.text:setText(widget.text:getText().."%")
end
end
if text:find("Range") or text:find("Mas Res") then
widget.scroll:setMaximum(10)
end
else
widget.item:setItemId(val)
widget.item:setShowCount(false)
widget.item.onItemChange = function(widget)
setting.value = widget:getItemId()
end
end
end
-- priority and toggles
local function setCrementalButtons()
for i, child in ipairs(priority.list:getChildren()) do
if i == 1 then
child.increment:disable()
elseif i == 6 then
child.decrement:disable()
else
child.increment:enable()
child.decrement:enable()
end
end
end
for i, action in ipairs(config.priorities) do
local widget = UI.createWidget("PriorityEntry", priority.list)
widget:setText(action.name)
widget.increment.onClick = function()
local index = priority.list:getChildIndex(widget)
local table = config.priorities
priority.list:moveChildToIndex(widget, index-1)
table[index], table[index-1] = table[index-1], table[index]
setCrementalButtons()
end
widget.decrement.onClick = function()
local index = priority.list:getChildIndex(widget)
local table = config.priorities
priority.list:moveChildToIndex(widget, index+1)
table[index], table[index+1] = table[index+1], table[index]
setCrementalButtons()
end
widget.enabled:setChecked(action.enabled)
widget:setColor(action.enabled and "#98BF64" or "#dfdfdf")
widget.enabled.onClick = function()
action.enabled = not action.enabled
widget:setColor(action.enabled and "#98BF64" or "#dfdfdf")
widget.enabled:setChecked(action.enabled)
validate(widget, 1)
end
if action.custom then
widget.onDoubleClick = function()
local window = modules.client_textedit.show(widget, {title = "Custom Spell", description = "Enter below formula for a custom healing spell"})
schedule(50, function()
window:raise()
window:focus()
end)
end
widget.onTextChange = function(widget,text)
action.name = text
end
widget:setTooltip("Double click to set spell formula.")
end
if i == #config.priorities then
validate(widget, 1)
setCrementalButtons()
end
end
local lastItemUse = now
local function friendHealerAction(spec, targetsInRange)
local name = spec:getName()
local health = spec:getHealthPercent()
local mana = spec:getManaPercent()
local dist = distanceFromPlayer(spec:getPosition())
targetsInRange = targetsInRange or 0
local masResAmount = config.settings[4].value
local itemRange = config.settings[2].value
local healItem = config.settings[3].value
local manaItem = config.settings[1].value
local normalHeal = config.customPlayers[name] or config.settings[5].value
local strongHeal = config.customPlayers[name] and normalHeal/2 or config.settings[6].value
for i, action in ipairs(config.priorities) do
if action.enabled then
if action.area and masResAmount <= targetsInRange and canCast("exura gran mas res") then
return say("exura gran mas res")
end
if action.mana and findItem(manaItem) and mana <= normalHeal and dist <= itemRange and now - lastItemUse > 1000 then
lastItemUse = now
return useWith(manaItem, spec)
end
if action.health and findItem(healItem) and health <= normalHeal and dist <= itemRange and now - lastItemUse > 1000 then
lastItemUse = now
return useWith(healItem, spec)
end
if action.strong and health <= strongHeal and not modules.game_cooldown.isCooldownIconActive(101) then
return say('exura gran sio "'..name)
end
if (action.normal or action.custom) and health <= normalHeal and canCast('exura sio "'..name) then
return say('exura sio "'..name)
end
end
end
end
local function isCandidate(spec)
if spec:isLocalPlayer() or not spec:isPlayer() then
return nil
end
if not spec:canShoot() then
return false
end
local curHp = spec:getHealthPercent()
if curHp == 100 or (config.customPlayers[name] and curHp > config.customPlayers[name]) then
return false
end
local specText = spec:getText()
local name = spec:getName()
-- check players is enabled and spectator already verified
if storage.extras.checkPlayer and specText:len() > 0 then
if specText:find("EK") and not config.conditions.knights or
specText:find("RP") and not config.conditions.paladins or
specText:find("ED") and not config.conditions.druids or
specText:find("MS") and not config.conditions.sorcerers then
if not config.customPlayers[name] then
return nil
end
end
end
local okParty = config.conditions.party and spec:isPartyMember()
local okFriend = config.conditions.friends and isFriend(spec)
local okGuild = config.conditions.guild and spec:getEmblem() == 1
local okBotServer = config.conditions.botserver and vBot.BotServerMembers[spec:getName()]
if not (okParty or okFriend or okGuild or okBotServer) then
return nil
end
local health = config.customPlayers[name] and curHp/2 or curHp
local dist = distanceFromPlayer(spec:getPosition())
return health, dist
end
macro(100, function()
if not config.enabled then return end
if modules.game_cooldown.isGroupCooldownIconActive(2) then return end
local minHp = config.settings[7].value
local minMp = config.settings[8].value
local healTarget = {creature=nil, hp=100}
local inMasResRange = 0
-- check basic
if hppercent() <= minHp or manapercent() <= minMp then return end
-- get all spectators
local spectators = getSpectators()
-- main check
local healtR
for i, spec in ipairs(spectators) do
local health, dist = isCandidate(spec)
--mas san
if dist then
inMasResRange = dist <= 3 and inMasResRange+1 or inMasResRange
-- best target
if health < healTarget.hp then
healTarget = {creature = spec, hp = health}
end
end
end
-- action
if healTarget.creature then
return friendHealerAction(healTarget.creature, inMasResRange)
end
end)

View File

@ -0,0 +1,413 @@
CategoryCheckBox < CheckBox
font: verdana-11px-rounded
margin-top: 3
$checked:
color: #98BF64
HealScroll < Panel
ToolTipLabel
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
text: test
HorizontalScrollBar
id: scroll
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 100
step: 1
HealItem < Panel
BotItem
id: item
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
size: 34 34
ToolTipLabel
id: text
anchors.fill: parent
anchors.left: prev.right
margin-left: 8
text-wrap: true
text-align: left
ToolTipLabel < UIWidget
font: verdana-11px-rounded
color: #dfdfdf
height: 14
text-align: center
HealerPlayerEntry < Label
background-color: alpha
text-offset: 5 1
focusable: true
height: 16
font: verdana-11px-rounded
text-align: left
$focus:
background-color: #00000055
Button
id: remove
anchors.right: parent.right
margin-right: 2
anchors.verticalCenter: parent.verticalCenter
size: 15 15
margin-right: 15
text: X
tooltip: Remove player from the list
PriorityEntry < ToolTipLabel
background-color: alpha
text-offset: 18 1
focusable: true
height: 16
font: verdana-11px-rounded
text-align: left
$focus:
background-color: #00000055
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
size: 15 15
margin-top: 2
margin-left: 3
Button
id: increment
anchors.right: parent.right
margin-right: 2
anchors.verticalCenter: parent.verticalCenter
size: 14 14
text: +
tooltip: Increase Priority
Button
id: decrement
anchors.right: prev.left
margin-right: 2
anchors.verticalCenter: parent.verticalCenter
size: 14 14
text: -
tooltip: Decrease Priority
TargetSettings < Panel
size: 280 125
padding: 3
image-source: /images/ui/window
image-border: 6
Label
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
font: verdana-11px-rounded
text: Heal Target Settings
Groups
id: groups
anchors.top: prev.bottom
margin-top: 8
anchors.left: parent.left
margin-left: 9
Vocations
id: vocations
anchors.left: prev.right
margin-left: 5
anchors.verticalCenter: prev.verticalCenter
Groups < FlatPanel
size: 150 90
padding: 3
padding-top: 5
ToolTipLabel
id: title
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
text: Groups
tooltip: Players added in custom list will always be in scope
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 2
anchors.left: parent.left
anchors.right: parent.right
Panel
id: box
anchors.top: prev.bottom
margin-top: 2
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
padding: 2
layout:
type: verticalBox
CategoryCheckBox
id: friends
text: Friends
CategoryCheckBox
id: party
text: Party Members
CategoryCheckBox
id: guild
text: Guild Members
CategoryCheckBox
id: botserver
text: BotServer Members
Vocations < FlatPanel
size: 100 90
padding: 3
padding-top: 5
ToolTipLabel
id: title
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
font: verdana-11px-rounded
text: Vocations
HorizontalSeparator
anchors.top: prev.bottom
margin-top: 2
anchors.left: parent.left
anchors.right: parent.right
Panel
id: box
anchors.top: prev.bottom
margin-top: 2
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
padding: 2
layout:
type: verticalBox
CategoryCheckBox
id: knights
text: Knights
CategoryCheckBox
id: paladins
text: Paladins
CategoryCheckBox
id: druids
text: Druids
CategoryCheckBox
id: sorcerers
text: Sorcerers
Priority < Panel
size: 190 123
padding: 6
padding-top: 3
image-source: /images/ui/window
image-border: 6
ToolTipLabel
id: title
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
font: verdana-11px-rounded
text: Priority & Toggles
TextList
id: list
anchors.top: prev.bottom
margin-top: 3
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
fit-children: true
padding-top: 1
AddPlayer < FlatPanel
padding: 5
Label
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
font: verdana-11px-rounded
text: Add Player to Custom List
text-align: center
text-wrap: true
HorizontalSeparator
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
SpinBox
id: health
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 20
width: 50
minimum: 1
maximum: 99
step: 1
focusable: true
text-align: center
Label
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
font: verdana-11px-rounded
text: %HP - heal if below
TextEdit
id: name
anchors.top: health.bottom
margin-top: 5
anchors.left: health.left
anchors.right: parent.right
font: verdana-11px-rounded
text-align: center
text: friend name
Button
id: add
anchors.left: health.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
font: verdana-11px-rounded
text: Add Player
PlayerList < Panel
TextList
id: list
anchors.fill: parent
fit-children: true
padding-top: 2
vertical-scrollbar: listScrollBar
VerticalScrollBar
id: listScrollBar
anchors.top: list.top
anchors.bottom: list.bottom
anchors.right: list.right
step: 14
pixels-scroll: true
CustomList < Panel
size: 190 172
padding: 6
padding-top: 3
image-source: /images/ui/window
image-border: 6
ToolTipLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
font: verdana-11px-rounded
text: Custom Player List
tooltip: Double click on the list below to add new player.
AddPlayer
id: addPanel
anchors.top: prev.bottom
margin-top: 3
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
PlayerList
id: playerList
anchors.fill: prev
Conditions < Panel
size: 280 170
padding: 3
image-source: /images/ui/window
image-border: 6
Label
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
font: verdana-11px-rounded
text: Player Conditions
Panel
id: box
anchors.fill: parent
margin-top: 16
padding: 5
padding-top: 3
layout:
type: grid
cell-size: 128 31
cell-spacing: 5
num-columns: 2
FriendHealer < MainWindow
!text: tr('Friend Healer')
size: 512 390
padding-top: 30
@onEscape: self:hide()
Conditions
id: conditions
anchors.top: parent.top
anchors.right: parent.right
TargetSettings
id: targetSettings
anchors.top: prev.bottom
margin-top: 10
anchors.left: prev.left
Priority
id: priority
anchors.top: parent.top
anchors.left: parent.left
CustomList
id: customList
anchors.top: priority.bottom
margin-top: 10
anchors.left: priority.left
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
@onClick: self:getParent():hide()

View File

@ -0,0 +1,5 @@
onAttackingCreatureChange(function(creature, OldCreature)
if creature and creature:isNpc() and distanceFromPlayer(creature:getPosition()) <= 3 then
CaveBot.Conversation("hi", "trade")
end
end)

View File

@ -0,0 +1,351 @@
--[[
configuration for check players
example made on server Gunzodus
example link for player overview:
https://www.gunzodus.net/character/show/Sir_Vithrax
*note that space in character name was replaced with underscore (_) - this character will be important
in this case:
link = "https://www.gunzodus.net/character/show/" -- everything with all the characters up to the start of the name
spacing = "_" -- space replacement in character name
]]
local link = "https://www.gunzodus.net/character/show/"
local spacing = "_"
-- do not edit below
setDefaultTab("Main")
local tabs = {"Friends", "Enemies", "BlackList"}
local panelName = "playerList"
local colors = {"#03C04A", "#fc4c4e", "orange"}
if not storage[panelName] then
storage[panelName] = {
enemyList = {},
friendList = {},
blackList = {},
groupMembers = true,
outfits = false,
marks = false,
highlight = false
}
end
local config = storage[panelName]
local playerTables = {config.friendList, config.enemyList, config.blackList}
-- functions
local function clearCachedPlayers()
CachedFriends = {}
CachedEnemies = {}
end
local refreshStatus = function()
for _, spec in ipairs(getSpectators()) do
if spec:isPlayer() and not spec:isLocalPlayer() then
if config.outfits then
local specOutfit = spec:getOutfit()
if isFriend(spec:getName()) then
if config.highlight then
spec:setMarked('#0000FF')
end
specOutfit.head = 88
specOutfit.body = 88
specOutfit.legs = 88
specOutfit.feet = 88
if storage.BOTserver.outfit then
local voc = vBot.BotServerMembers[spec:getName()]
specOutfit.addons = 3
if voc == 1 then
specOutfit.type = 131
elseif voc == 2 then
specOutfit.type = 129
elseif voc == 3 then
specOutfit.type = 130
elseif voc == 4 then
specOutfit.type = 144
end
end
spec:setOutfit(specOutfit)
elseif isEnemy(spec:getName()) then
if config.highlight then
spec:setMarked('#FF0000')
end
specOutfit.head = 94
specOutfit.body = 94
specOutfit.legs = 94
specOutfit.feet = 94
spec:setOutfit(specOutfit)
end
end
end
end
end
refreshStatus()
local checkStatus = function(creature)
if not creature:isPlayer() or creature:isLocalPlayer() then return end
local specName = creature:getName()
local specOutfit = creature:getOutfit()
if isFriend(specName) then
if config.highlight then
creature:setMarked('#0000FF')
end
if config.outfits then
specOutfit.head = 88
specOutfit.body = 88
specOutfit.legs = 88
specOutfit.feet = 88
if storage.BOTserver.outfit then
local voc = vBot.BotServerMembers[creature:getName()]
specOutfit.addons = 3
if voc == 1 then
specOutfit.type = 131
elseif voc == 2 then
specOutfit.type = 129
elseif voc == 3 then
specOutfit.type = 130
elseif voc == 4 then
specOutfit.type = 144
end
end
creature:setOutfit(specOutfit)
end
elseif isEnemy(specName) then
if config.highlight then
creature:setMarked('#FF0000')
end
if config.outfits then
specOutfit.head = 94
specOutfit.body = 94
specOutfit.legs = 94
specOutfit.feet = 94
creature:setOutfit(specOutfit)
end
end
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
local ListWindow = UI.createWindow('PlayerListWindow', rootWidget)
ListWindow:hide()
UI.Button("Player Lists", function()
ListWindow:show()
ListWindow:raise()
ListWindow:focus()
end)
-- settings
ListWindow.settings.Members:setChecked(config.groupMembers)
ListWindow.settings.Members.onClick = function(widget)
config.groupMembers = not config.groupMembers
if not config.groupMembers then
clearCachedPlayers()
end
refreshStatus()
widget:setChecked(config.groupMembers)
end
ListWindow.settings.Outfit:setChecked(config.outfits)
ListWindow.settings.Outfit.onClick = function(widget)
config.outfits = not config.outfits
widget:setChecked(config.outfits)
refreshStatus()
end
ListWindow.settings.NeutralsAreEnemy:setChecked(config.marks)
ListWindow.settings.NeutralsAreEnemy.onClick = function(widget)
config.marks = not config.marks
widget:setChecked(config.marks)
end
ListWindow.settings.Highlight:setChecked(config.highlight)
ListWindow.settings.Highlight.onClick = function(widget)
config.highlight = not config.highlight
widget:setChecked(config.highlight)
end
ListWindow.settings.AutoAdd:setChecked(config.autoAdd)
ListWindow.settings.AutoAdd.onClick = function(widget)
config.autoAdd = not config.autoAdd
widget:setChecked(config.autoAdd)
end
local TabBar = ListWindow.tmpTabBar
TabBar:setContentWidget(ListWindow.tmpTabContent)
local blacklistList
for v = 1, 3 do
local listPanel = g_ui.createWidget("tPanel") -- Creates Panel
local playerList = playerTables[v]
listPanel:setId(tabs[v].."Tab")
TabBar:addTab(tabs[v], listPanel)
-- elements
local addButton = listPanel.add
local nameTab = listPanel.name
local list = listPanel.list
if v == 3 then
blacklistList = list
end
for i, name in ipairs(playerList) do
local label = UI.createWidget("PlayerLabel", list)
label:setText(name)
label.remove.onClick = function()
table.remove(playerList, table.find(playerList, name))
label:destroy()
clearCachedPlayers()
refreshStatus()
end
label.onMouseRelease = function(widget, mousePos, mouseButton)
if mouseButton == 2 then
local child = rootWidget:recursiveGetChildByPos(mousePos)
if child == widget then
local menu = g_ui.createWidget('PopupMenu')
menu:setId("blzMenu")
menu:setGameMenu(true)
menu:addOption('Check Player', function()
local name = widget:getText():gsub(" ", spacing)
g_platform.openUrl(link..name)
end, "")
menu:addOption('Copy Name', function()
g_window.setClipboardText(widget:getText())
end, "")
menu:display(mousePos)
return true
end
end
end
end
local tabButton = TabBar.buttonsPanel:getChildren()[v]
tabButton.onStyleApply = function(widget)
if TabBar:getCurrentTab() == widget then
widget:setColor(colors[v])
end
end
-- callbacks
addButton.onClick = function()
local names = string.split(nameTab:getText(), ",")
if #names == 0 then
warn("vBot[PlayerList]: Name is missing!")
return
end
for i=1,#names do
local name = names[i]:trim()
if name:len() == 0 then
warn("vBot[PlayerList]: Name is missing!")
else
if not table.find(playerList, name) then
table.insert(playerList, name)
local label = UI.createWidget("PlayerLabel", list)
label:setText(name)
label.remove.onClick = function()
table.remove(playerList, table.find(playerList, name))
label:destroy()
end
label.onMouseRelease = function(widget, mousePos, mouseButton)
if mouseButton == 2 then
local child = rootWidget:recursiveGetChildByPos(mousePos)
if child == widget then
local menu = g_ui.createWidget('PopupMenu')
menu:setId("blzMenu")
menu:setGameMenu(true)
menu:addOption('Check Player', function()
local name = widget:getText():gsub(" ", "_")
local link = "https://www.gunzodus.net/character/show/"
g_platform.openUrl(link..name)
end, "")
menu:addOption('Copy Name', function()
g_window.setClipboardText(widget:getText())
end, "")
menu:display(mousePos)
return true
end
end
end
nameTab:setText("")
else
warn("vBot[PlayerList]: Player ".. name .." is already added!")
nameTab:setText("")
end
clearCachedPlayers()
refreshStatus()
end
end
end
nameTab.onKeyPress = function(widget, keyCode, keyboardModifiers)
if keyCode ~= 5 then
return false
end
addButton.onClick()
return true
end
end
function addBlackListPlayer(name)
if table.find(config.blackList, name) then return end
table.insert(config.blackList, name)
local label = UI.createWidget("PlayerLabel", blacklistList)
label:setText(name)
label.remove.onClick = function()
table.remove(playerList, table.find(playerList, name))
label:destroy()
end
label.onMouseRelease = function(widget, mousePos, mouseButton)
if mouseButton == 2 then
local child = rootWidget:recursiveGetChildByPos(mousePos)
if child == widget then
local menu = g_ui.createWidget('PopupMenu')
menu:setId("blzMenu")
menu:setGameMenu(true)
menu:addOption('Check Player', function()
local name = widget:getText():gsub(" ", "_")
local link = "https://www.gunzodus.net/character/show/"
g_platform.openUrl(link..name)
end, "")
menu:addOption('Copy Name', function()
g_window.setClipboardText(widget:getText())
end, "")
menu:display(mousePos)
return true
end
end
end
end
end
onTextMessage(function(mode,text)
if not config.autoAdd then return end
if CaveBot.isOff() or TargetBot.isOff() then return end
if not text:find("Warning! The murder of") then return end
text = string.split(text, "Warning! The murder of ")[1]
text = string.split(text, " was not justified.")[1]
addBlackListPlayer(text)
end)
onCreatureAppear(function(creature)
checkStatus(creature)
end)
onPlayerPositionChange(function(x,y)
if x.z ~= y.z then
schedule(20, function()
refreshStatus()
end)
end
end)

Some files were not shown because too many files have changed in this diff Show More