Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/modules/game_battle/battle.otui +155-0 View file
@@ -0,0 +1,155 @@
1+BattleIcon < UICheckBox
2+ size: 20 20
3+ image-color: white
4+ image-rect: 0 0 20 20
5+
6+ $hover !disabled:
7+ color: #cccccc
8+
9+ $!checked:
10+ image-clip: 0 0 20 20
11+
12+ $hover !checked:
13+ image-clip: 0 40 20 20
14+
15+ $checked:
16+ image-clip: 0 20 20 20
17+
18+ $hover checked:
19+ image-clip: 0 60 20 20
20+
21+ $disabled:
22+ image-color: #ffffff88
23+
24+BattlePlayers < BattleIcon
25+ image-source: /images/game/battle/battle_players
26+
27+BattleNPCs < BattleIcon
28+ image-source: /images/game/battle/battle_npcs
29+
30+BattleMonsters < BattleIcon
31+ image-source: /images/game/battle/battle_monsters
32+
33+BattleSkulls < BattleIcon
34+ image-source: /images/game/battle/battle_skulls
35+
36+BattleParty < BattleIcon
37+ image-source: /images/game/battle/battle_party
38+
39+MiniWindow
40+ id: battleWindow
41+ !text: tr('Battle')
42+ height: 166
43+ icon: /images/topbuttons/battle
44+ @onClose: modules.game_battle.onMiniWindowClose()
45+ &save: true
46+ &autoOpen: false
47+
48+ Panel
49+ id: filterPanel
50+ margin-top: 26
51+ anchors.top: parent.top
52+ anchors.left: parent.left
53+ anchors.right: miniwindowScrollBar.left
54+ height: 45
55+
56+ Panel
57+ id: buttons
58+ anchors.top: parent.top
59+ anchors.horizontalCenter: parent.horizontalCenter
60+ height: 20
61+ width: 120
62+ layout:
63+ type: horizontalBox
64+ spacing: 5
65+
66+ BattlePlayers
67+ id: hidePlayers
68+ !tooltip: tr('Hide players')
69+ @onSetup: if g_app.isMobile() then self:setChecked(true) end
70+ @onCheckChange: modules.game_battle.checkCreatures()
71+
72+ BattleNPCs
73+ id: hideNPCs
74+ !tooltip: tr('Hide Npcs')
75+ @onSetup: if g_app.isMobile() then self:setChecked(true) end
76+ @onCheckChange: modules.game_battle.checkCreatures()
77+
78+ BattleMonsters
79+ id: hideMonsters
80+ !tooltip: tr('Hide monsters')
81+ @onCheckChange: modules.game_battle.checkCreatures()
82+
83+ BattleSkulls
84+ id: hideSkulls
85+ !tooltip: tr('Hide non-skull players')
86+ @onCheckChange: modules.game_battle.checkCreatures()
87+
88+ BattleParty
89+ id: hideParty
90+ !tooltip: tr('Hide party members')
91+ @onSetup: if g_app.isMobile() then self:setChecked(true) end
92+ @onCheckChange: modules.game_battle.checkCreatures()
93+
94+ Panel
95+ id: sortPanel
96+ anchors.top: prev.bottom
97+ anchors.left: parent.left
98+ anchors.right: parent.right
99+ height: 20
100+ margin-top: 6
101+
102+ ComboBox
103+ id: sortTypeBox
104+ width: 90
105+ anchors.top: parent.top
106+ anchors.left: prev.right
107+ anchors.horizontalCenter: parent.horizontalCenter
108+ margin-left: -31
109+
110+ ComboBox
111+ id: sortOrderBox
112+ width: 60
113+ anchors.top: parent.top
114+ anchors.left: prev.right
115+ margin-left: 4
116+
117+ Panel
118+ height: 18
119+ anchors.top: prev.bottom
120+ anchors.left: parent.left
121+ anchors.right: miniwindowScrollBar.left
122+ margin-top: 4
123+
124+ UIWidget
125+ id: toggleFilterButton
126+ anchors.top: prev.top
127+ width: 21
128+ anchors.horizontalCenter: parent.horizontalCenter
129+ image-source: /images/ui/arrow_vertical
130+ image-rect: 0 0 21 12
131+ image-clip: 21 0 21 12
132+ @onClick: modules.game_battle.toggleFilterPanel()
133+ phantom: false
134+
135+ HorizontalSeparator
136+ anchors.top: prev.top
137+ anchors.left: parent.left
138+ anchors.right: miniwindowScrollBar.left
139+ margin-right: 1
140+ margin-top: 11
141+
142+ MiniWindowContents
143+ anchors.top: prev.bottom
144+ margin-top: 6
145+
146+ Panel
147+ id: battlePanel
148+ anchors.left: parent.left
149+ anchors.right: parent.right
150+ anchors.top: parent.top
151+ margin-top: 5
152+ padding-right: 5
153+ layout:
154+ type: verticalBox
155+ fit-children: true
A RD_Client/modules/game_battle/battlebutton.otui +2-0 View file
@@ -0,0 +1,2 @@
1+BattleButton < CreatureButton
2+ &isBattleButton: true
A RD_Client/modules/game_bot/bot.otmod +8-0 View file
@@ -0,0 +1,8 @@
1+Module
2+ name: game_bot
3+ description: Advanced OTClientV8 Bot
4+ author: otclient@otclient.ovh
5+ sandboxed: true
6+ scripts: [ bot ]
7+ @onLoad: init()
8+ @onUnload: terminate()
A RD_Client/modules/game_bot/bot.otui +126-0 View file
@@ -0,0 +1,126 @@
1+BotTabBar < TabBar
2+ tab-spacing: 1
3+ margin-left: 1
4+ margin-right: 1
5+ height: 20
6+
7+ $on:
8+ visible: true
9+ margin-top: 2
10+
11+ $!on:
12+ visible: false
13+ margin-top: -20
14+
15+BotTabBarPanel < TabBarPanel
16+BotTabBarButton < TabBarButton
17+ padding: 4
18+ padding-right: 5
19+ text-horizontal-auto-resize: true
20+ $!first:
21+ margin-left: 0
22+
23+MiniWindow
24+ id: botWindow
25+ visible: false
26+ !text: tr('Bot')
27+ height: 600
28+ icon: /images/topbuttons/bot
29+ @onClose: modules.game_bot.onMiniWindowClose()
30+ &save: true
31+
32+ MiniWindowContents
33+ ComboBox
34+ id: config
35+ anchors.top: parent.top
36+ anchors.left: parent.left
37+ anchors.right: parent.right
38+ margin-top: 2
39+ margin-left: 2
40+ margin-right: 75
41+ text-offset: 3 0
42+
43+ Button
44+ id: editConfig
45+ anchors.top: prev.top
46+ anchors.left: prev.right
47+ anchors.right: parent.right
48+ !text: tr('Edit')
49+ @onClick: modules.game_bot.edit()
50+ margin-left: 3
51+ margin-right: 37
52+
53+ Button
54+ id: enableButton
55+ anchors.top: prev.top
56+ anchors.left: prev.right
57+ anchors.right: parent.right
58+ margin-left: 3
59+ margin-right: 2
60+
61+ $on:
62+ text: On
63+ color: #00AA00
64+
65+ $!on:
66+ text: Off
67+ color: #FF0000
68+
69+ Label
70+ id: statusLabel
71+ anchors.top: prev.bottom
72+ anchors.left: parent.left
73+ anchors.right: parent.right
74+ text-wrap: true
75+ text-auto-resize: true
76+ text-align: center
77+ !text: tr('Status: waiting')
78+ margin-left: 3
79+ margin-right: 3
80+
81+ $on:
82+ margin-top: 3
83+
84+ $!on:
85+ text:
86+ margin-top: -13
87+
88+ HorizontalSeparator
89+ anchors.left: parent.left
90+ anchors.right: parent.right
91+ anchors.top: prev.bottom
92+ margin-top: 3
93+ margin-left: 2
94+ margin-right: 2
95+
96+ Panel
97+ anchors.top: prev.bottom
98+ anchors.left: parent.left
99+ anchors.right: parent.right
100+ id: messages
101+ layout:
102+ type: verticalBox
103+ fit-children: true
104+
105+ HorizontalSeparator
106+ anchors.left: parent.left
107+ anchors.right: parent.right
108+ anchors.top: prev.bottom
109+ margin-top: 5
110+ margin-left: 2
111+ margin-right: 2
112+
113+ BotTabBar
114+ id: botTabs
115+ anchors.top: prev.bottom
116+ anchors.left: parent.left
117+ anchors.right: parent.right
118+ margin-right: -20
119+
120+ Panel
121+ id: botPanel
122+ margin-top: 2
123+ anchors.top: prev.bottom
124+ anchors.bottom: parent.bottom
125+ anchors.left: parent.left
126+ anchors.right: parent.right
A RD_Client/modules/game_bot/configs.png +0-0 View file
Binary file not shown.
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot.lua +36-0 View file
@@ -0,0 +1,36 @@
1+-- Cavebot by otclient@otclient.ovh
2+-- visit http://bot.otclient.ovh/
3+
4+local cavebotTab = "Cave"
5+local targetingTab = "Target"
6+
7+setDefaultTab(cavebotTab)
8+CaveBot = {} -- global namespace
9+CaveBot.Extensions = {}
10+importStyle("/cavebot/cavebot.otui")
11+importStyle("/cavebot/editor.otui")
12+importStyle("/cavebot/supply.otui")
13+dofile("/cavebot/actions.lua")
14+dofile("/cavebot/editor.lua")
15+dofile("/cavebot/example_functions.lua")
16+dofile("/cavebot/recorder.lua")
17+-- in this section you can add extensions, check extension_template.lua
18+--dofile("/cavebot/extension_template.lua")
19+dofile("/cavebot/depositer.lua")
20+dofile("/cavebot/supply.lua")
21+-- main cavebot file, must be last
22+dofile("/cavebot/cavebot.lua")
23+
24+setDefaultTab(targetingTab)
25+TargetBot = {} -- global namespace
26+importStyle("/targetbot/looting.otui")
27+importStyle("/targetbot/target.otui")
28+importStyle("/targetbot/creature_editor.otui")
29+dofile("/targetbot/creature.lua")
30+dofile("/targetbot/creature_attack.lua")
31+dofile("/targetbot/creature_editor.lua")
32+dofile("/targetbot/creature_priority.lua")
33+dofile("/targetbot/looting.lua")
34+dofile("/targetbot/walking.lua")
35+-- main targetbot file, must be last
36+dofile("/targetbot/target.lua")
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/actions.lua +252-0 View file
@@ -0,0 +1,252 @@
1+CaveBot.Actions = {}
2+
3+-- it adds an action widget to list
4+CaveBot.addAction = function(action, value, focus)
5+ action = action:lower()
6+ local raction = CaveBot.Actions[action]
7+ if not raction then
8+ return error("Invalid cavebot action: " .. action)
9+ end
10+ if type(value) == 'number' then
11+ value = tostring(value)
12+ end
13+ local widget = UI.createWidget("CaveBotAction", CaveBot.actionList)
14+ widget:setText(action .. ":" .. value:split("\n")[1])
15+ widget.action = action
16+ widget.value = value
17+ if raction.color then
18+ widget:setColor(raction.color)
19+ end
20+ widget.onDoubleClick = function(cwidget) -- edit on double click
21+ if CaveBot.Editor then
22+ schedule(20, function() -- schedule to have correct focus
23+ CaveBot.Editor.edit(cwidget.action, cwidget.value, function(action, value)
24+ CaveBot.editAction(cwidget, action, value)
25+ CaveBot.save()
26+ end)
27+ end)
28+ end
29+ end
30+ if focus then
31+ widget:focus()
32+ CaveBot.actionList:ensureChildVisible(widget)
33+ end
34+ return widget
35+end
36+
37+-- it updates existing widget, you should call CaveBot.save() later
38+CaveBot.editAction = function(widget, action, value)
39+ action = action:lower()
40+ local raction = CaveBot.Actions[action]
41+ if not raction then
42+ return error("Invalid cavebot action: " .. action)
43+ end
44+
45+ if not widget.action or not widget.value then
46+ return error("Invalid cavebot action widget, has missing action or value")
47+ end
48+
49+ widget:setText(action .. ":" .. value:split("\n")[1])
50+ widget.action = action
51+ widget.value = value
52+ if raction.color then
53+ widget:setColor(raction.color)
54+ end
55+ return widget
56+end
57+
58+--[[
59+registerAction:
60+action - string, color - string, callback = function(value, retries, prev)
61+value is a string value of action, retries is number which will grow by 1 if return is "retry"
62+prev is a true when previuos action was executed succesfully, false otherwise
63+it must return true if executed correctly, false otherwise
64+it can also return string "retry", then the function will be called again in 20 ms
65+]]--
66+CaveBot.registerAction = function(action, color, callback)
67+ action = action:lower()
68+ if CaveBot.Actions[action] then
69+ return error("Duplicated acction: " .. action)
70+ end
71+ CaveBot.Actions[action] = {
72+ color=color,
73+ callback=callback
74+ }
75+end
76+
77+CaveBot.registerAction("label", "yellow", function(value, retries, prev)
78+ return true
79+end)
80+
81+CaveBot.registerAction("gotolabel", "#FFFF55", function(value, retries, prev)
82+ return CaveBot.gotoLabel(value)
83+end)
84+
85+CaveBot.registerAction("delay", "#AAAAAA", function(value, retries, prev)
86+ if retries == 0 then
87+ CaveBot.delay(tonumber(value))
88+ return "retry"
89+ end
90+ return true
91+end)
92+
93+CaveBot.registerAction("function", "red", function(value, retries, prev)
94+ local prefix = "local retries = " .. retries .. "\nlocal prev = " .. tostring(prev) .. "\nlocal delay = CaveBot.delay\nlocal gotoLabel = CaveBot.gotoLabel\n"
95+ prefix = prefix .. "local macro = function() error('Macros inside cavebot functions are not allowed') end\n"
96+ for extension, callbacks in pairs(CaveBot.Extensions) do
97+ prefix = prefix .. "local " .. extension .. " = CaveBot.Extensions." .. extension .. "\n"
98+ end
99+ local status, result = pcall(function()
100+ return assert(load(prefix .. value, "cavebot_function"))()
101+ end)
102+ if not status then
103+ error("Error in cavebot function:\n" .. result)
104+ return false
105+ end
106+ return result
107+end)
108+
109+CaveBot.registerAction("goto", "green", function(value, retries, prev)
110+ local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
111+ print("Bot Walk Path")
112+ if not pos[1] then
113+ error("Invalid cavebot goto action value. It should be position (x,y,z), is: " .. value)
114+ return false
115+ end
116+
117+ if retries >= 5 then
118+ return false -- tried 5 times, can't get there
119+ end
120+
121+ pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
122+ local playerPos = player:getPosition()
123+ if pos.z ~= playerPos.z then
124+ return false -- different floor
125+ end
126+
127+ if math.abs(pos.x-playerPos.x) + math.abs(pos.y-playerPos.y) > 40 then
128+ return false -- too far way
129+ end
130+
131+ local minimapColor = g_map.getMinimapColor(pos)
132+ local stairs = (minimapColor >= 210 and minimapColor <= 213)
133+
134+ if stairs then
135+ if math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 0 then
136+ return true -- already at position
137+ end
138+ elseif math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 1 then
139+ return true -- already at position
140+ end
141+ -- check if there's a path to that place, ignore creatures and fields
142+ local path = findPath(playerPos, pos, 40, { ignoreNonPathable = true, precision = 1, ignoreCreatures = true })
143+ if not path then
144+ return false -- there's no way
145+ end
146+
147+ -- walk will be executed, but it will take some time to get response from server, wait 300ms after autowalk
148+ CaveBot.delay(300)
149+
150+ -- try to find path, don't ignore creatures, don't ignore fields
151+ if autoWalk(pos, 40) then
152+ return "retry"
153+ end
154+
155+ -- try to find path, don't ignore creatures, ignore fields
156+ if autoWalk(pos, 40, { ignoreNonPathable = true }) then
157+ return "retry"
158+ end
159+
160+ if retries >= 3 then
161+ -- try to lower precision, find something close to final position
162+ local precison = retries - 1
163+ if stairs then
164+ precison = 0
165+ end
166+ if autoWalk(pos, 50, { ignoreNonPathable = true, precision = precison }) then
167+ return "retry"
168+ end
169+ end
170+
171+ autoWalk(path) -- everything else failed, try to walk ignoring creatures, maybe will work
172+ return "retry"
173+end)
174+
175+CaveBot.registerAction("use", "#FFB272", function(value, retries, prev)
176+ local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
177+ if not pos[1] then
178+ local itemid = tonumber(value)
179+ if not itemid then
180+ error("Invalid cavebot use action value. It should be (x,y,z) or item id, is: " .. value)
181+ return false
182+ end
183+ use(itemid)
184+ return true
185+ end
186+
187+ pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
188+ local playerPos = player:getPosition()
189+ if pos.z ~= playerPos.z then
190+ return false -- different floor
191+ end
192+
193+ if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then
194+ return false -- too far way
195+ end
196+
197+ local tile = g_map.getTile(pos)
198+ if not tile then
199+ return false
200+ end
201+
202+ local topThing = tile:getTopUseThing()
203+ if not topThing then
204+ return false
205+ end
206+
207+ use(topThing)
208+ CaveBot.delay(400)
209+ return true
210+end)
211+
212+CaveBot.registerAction("usewith", "#EEB292", function(value, retries, prev)
213+ local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
214+ if not pos[1] then
215+ if not itemid then
216+ error("Invalid cavebot usewith action value. It should be (itemid,x,y,z) or item id, is: " .. value)
217+ return false
218+ end
219+ use(itemid)
220+ return true
221+ end
222+
223+ local itemid = tonumber(pos[1][2])
224+ pos = {x=tonumber(pos[1][3]), y=tonumber(pos[1][4]), z=tonumber(pos[1][5])}
225+ local playerPos = player:getPosition()
226+ if pos.z ~= playerPos.z then
227+ return false -- different floor
228+ end
229+
230+ if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then
231+ return false -- too far way
232+ end
233+
234+ local tile = g_map.getTile(pos)
235+ if not tile then
236+ return false
237+ end
238+
239+ local topThing = tile:getTopUseThing()
240+ if not topThing then
241+ return false
242+ end
243+
244+ usewith(itemid, topThing)
245+ CaveBot.delay(400)
246+ return true
247+end)
248+
249+CaveBot.registerAction("say", "#FF55FF", function(value, retries, prev)
250+ say(value)
251+ return true
252+end)
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.lua +192-0 View file
@@ -0,0 +1,192 @@
1+local cavebotMacro = nil
2+local config = nil
3+
4+-- ui
5+local configWidget = UI.Config()
6+local ui = UI.createWidget("CaveBotPanel")
7+
8+ui.list = ui.listPanel.list -- shortcut
9+CaveBot.actionList = ui.list
10+
11+if CaveBot.Editor then
12+ CaveBot.Editor.setup()
13+end
14+for extension, callbacks in pairs(CaveBot.Extensions) do
15+ if callbacks.setup then
16+ callbacks.setup()
17+ end
18+end
19+
20+-- main loop, controlled by config
21+local actionRetries = 0
22+local prevActionResult = true
23+cavebotMacro = macro(20, function()
24+ if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then
25+ return -- target bot or looting is working, wait
26+ end
27+
28+ local actions = ui.list:getChildCount()
29+ if actions == 0 then return end
30+ local currentAction = ui.list:getFocusedChild()
31+ if not currentAction then
32+ currentAction = ui.list:getFirstChild()
33+ end
34+ local action = CaveBot.Actions[currentAction.action]
35+ local value = currentAction.value
36+ local retry = false
37+ if action then
38+ local status, result = pcall(function()
39+ return action.callback(value, actionRetries, prevActionResult)
40+ end)
41+ if status then
42+ if result == "retry" then
43+ actionRetries = actionRetries + 1
44+ retry = true
45+ elseif type(result) == 'boolean' then
46+ actionRetries = 0
47+ prevActionResult = result
48+ else
49+ error("Invalid return from cavebot action (" .. currentAction.action .. "), should be \"retry\", false or true, is: " .. tostring(result))
50+ end
51+ else
52+ error("Error while executing cavebot action (" .. currentAction.action .. "):\n" .. result)
53+ end
54+ else
55+ error("Invalid cavebot action: " .. currentAction.action)
56+ end
57+
58+ if retry then
59+ return
60+ end
61+
62+ if currentAction ~= ui.list:getFocusedChild() then
63+ -- focused child can change durring action, get it again and reset state
64+ currentAction = ui.list:getFocusedChild() or ui.list:getFirstChild()
65+ actionRetries = 0
66+ prevActionResult = true
67+ end
68+ local nextAction = ui.list:getChildIndex(currentAction) + 1
69+ if nextAction > actions then
70+ nextAction = 1
71+ end
72+ ui.list:focusChild(ui.list:getChildByIndex(nextAction))
73+end)
74+
75+onPlayerPositionChange(function()
76+ local delayAfterPositionChange = math.max(player:getStepDuration() + 100, 200)
77+ cavebotMacro.delay = math.max(cavebotMacro.delay or 0, now + delayAfterPositionChange)
78+end)
79+
80+-- config, its callback is called immediately, data can be nil
81+local lastConfig = ""
82+config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, enabled, data)
83+ if enabled and CaveBot.Recorder.isOn() then
84+ CaveBot.Recorder.disable()
85+ CaveBot.setOff()
86+ return
87+ end
88+
89+ local currentActionIndex = ui.list:getChildIndex(ui.list:getFocusedChild())
90+ ui.list:destroyChildren()
91+ if not data then return cavebotMacro.setOff() end
92+
93+ for k,v in ipairs(data) do
94+ if type(v) == "table" and #v == 2 then
95+ if v[1] == "extensions" then
96+ local status, result = pcall(function()
97+ return json.decode(v[2])
98+ end)
99+ if not status then
100+ error("Error while parsing CaveBot extensions from config:\n" .. result)
101+ else
102+ for extension, callbacks in pairs(CaveBot.Extensions) do
103+ if callbacks.onConfigChange then
104+ callbacks.onConfigChange(name, enabled, result[extension])
105+ end
106+ end
107+ end
108+ else
109+ CaveBot.addAction(v[1], v[2])
110+ end
111+ end
112+ end
113+
114+ actionRetries = 0
115+ prevActionResult = true
116+ cavebotMacro.setOn(enabled)
117+ cavebotMacro.delay = nil
118+ if lastConfig == name then
119+ -- restore focused child on the action list
120+ ui.list:focusChild(ui.list:getChildByIndex(currentActionIndex))
121+ end
122+ lastConfig = name
123+end)
124+
125+-- ui callbacks
126+ui.showEditor.onClick = function()
127+ if not CaveBot.Editor then return end
128+ if ui.showEditor:isOn() then
129+ CaveBot.Editor.hide()
130+ ui.showEditor:setOn(false)
131+ else
132+ CaveBot.Editor.show()
133+ ui.showEditor:setOn(true)
134+ end
135+end
136+
137+
138+-- public function, you can use them in your scripts
139+CaveBot.isOn = function()
140+ return config.isOn()
141+end
142+
143+CaveBot.isOff = function()
144+ return config.isOff()
145+end
146+
147+CaveBot.setOn = function(val)
148+ if val == false then
149+ return CaveBot.setOff(true)
150+ end
151+ config.setOn()
152+end
153+
154+CaveBot.setOff = function(val)
155+ if val == false then
156+ return CaveBot.setOn(true)
157+ end
158+ config.setOff()
159+end
160+
161+CaveBot.delay = function(value)
162+ cavebotMacro.delay = now + value
163+end
164+
165+CaveBot.gotoLabel = function(label)
166+ label = label:lower()
167+ for index, child in ipairs(ui.list:getChildren()) do
168+ if child.action == "label" and child.value:lower() == label then
169+ ui.list:focusChild(child)
170+ return true
171+ end
172+ end
173+ return false
174+end
175+
176+CaveBot.save = function()
177+ local data = {}
178+ for index, child in ipairs(ui.list:getChildren()) do
179+ table.insert(data, {child.action, child.value})
180+ end
181+ local extension_data = {}
182+ for extension, callbacks in pairs(CaveBot.Extensions) do
183+ if callbacks.onSave then
184+ local ext_data = callbacks.onSave()
185+ if type(ext_data) == "table" then
186+ extension_data[extension] = ext_data
187+ end
188+ end
189+ end
190+ table.insert(data, {"extensions", json.encode(extension_data, 2)})
191+ config.save(data)
192+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.otui +48-0 View file
@@ -0,0 +1,48 @@
1+CaveBotAction < Label
2+ background-color: alpha
3+ text-offset: 2 0
4+ focusable: true
5+
6+ $focus:
7+ background-color: #00000055
8+
9+
10+CaveBotPanel < Panel
11+ layout:
12+ type: verticalBox
13+ fit-children: true
14+
15+ HorizontalSeparator
16+ margin-top: 2
17+ margin-bottom: 5
18+
19+ Panel
20+ id: listPanel
21+ height: 100
22+ margin-top: 2
23+
24+ TextList
25+ id: list
26+ anchors.fill: parent
27+ vertical-scrollbar: listScrollbar
28+ margin-right: 15
29+ focusable: false
30+ auto-focus: first
31+
32+ VerticalScrollBar
33+ id: listScrollbar
34+ anchors.top: parent.top
35+ anchors.bottom: parent.bottom
36+ anchors.right: parent.right
37+ pixels-scroll: true
38+ step: 10
39+
40+ BotSwitch
41+ id: showEditor
42+ margin-top: 2
43+
44+ $on:
45+ text: Hide waypoints editor
46+
47+ $!on:
48+ text: Show waypoints editor
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/depositer.lua +27-0 View file
@@ -0,0 +1,27 @@
1+CaveBot.Extensions.Depositer = {}
2+
3+local ui
4+
5+-- first function called, here you should setup your UI
6+CaveBot.Extensions.Depositer.setup = function()
7+ --ui = UI.createWidget('Label')
8+ --ui:setText("Depositer UI")
9+end
10+
11+-- called when cavebot config changes, configData is a table but it can be nil
12+CaveBot.Extensions.Depositer.onConfigChange = function(configName, isEnabled, configData)
13+ if not configData then return end
14+
15+end
16+
17+-- called when cavebot is saving config, should return table or nil
18+CaveBot.Extensions.Depositer.onSave = function()
19+ return {}
20+end
21+
22+-- bellow add you custom functions
23+-- this function can be used in cavebot function waypoint as: return Depositer.run(retries, prev)
24+-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua to learn more
25+CaveBot.Extensions.Depositer.run = function(retries, prev)
26+ return true
27+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.lua +174-0 View file
@@ -0,0 +1,174 @@
1+CaveBot.Editor = {}
2+CaveBot.Editor.Actions = {}
3+
4+-- also works as registerAction(action, params), then text == action
5+-- params are options for text editor or function to be executed when clicked
6+-- you have many examples how to use it bellow
7+CaveBot.Editor.registerAction = function(action, text, params)
8+ if type(text) ~= 'string' then
9+ params = text
10+ text = action
11+ end
12+
13+ local color = nil
14+ if type(params) ~= 'function' then
15+ local raction = CaveBot.Actions[action]
16+ if not raction then
17+ return error("CaveBot editor error: action " .. action .. " doesn't exist")
18+ end
19+ CaveBot.Editor.Actions[action] = params
20+ color = raction.color
21+ end
22+
23+ local button = UI.createWidget('CaveBotEditorButton', CaveBot.Editor.ui.buttons)
24+ button:setText(text)
25+ if color then
26+ button:setColor(color)
27+ end
28+ button.onClick = function()
29+ if type(params) == 'function' then
30+ params()
31+ return
32+ end
33+ CaveBot.Editor.edit(action, nil, function(action, value)
34+ local focusedAction = CaveBot.actionList:getFocusedChild()
35+ local index = CaveBot.actionList:getChildCount()
36+ if focusedAction then
37+ index = CaveBot.actionList:getChildIndex(focusedAction)
38+ end
39+ local widget = CaveBot.addAction(action, value)
40+ CaveBot.actionList:moveChildToIndex(widget, index + 1)
41+ CaveBot.actionList:focusChild(widget)
42+ CaveBot.save()
43+ end)
44+ end
45+ return button
46+end
47+
48+CaveBot.Editor.setup = function()
49+ CaveBot.Editor.ui = UI.createWidget("CaveBotEditorPanel")
50+ local ui = CaveBot.Editor.ui
51+ local registerAction = CaveBot.Editor.registerAction
52+
53+ registerAction("move up", function()
54+ local action = CaveBot.actionList:getFocusedChild()
55+ if not action then return end
56+ local index = CaveBot.actionList:getChildIndex(action)
57+ if index < 2 then return end
58+ CaveBot.actionList:moveChildToIndex(action, index - 1)
59+ CaveBot.actionList:ensureChildVisible(action)
60+ CaveBot.save()
61+ end)
62+ registerAction("edit", function()
63+ local action = CaveBot.actionList:getFocusedChild()
64+ if not action or not action.onDoubleClick then return end
65+ action.onDoubleClick(action)
66+ end)
67+ registerAction("move down", function()
68+ local action = CaveBot.actionList:getFocusedChild()
69+ if not action then return end
70+ local index = CaveBot.actionList:getChildIndex(action)
71+ if index >= CaveBot.actionList:getChildCount() then return end
72+ CaveBot.actionList:moveChildToIndex(action, index + 1)
73+ CaveBot.actionList:ensureChildVisible(action)
74+ CaveBot.save()
75+ end)
76+ registerAction("remove", function()
77+ local action = CaveBot.actionList:getFocusedChild()
78+ if not action then return end
79+ action:destroy()
80+ CaveBot.save()
81+ end)
82+
83+ registerAction("label", {
84+ value="labelName",
85+ title="Label",
86+ description="Add label",
87+ multiline=false
88+ })
89+ registerAction("delay", {
90+ value="500",
91+ title="Delay",
92+ description="Delay next action (in milliseconds)",
93+ multiline=false,
94+ validation="^\\s*[0-9]{1,10}\\s*$"
95+ })
96+ registerAction("gotolabel", "go to label", {
97+ value="labelName",
98+ title="Go to label",
99+ description="Go to label",
100+ multiline=false
101+ })
102+ registerAction("goto", "go to", {
103+ value=function() return posx() .. "," .. posy() .. "," .. posz() end,
104+ title="Go to position",
105+ description="Go to position (x,y,z)",
106+ multiline=false,
107+ validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
108+ })
109+ registerAction("use", {
110+ value=function() return posx() .. "," .. posy() .. "," .. posz() end,
111+ title="Use",
112+ description="Use item from position (x,y,z) or from inventory (itemId)",
113+ multiline=false
114+ })
115+ registerAction("usewith", "use with", {
116+ value=function() return "itemId," .. posx() .. "," .. posy() .. "," .. posz() end,
117+ title="Use with",
118+ description="Use item at position (itemid,x,y,z)",
119+ multiline=false,
120+ validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
121+ })
122+ registerAction("say", {
123+ value="text",
124+ title="Say",
125+ description="Enter text to say",
126+ multiline=false
127+ })
128+ registerAction("function", {
129+ title="Edit bot function",
130+ multiline=true,
131+ value=CaveBot.Editor.ExampleFunctions[1][2],
132+ examples=CaveBot.Editor.ExampleFunctions,
133+ width=650
134+ })
135+
136+ ui.autoRecording.onClick = function()
137+ if ui.autoRecording:isOn() then
138+ CaveBot.Recorder.disable()
139+ else
140+ CaveBot.Recorder.enable()
141+ end
142+ end
143+
144+ -- callbacks
145+ onPlayerPositionChange(function(pos)
146+ ui.pos:setText("Position: " .. pos.x .. ", " .. pos.y .. ", " .. pos.z)
147+ end)
148+ ui.pos:setText("Position: " .. posx() .. ", " .. posy() .. ", " .. posz())
149+end
150+
151+CaveBot.Editor.show = function()
152+ CaveBot.Editor.ui:show()
153+end
154+
155+
156+CaveBot.Editor.hide = function()
157+ CaveBot.Editor.ui:hide()
158+end
159+
160+CaveBot.Editor.edit = function(action, value, callback) -- callback = function(action, value)
161+ local params = CaveBot.Editor.Actions[action]
162+ if not params then return end
163+ if not value then
164+ if type(params.value) == 'function' then
165+ value = params.value()
166+ elseif type(params.value) == 'string' then
167+ value = params.value
168+ end
169+ end
170+
171+ UI.EditorWindow(value, params, function(newText)
172+ callback(action, newText)
173+ end)
174+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.otui +44-0 View file
@@ -0,0 +1,44 @@
1+CaveBotEditorButton < Button
2+
3+
4+CaveBotEditorPanel < Panel
5+ id: cavebotEditor
6+ visible: false
7+ layout:
8+ type: verticalBox
9+ fit-children: true
10+
11+ Label
12+ id: pos
13+ text-align: center
14+ text: -
15+
16+ Panel
17+ id: buttons
18+ margin-top: 2
19+ layout:
20+ type: grid
21+ cell-size: 86 20
22+ cell-spacing: 1
23+ flow: true
24+ fit-children: true
25+
26+ Label
27+ text: Double click on action from action list to edit it
28+ text-align: center
29+ text-auto-resize: true
30+ text-wrap: true
31+ margin-top: 3
32+ margin-left: 2
33+ margin-right: 2
34+
35+ BotSwitch
36+ id: autoRecording
37+ text: Auto Recording
38+ margin-top: 3
39+
40+ BotButton
41+ margin-top: 3
42+ margin-bottom: 3
43+ text: Documentation
44+ @onClick: g_platform.openUrl("http://bot.otclient.ovh/")
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/example_functions.lua +65-0 View file
@@ -0,0 +1,65 @@
1+CaveBot.Editor.ExampleFunctions = {}
2+
3+local function addExampleFunction(title, text)
4+ return table.insert(CaveBot.Editor.ExampleFunctions, {title, text:trim()})
5+end
6+
7+addExampleFunction("Click to browse example functions", [[
8+-- available functions/variables:
9+-- prev - result of previous action (true or false)
10+-- retries - number of retries of current function, goes up by one when you return "retry"
11+-- delay(number) - delays bot next action, value in milliseconds
12+-- gotoLabel(string) - goes to specific label, return true if label exists
13+-- you can easily access bot extensions, Depositer.run() instead of CaveBot.Extensions.Depositer.run()
14+-- also you can access bot global variables, like CaveBot, TargetBot
15+-- use storage variable to store date between calls
16+
17+-- function should return false, true or "retry"
18+-- if "retry" is returned, function will be executed again in 20 ms (so better call delay before)
19+
20+return true
21+]])
22+
23+addExampleFunction("buy 200 mana potion from npc Eryn", [[
24+--buy 200 mana potions
25+local npc = getCreatureByName("Eryn")
26+if not npc then
27+ return false
28+end
29+if retries > 10 then
30+ return false
31+end
32+local pos = player:getPosition()
33+local npcPos = npc:getPosition()
34+if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
35+ autoWalk(npcPos, {precision=3})
36+ delay(300)
37+ return "retry"
38+end
39+if not NPC.isTrading() then
40+ NPC.say("hi")
41+ NPC.say("trade")
42+ delay(200)
43+ return "retry"
44+end
45+NPC.buy(268, 100)
46+schedule(1000, function()
47+ -- buy again in 1s
48+ NPC.buy(268, 100)
49+ NPC.closeTrade()
50+ NPC.say("bye")
51+end)
52+delay(1200)
53+return true
54+]])
55+
56+addExampleFunction("Say hello 5 times with some delay", [[
57+--say hello
58+if retries > 5 then
59+ return true -- finish
60+end
61+say("hello")
62+delay(100 + retries * 100)
63+return "retry"
64+]])
65+
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/extension_template.lua +58-0 View file
@@ -0,0 +1,58 @@
1+-- example cavebot extension (remember to add this file to ../cavebot.lua)
2+CaveBot.Extensions.Example = {}
3+
4+local ui
5+
6+-- setup is called automaticly when cavebot is ready
7+CaveBot.Extensions.Example.setup = function()
8+ ui = UI.createWidget('BotTextEdit')
9+ ui:setText("Hello")
10+ ui.onTextChange = function()
11+ CaveBot.save() -- save new config when you change something
12+ end
13+
14+ -- add custom cavebot action (check out actions.lua)
15+ CaveBot.registerAction("sayhello", "orange", function(value, retries, prev)
16+ local how_many_times = tonumber(value)
17+ if retries >= how_many_times then
18+ return true
19+ end
20+ say("hello " .. (retries + 1))
21+ delay(250)
22+ return "retry"
23+ end)
24+
25+ -- add this custom action to editor (check out editor.lua)
26+ CaveBot.Editor.registerAction("sayhello", "say hello", {
27+ value="5",
28+ title="Say hello",
29+ description="Says hello x times",
30+ validation="[0-9]{1,5}" -- regex, optional
31+ })
32+end
33+
34+-- called when cavebot config changes, configData is a table but it can also be nil
35+CaveBot.Extensions.Example.onConfigChange = function(configName, isEnabled, configData)
36+ if not configData then return end
37+ if configData["text"] then
38+ ui:setText(configData["text"])
39+ end
40+end
41+
42+-- called when cavebot is saving config (so when CaveBot.save() is called), should return table or nil
43+CaveBot.Extensions.Example.onSave = function()
44+ return {text=ui:getText()}
45+end
46+
47+-- bellow add you custom functions to be used in cavebot function action
48+-- an example: return Example.run(retries, prev)
49+-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua and example_functions.lua to learn more
50+CaveBot.Extensions.Example.run = function(retries, prev)
51+ -- it will say text 10 times with some delay and then continue
52+ if retries > 10 then
53+ return true
54+ end
55+ say(ui:getText() .. " x" .. retries)
56+ delay(100 + retries * 100)
57+ return "retry"
58+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/recorder.lua +65-0 View file
@@ -0,0 +1,65 @@
1+-- auto recording for cavebot
2+CaveBot.Recorder = {}
3+
4+local isEnabled = nil
5+local lastPos = nil
6+
7+local function setup()
8+ local function addPosition(pos)
9+ CaveBot.addAction("goto", pos.x .. "," .. pos.y .. "," .. pos.z, true)
10+ lastPos = pos
11+ end
12+
13+ onPlayerPositionChange(function(newPos, oldPos)
14+ if CaveBot.isOn() or not isEnabled then return end
15+ if not lastPos then
16+ -- first step
17+ addPosition(oldPos)
18+ elseif newPos.z ~= oldPos.z or math.abs(oldPos.x - newPos.x) > 1 or math.abs(oldPos.y - newPos.y) > 1 then
19+ -- stairs/teleport
20+ addPosition(oldPos)
21+ elseif math.max(math.abs(lastPos.x - newPos.x), math.abs(lastPos.y - newPos.y)) > 5 then
22+ -- 5 steps from last pos
23+ addPosition(newPos)
24+ end
25+ end)
26+
27+ onUse(function(pos, itemId, stackPos, subType)
28+ if CaveBot.isOn() or not isEnabled then return end
29+ if pos.x ~= 0xFFFF then
30+ lastPos = pos
31+ CaveBot.addAction("use", pos.x .. "," .. pos.y .. "," .. pos.z, true)
32+ end
33+ end)
34+
35+ onUseWith(function(pos, itemId, target, subType)
36+ if CaveBot.isOn() or not isEnabled then return end
37+ if not target:isItem() then return end
38+ local targetPos = target:getPosition()
39+ if targetPos.x == 0xFFFF then return end
40+ lastPos = pos
41+ CaveBot.addAction("usewith", itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z, true)
42+ end)
43+end
44+
45+CaveBot.Recorder.isOn = function()
46+ return isEnabled
47+end
48+
49+CaveBot.Recorder.enable = function()
50+ CaveBot.setOff()
51+ if isEnabled == nil then
52+ setup()
53+ end
54+ CaveBot.Editor.ui.autoRecording:setOn(true)
55+ isEnabled = true
56+ lastPos = nil
57+end
58+
59+CaveBot.Recorder.disable = function()
60+ if isEnabled == true then
61+ isEnabled = false
62+ end
63+ CaveBot.Editor.ui.autoRecording:setOn(false)
64+ CaveBot.save()
65+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.lua +30-0 View file
@@ -0,0 +1,30 @@
1+CaveBot.Extensions.Supply = {}
2+
3+local ui
4+
5+-- first function called, here you should setup your UI
6+CaveBot.Extensions.Supply.setup = function()
7+ --ui = UI.createWidget('SupplyItemList')
8+ --local widget = UI.createWidget('SupplyItem', ui.list)
9+ --widget.item.onItemChange = function(newItem)
10+ --widget.fields.min.onTextChange = function(newText)
11+ -- make it similar to UI.Container, so if there are no free slots, add another one, keep min 4 slots, check if value min/max is number after edit
12+end
13+
14+-- called when cavebot config changes, configData is a table but it can be nil
15+CaveBot.Extensions.Supply.onConfigChange = function(configName, isEnabled, configData)
16+ if not configData then return end
17+
18+end
19+
20+-- called when cavebot is saving config, should return table or nil
21+CaveBot.Extensions.Supply.onSave = function()
22+ return {}
23+end
24+
25+-- bellow add you custom functions
26+-- this function can be used in cavebot function waypoint as: return Supply.run(retries, prev)
27+-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua to learn more
28+CaveBot.Extensions.Supply.run = function(retries, prev)
29+ return true
30+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.otui +72-0 View file
@@ -0,0 +1,72 @@
1+SupplyItem < Panel
2+ height: 34
3+
4+ BotItem
5+ id: item
6+ size: 32 32
7+ anchors.left: parent.left
8+ anchors.top: parent.top
9+ margin-top: 1
10+
11+ Panel
12+ id: fields
13+ anchors.top: parent.top
14+ anchors.bottom: parent.bottom
15+ anchors.left: prev.right
16+ anchors.right: parent.right
17+ margin-left: 2
18+ margin-right: 2
19+
20+ Label
21+ id: minLabel
22+ anchors.top: parent.top
23+ anchors.left: parent.left
24+ anchors.right: parent.horizontalCenter
25+ margin-right: 2
26+ text-align: center
27+ text: "Min"
28+
29+ Label
30+ id: maxLabel
31+ anchors.top: parent.top
32+ anchors.left: parent.horizontalCenter
33+ anchors.right: parent.right
34+ margin-left: 2
35+ text-align: center
36+ text: "Max"
37+
38+ BotTextEdit
39+ id: min
40+ anchors.top: minLabel.bottom
41+ anchors.left: minLabel.left
42+ anchors.right: minLabel.right
43+ text-align: center
44+ text: 1
45+
46+ BotTextEdit
47+ id: max
48+ anchors.top: maxLabel.bottom
49+ anchors.left: maxLabel.left
50+ anchors.right: maxLabel.right
51+ text-align: center
52+ text: 100
53+
54+SupplyItemList < Panel
55+ height: 102
56+
57+ ScrollablePanel
58+ id: list
59+ anchors.fill: parent
60+ vertical-scrollbar: scroll
61+ margin-right: 7
62+ layout:
63+ type: verticalBox
64+ cell-height: 34
65+
66+ BotSmallScrollBar
67+ id: scroll
68+ anchors.top: prev.top
69+ anchors.bottom: prev.bottom
70+ anchors.right: parent.right
71+ step: 10
72+ pixels-scroll: true
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/hp.lua +192-0 View file
@@ -0,0 +1,192 @@
1+setDefaultTab("HP")
2+
3+--2x healing spell
4+--2x healing rune
5+--utani hur
6+--mana shield
7+--anti paralyze
8+--4x equip
9+
10+UI.Label("Healing spells")
11+
12+if type(storage.healing1) ~= "table" then
13+ storage.healing1 = {on=false, title="HP%", text="exura", min=51, max=90}
14+end
15+if type(storage.healing2) ~= "table" then
16+ storage.healing2 = {on=false, title="HP%", text="exura vita", min=0, max=50}
17+end
18+
19+-- create 2 healing widgets
20+for _, healingInfo in ipairs({storage.healing1, storage.healing2}) do
21+ local healingmacro = macro(20, function()
22+ local hp = player:getHealthPercent()
23+ if healingInfo.max >= hp and hp >= healingInfo.min then
24+ if TargetBot then
25+ TargetBot.saySpell(healingInfo.text) -- sync spell with targetbot if available
26+ else
27+ say(healingInfo.text)
28+ end
29+ end
30+ end)
31+ healingmacro.setOn(healingInfo.on)
32+
33+ UI.DualScrollPanel(healingInfo, function(widget, newParams)
34+ healingInfo = newParams
35+ healingmacro.setOn(healingInfo.on)
36+ end)
37+end
38+
39+UI.Separator()
40+
41+UI.Label("Mana & health potions/runes")
42+
43+if type(storage.hpitem1) ~= "table" then
44+ storage.hpitem1 = {on=false, title="HP%", item=266, min=51, max=90}
45+end
46+if type(storage.hpitem2) ~= "table" then
47+ storage.hpitem2 = {on=false, title="HP%", item=3160, min=0, max=50}
48+end
49+if type(storage.manaitem1) ~= "table" then
50+ storage.manaitem1 = {on=false, title="MP%", item=268, min=51, max=90}
51+end
52+if type(storage.manaitem2) ~= "table" then
53+ storage.manaitem2 = {on=false, title="MP%", item=3157, min=0, max=50}
54+end
55+
56+for i, healingInfo in ipairs({storage.hpitem1, storage.hpitem2, storage.manaitem1, storage.manaitem2}) do
57+ local healingmacro = macro(20, function()
58+ local hp = i <= 2 and player:getHealthPercent() or math.min(100, math.floor(100 * (player:getMana() / player:getMaxMana())))
59+ if healingInfo.max >= hp and hp >= healingInfo.min then
60+ if TargetBot then
61+ TargetBot.useItem(healingInfo.item, healingInfo.subType, player) -- sync spell with targetbot if available
62+ else
63+ local thing = g_things.getThingType(healingInfo.item)
64+ local subType = g_game.getClientVersion() >= 860 and 0 or 1
65+ if thing and thing:isFluidContainer() then
66+ subType = healingInfo.subType
67+ end
68+ g_game.useInventoryItemWith(healingInfo.item, player, subType)
69+ end
70+ end
71+ end)
72+ healingmacro.setOn(healingInfo.on)
73+
74+ UI.DualScrollItemPanel(healingInfo, function(widget, newParams)
75+ healingInfo = newParams
76+ healingmacro.setOn(healingInfo.on and healingInfo.item > 100)
77+ end)
78+end
79+
80+if g_game.getClientVersion() < 780 then
81+ UI.Label("In old tibia potions & runes work only when you have backpack with them opened")
82+end
83+
84+UI.Separator()
85+
86+UI.Label("Mana shield spell:")
87+UI.TextEdit(storage.manaShield or "utamo vita", function(widget, newText)
88+ storage.manaShield = newText
89+end)
90+
91+local lastManaShield = 0
92+macro(20, "mana shield", function()
93+ if hasManaShield() or lastManaShield + 90000 > now then return end
94+ if TargetBot then
95+ TargetBot.saySpell(storage.manaShield) -- sync spell with targetbot if available
96+ else
97+ say(storage.manaShield)
98+ end
99+end)
100+
101+UI.Label("Haste spell:")
102+UI.TextEdit(storage.hasteSpell or "utani hur", function(widget, newText)
103+ storage.hasteSpell = newText
104+end)
105+
106+macro(500, "haste", function()
107+ if hasHaste() then return end
108+ if TargetBot then
109+ TargetBot.saySpell(storage.hasteSpell) -- sync spell with targetbot if available
110+ else
111+ say(storage.hasteSpell)
112+ end
113+end)
114+
115+UI.Label("Anti paralyze spell:")
116+UI.TextEdit(storage.antiParalyze or "utani hur", function(widget, newText)
117+ storage.antiParalyze = newText
118+end)
119+
120+macro(100, "anti paralyze", function()
121+ if not isParalyzed() then return end
122+ if TargetBot then
123+ TargetBot.saySpell(storage.antiParalyze) -- sync spell with targetbot if available
124+ else
125+ say(storage.antiParalyze)
126+ end
127+end)
128+
129+UI.Separator()
130+
131+UI.Label("Eatable items:")
132+if type(storage.foodItems) ~= "table" then
133+ storage.foodItems = {3582, 3577}
134+end
135+
136+local foodContainer = UI.Container(function(widget, items)
137+ storage.foodItems = items
138+end, true)
139+foodContainer:setHeight(35)
140+foodContainer:setItems(storage.foodItems)
141+
142+macro(10000, "eat food", function()
143+ if not storage.foodItems[1] then return end
144+ -- search for food in containers
145+ for _, container in pairs(g_game.getContainers()) do
146+ for __, item in ipairs(container:getItems()) do
147+ for i, foodItem in ipairs(storage.foodItems) do
148+ if item:getId() == foodItem.id then
149+ return g_game.use(item)
150+ end
151+ end
152+ end
153+ end
154+ -- can't find any food, try to eat random item using hotkey
155+ if g_game.getClientVersion() < 780 then return end -- hotkey's dont work on old tibia
156+ local toEat = storage.foodItems[math.random(1, #storage.foodItems)]
157+ if toEat then g_game.useInventoryItem(toEat.id) end
158+end)
159+
160+UI.Separator()
161+UI.Label("Auto equip")
162+
163+if type(storage.autoEquip) ~= "table" then
164+ storage.autoEquip = {}
165+end
166+for i=1,4 do -- if you want more auto equip panels you can change 4 to higher value
167+ if not storage.autoEquip[i] then
168+ 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}
169+ end
170+ UI.TwoItemsAndSlotPanel(storage.autoEquip[i], function(widget, newParams)
171+ storage.autoEquip[i] = newParams
172+ end)
173+end
174+macro(250, function()
175+ local containers = g_game.getContainers()
176+ for index, autoEquip in ipairs(storage.autoEquip) do
177+ if autoEquip.on then
178+ local slotItem = getSlot(autoEquip.slot)
179+ if not slotItem or (slotItem:getId() ~= autoEquip.item1 and slotItem:getId() ~= autoEquip.item2) then
180+ for _, container in pairs(containers) do
181+ for __, item in ipairs(container:getItems()) do
182+ if item:getId() == autoEquip.item1 or item:getId() == autoEquip.item2 then
183+ g_game.move(item, {x=65535, y=autoEquip.slot, z=0}, item:getCount())
184+ delay(1000) -- don't call it too often
185+ return
186+ end
187+ end
188+ end
189+ end
190+ end
191+ end
192+end)
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/main.lua +22-0 View file
@@ -0,0 +1,22 @@
1+-- main tab
2+VERSION = "1.2"
3+
4+UI.Label("Config version: " .. VERSION)
5+
6+UI.Separator()
7+
8+
9+
10+UI.Separator()
11+
12+UI.Button("Discord", function()
13+ g_platform.openUrl("https://discord.gg/yhqBE4A")
14+end)
15+
16+UI.Button("Forum", function()
17+ g_platform.openUrl("http://otclient.net/")
18+end)
19+
20+UI.Button("Help & Tutorials", function()
21+ g_platform.openUrl("http://bot.otclient.ovh/")
22+end)
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/mwall_timer.lua +41-0 View file
@@ -0,0 +1,41 @@
1+-- Magic wall & Wild growth timer
2+
3+-- config
4+local magicWallId = 2129
5+local magicWallTime = 20000
6+local wildGrowthId = 2130
7+local wildGrowthTime = 45000
8+
9+-- script
10+local activeTimers = {}
11+
12+onAddThing(function(tile, thing)
13+ if not thing:isItem() then
14+ return
15+ end
16+ local timer = 0
17+ if thing:getId() == magicWallId then
18+ timer = magicWallTime
19+ elseif thing:getId() == wildGrowthId then
20+ timer = wildGrowthTime
21+ else
22+ return
23+ end
24+
25+ local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
26+ if not activeTimers[pos] or activeTimers[pos] < now then
27+ activeTimers[pos] = now + timer
28+ end
29+ tile:setTimer(activeTimers[pos] - now)
30+end)
31+
32+onRemoveThing(function(tile, thing)
33+ if not thing:isItem() then
34+ return
35+ end
36+ if (thing:getId() == magicWallId or thing:getId() == wildGrowthId) and tile:getGround() then
37+ local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
38+ activeTimers[pos] = nil
39+ tile:setTimer(0)
40+ end
41+end)
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature.lua +93-0 View file
@@ -0,0 +1,93 @@
1+
2+TargetBot.Creature = {}
3+TargetBot.Creature.configsCache = {}
4+TargetBot.Creature.cached = 0
5+
6+TargetBot.Creature.resetConfigs = function()
7+ TargetBot.targetList:destroyChildren()
8+ TargetBot.Creature.resetConfigsCache()
9+end
10+
11+TargetBot.Creature.resetConfigsCache = function()
12+ TargetBot.Creature.configsCache = {}
13+ TargetBot.Creature.cached = 0
14+end
15+
16+TargetBot.Creature.addConfig = function(config, focus)
17+ if type(config) ~= 'table' or type(config.name) ~= 'string' then
18+ return error("Invalid targetbot creature config (missing name)")
19+ end
20+ TargetBot.Creature.resetConfigsCache()
21+
22+ if not config.regex then
23+ config.regex = "^" .. config.name:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
24+ end
25+
26+ local widget = UI.createWidget("TargetBotEntry", TargetBot.targetList)
27+ widget:setText(config.name)
28+ widget.value = config
29+
30+ widget.onDoubleClick = function(entry) -- edit on double click
31+ schedule(20, function() -- schedule to have correct focus
32+ TargetBot.Creature.edit(entry.value, function(newConfig)
33+ entry:setText(newConfig.name)
34+ entry.value = newConfig
35+ TargetBot.Creature.resetConfigsCache()
36+ TargetBot.save()
37+ end)
38+ end)
39+ end
40+
41+ if focus then
42+ widget:focus()
43+ TargetBot.targetList:ensureChildVisible(widget)
44+ end
45+ return widget
46+end
47+
48+TargetBot.Creature.getConfigs = function(creature)
49+ if not creature then return {} end
50+ local name = creature:getName():trim():lower()
51+ -- this function may be slow, so it will be using cache
52+ if TargetBot.Creature.configsCache[name] then
53+ return TargetBot.Creature.configsCache[name]
54+ end
55+ local configs = {}
56+ for _, config in ipairs(TargetBot.targetList:getChildren()) do
57+ if regexMatch(name, config.value.regex)[1] then
58+ table.insert(configs, config.value)
59+ end
60+ end
61+ if TargetBot.Creature.cached > 1000 then
62+ TargetBot.Creature.resetConfigsCache() -- too big cache size, reset
63+ end
64+ TargetBot.Creature.configsCache[name] = configs -- add to cache
65+ TargetBot.Creature.cached = TargetBot.Creature.cached + 1
66+ return configs
67+end
68+
69+TargetBot.Creature.calculateParams = function(creature, path)
70+ local configs = TargetBot.Creature.getConfigs(creature)
71+ local priority = 0
72+ local danger = 0
73+ local selectedConfig = nil
74+ for _, config in ipairs(configs) do
75+ local config_priority = TargetBot.Creature.calculatePriority(creature, config, path)
76+ if config_priority > priority then
77+ priority = config_priority
78+ danger = TargetBot.Creature.calculateDanger(creature, config, path)
79+ selectedConfig = config
80+ end
81+ end
82+ return {
83+ config = selectedConfig,
84+ creature = creature,
85+ danger = danger,
86+ priority = priority
87+ }
88+end
89+
90+TargetBot.Creature.calculateDanger = function(creature, config, path)
91+ -- config is based on creature_editor
92+ return config.danger
93+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_attack.lua +112-0 View file
@@ -0,0 +1,112 @@
1+TargetBot.Creature.attack = function(params, targets, isLooting) -- params {config, creature, danger, priority}
2+ if player:isWalking() then
3+ lastWalk = now
4+ end
5+
6+ local config = params.config
7+ local creature = params.creature
8+
9+ if g_game.getAttackingCreature() ~= creature then
10+ g_game.attack(creature)
11+ end
12+
13+ if not isLooting then -- walk only when not looting
14+ TargetBot.Creature.walk(creature, config, targets)
15+ end
16+
17+ -- attacks
18+ local mana = player:getMana()
19+ if config.useGroupAttack and config.groupAttackSpell:len() > 1 and mana > config.minManaGroup then
20+ local creatures = g_map.getSpectatorsInRange(player:getPosition(), false, config.groupAttackRadius, config.groupAttackRadius)
21+ local playersAround = false
22+ local monsters = 0
23+ for _, creature in ipairs(creatures) do
24+ if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then
25+ playersAround = true
26+ elseif creature:isMonster() then
27+ monsters = monsters + 1
28+ end
29+ end
30+ if monsters >= config.groupAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then
31+ if TargetBot.sayAttackSpell(config.groupAttackSpell, config.groupAttackDelay) then
32+ return
33+ end
34+ end
35+ end
36+
37+ if config.useGroupAttackRune and config.groupAttackRune > 100 then
38+ local creatures = g_map.getSpectatorsInRange(creature:getPosition(), false, config.groupRuneAttackRadius, config.groupRuneAttackRadius)
39+ local playersAround = false
40+ local monsters = 0
41+ for _, creature in ipairs(creatures) do
42+ if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then
43+ playersAround = true
44+ elseif creature:isMonster() then
45+ monsters = monsters + 1
46+ end
47+ end
48+ if monsters >= config.groupRuneAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then
49+ if TargetBot.useAttackItem(config.groupAttackRune, 0, creature, config.groupRuneAttackDelay) then
50+ return
51+ end
52+ end
53+ end
54+ if config.useSpellAttack and config.attackSpell:len() > 1 and mana > config.minMana then
55+ if TargetBot.sayAttackSpell(config.attackSpell, config.attackSpellDelay) then
56+ return
57+ end
58+ end
59+ if config.useRuneAttack and config.attackRune > 100 then
60+ if TargetBot.useAttackItem(config.attackRune, 0, creature, config.attackRuneDelay) then
61+ return
62+ end
63+ end
64+end
65+
66+TargetBot.Creature.walk = function(creature, config, targets)
67+ local cpos = creature:getPosition()
68+ local pos = player:getPosition()
69+
70+ -- luring
71+ if (config.lure or config.lureCavebot) and not (config.chase and creature:getHealthPercent() < 30) then
72+ local monsters = 0
73+ if targets < config.lureCount then
74+ if config.lureCavebot then
75+ return TargetBot.allowCaveBot(200)
76+ else
77+ local path = findPath(pos, cpos, 5, {ignoreNonPathable=true, precision=2})
78+ if path then
79+ return TargetBot.walkTo(cpos, 10, {marginMin=5, marginMax=6, ignoreNonPathable=true})
80+ end
81+ end
82+ end
83+ end
84+
85+ local currentDistance = findPath(pos, cpos, 10, {ignoreCreatures=true, ignoreNonPathable=true, ignoreCost=true})
86+ if config.chase and (creature:getHealthPercent() < 30 or not config.keepDistance) then
87+ if #currentDistance > 1 then
88+ return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, precision=1})
89+ end
90+ elseif config.keepDistance then
91+ if #currentDistance ~= config.keepDistanceRange and #currentDistance ~= config.keepDistanceRange + 1 then
92+ return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1})
93+ end
94+ end
95+
96+ if config.avoidAttacks then
97+ local diffx = cpos.x - pos.x
98+ local diffy = cpos.y - pos.y
99+ local candidates = {}
100+ if math.abs(diffx) == 1 and diffy == 0 then
101+ candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x, y=pos.y+1, z=pos.z}}
102+ elseif diffx == 0 and math.abs(diffy) == 1 then
103+ candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}}
104+ end
105+ for _, candidate in ipairs(candidates) do
106+ local tile = g_map.getTile(candidate)
107+ if tile and tile:isWalkable() then
108+ return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true})
109+ end
110+ end
111+ end
112+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.lua +105-0 View file
@@ -0,0 +1,105 @@
1+TargetBot.Creature.edit = function(config, callback) -- callback = function(newConfig)
2+ config = config or {}
3+
4+ local editor = UI.createWindow('TargetBotCreatureEditorWindow')
5+ local values = {} -- (key, function returning value of key)
6+
7+ editor.name:setText(config.name or "")
8+ table.insert(values, {"name", function() return editor.name:getText() end})
9+
10+ local addScrollBar = function(id, title, min, max, defaultValue)
11+ local widget = UI.createWidget('TargetBotCreatureEditorScrollBar', editor.left)
12+ widget.scroll.onValueChange = function(scroll, value)
13+ widget.text:setText(title .. ": " .. value)
14+ end
15+ widget.scroll:setRange(min, max)
16+ if max-min > 1000 then
17+ widget.scroll:setStep(100)
18+ elseif max-min > 100 then
19+ widget.scroll:setStep(10)
20+ end
21+ widget.scroll:setValue(config[id] or defaultValue)
22+ widget.scroll.onValueChange(widget.scroll, widget.scroll:getValue())
23+ table.insert(values, {id, function() return widget.scroll:getValue() end})
24+ end
25+
26+ local addTextEdit = function(id, title, defaultValue)
27+ local widget = UI.createWidget('TargetBotCreatureEditorTextEdit', editor.right)
28+ widget.text:setText(title)
29+ widget.textEdit:setText(config[id] or defaultValue or "")
30+ table.insert(values, {id, function() return widget.textEdit:getText() end})
31+ end
32+
33+ local addCheckBox = function(id, title, defaultValue)
34+ local widget = UI.createWidget('TargetBotCreatureEditorCheckBox', editor.right)
35+ widget.onClick = function()
36+ widget:setOn(not widget:isOn())
37+ end
38+ widget:setText(title)
39+ if config[id] == nil then
40+ widget:setOn(defaultValue)
41+ else
42+ widget:setOn(config[id])
43+ end
44+ table.insert(values, {id, function() return widget:isOn() end})
45+ end
46+
47+ local addItem = function(id, title, defaultItem)
48+ local widget = UI.createWidget('TargetBotCreatureEditorItem', editor.right)
49+ widget.text:setText(title)
50+ widget.item:setItemId(config[id] or defaultItem)
51+ table.insert(values, {id, function() return widget.item:getItemId() end})
52+ end
53+
54+ editor.cancel.onClick = function()
55+ editor:destroy()
56+ end
57+ editor.onEscape = editor.cancel.onClick
58+
59+ editor.ok.onClick = function()
60+ local newConfig = {}
61+ for _, value in ipairs(values) do
62+ newConfig[value[1]] = value[2]()
63+ end
64+ if newConfig.name:len() < 1 then return end
65+ newConfig.regex = "^" .. newConfig.name:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
66+
67+ editor:destroy()
68+ callback(newConfig)
69+ end
70+
71+ -- values
72+ addScrollBar("priority", "Priority", 0, 10, 1)
73+ addScrollBar("danger", "Danger", 0, 10, 1)
74+ addScrollBar("maxDistance", "Max distance", 1, 10, 10)
75+ addScrollBar("keepDistanceRange", "Keep distance", 1, 5, 1)
76+ addScrollBar("lureCount", "Lure", 0, 5, 1)
77+
78+ addScrollBar("minMana", "Min. mana for attack spell", 0, 3000, 200)
79+ addScrollBar("attackSpellDelay", "Attack spell delay", 200, 5000, 2500)
80+ addScrollBar("minManaGroup", "Min. mana for group attack", 0, 3000, 1500)
81+ addScrollBar("groupAttackTargets", "Min. targets for group attack", 1, 10, 2)
82+ addScrollBar("groupAttackRadius", "Radius of group attack spell", 1, 7, 1)
83+ addScrollBar("groupAttackDelay", "Group attack spell delay", 200, 60000, 5000)
84+ addScrollBar("runeAttackDelay", "Rune attack delay", 200, 5000, 2000)
85+ addScrollBar("groupRuneAttackTargets", "Min. targets for group rune attack", 1, 10, 2)
86+ addScrollBar("groupRuneAttackRadius", "Radius of group rune attack", 1, 7, 1)
87+ addScrollBar("groupRuneAttackDelay", "Group rune attack delay", 200, 60000, 5000)
88+
89+ addCheckBox("chase", "Chase", true)
90+ addCheckBox("keepDistance", "Keep Distance", false)
91+ addCheckBox("lure", "Lure", false)
92+ addCheckBox("lureCavebot", "Lure using cavebot", false)
93+ addCheckBox("avoidAttacks", "Avoid wave attacks", false)
94+
95+ addCheckBox("useSpellAttack", "Use attack spell", false)
96+ addTextEdit("attackSpell", "Attack spell", "")
97+ addCheckBox("useRuneAttack", "Use attack rune", false)
98+ addItem("attackRune", "Attack rune:", 0)
99+ addCheckBox("useGroupAttack", "Use group attack spell", false)
100+ addTextEdit("groupAttackSpell", "Group attack spell", "")
101+ addCheckBox("useGroupAttackRune", "Use group attack rune", false)
102+ addItem("groupAttackRune", "Group attack rune:", 0)
103+ addCheckBox("groupAttackIgnorePlayers", "Ignore players in group attack", false)
104+ addCheckBox("groupAttackIgnoreParty", "Ignore party in group attack", false)
105+end
A RD_Client/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.otui +134-0 View file
@@ -0,0 +1,134 @@
1+TargetBotCreatureEditorScrollBar < Panel
2+ height: 28
3+ margin-top: 3
4+
5+ Label
6+ id: text
7+ anchors.left: parent.left
8+ anchors.right: parent.right
9+ anchors.top: parent.top
10+ text-align: center
11+
12+ HorizontalScrollBar
13+ id: scroll
14+ anchors.left: parent.left
15+ anchors.right: parent.right
16+ anchors.top: prev.bottom
17+ margin-top: 3
18+ minimum: 0
19+ maximum: 10
20+ step: 1
21+
22+TargetBotCreatureEditorTextEdit < Panel
23+ height: 40
24+ margin-top: 7
25+
26+ Label
27+ id: text
28+ anchors.left: parent.left
29+ anchors.right: parent.right
30+ anchors.top: parent.top
31+ text-align: center
32+
33+ TextEdit
34+ id: textEdit
35+ anchors.left: parent.left
36+ anchors.right: parent.right
37+ anchors.top: prev.bottom
38+ margin-top: 5
39+ minimum: 0
40+ maximum: 10
41+ step: 1
42+
43+TargetBotCreatureEditorItem < Panel
44+ height: 34
45+ margin-top: 7
46+ margin-left: 25
47+ margin-right: 25
48+
49+ Label
50+ id: text
51+ anchors.left: parent.left
52+ anchors.verticalCenter: next.verticalCenter
53+
54+ BotItem
55+ id: item
56+ anchors.top: parent.top
57+ anchors.right: parent.right
58+
59+
60+TargetBotCreatureEditorCheckBox < BotSwitch
61+ height: 20
62+ margin-top: 7
63+
64+TargetBotCreatureEditorWindow < MainWindow
65+ text: TargetBot creature editor
66+ width: 500
67+ height: 600
68+
69+ Label
70+ anchors.left: parent.left
71+ anchors.right: parent.right
72+ anchors.top: parent.top
73+ text-align: center
74+ text: You can use * (any characters) and ? (any character) in target name
75+
76+ TextEdit
77+ id: name
78+ anchors.top: prev.bottom
79+ anchors.left: parent.left
80+ anchors.right: parent.right
81+ margin-left: 90
82+ margin-top: 5
83+
84+ Label
85+ anchors.verticalCenter: prev.verticalCenter
86+ anchors.left: parent.left
87+ text: Target name:
88+
89+ Panel
90+ id: left
91+ anchors.top: name.bottom
92+ anchors.left: parent.left
93+ anchors.right: parent.horizontalCenter
94+ margin-top: 5
95+ margin-left: 10
96+ margin-right: 10
97+ layout:
98+ type: verticalBox
99+ fit-children: true
100+
101+ Panel
102+ id: right
103+ anchors.top: name.bottom
104+ anchors.left: parent.horizontalCenter
105+ anchors.right: parent.right
106+ margin-top: 5
107+ margin-left: 10
108+ margin-right: 10
109+ layout:
110+ type: verticalBox
111+ fit-children: true
112+
113+ Button
114+ id: help
115+ !text: tr('Help & Tutorials')
116+ anchors.bottom: parent.bottom
117+ anchors.left: parent.left
118+ width: 150
119+ @onClick: g_platform.openUrl("http://bot.otclient.ovh/")
120+
121+ Button
122+ id: ok
123+ !text: tr('Ok')
124+ anchors.bottom: parent.bottom
125+ anchors.right: next.left
126+ margin-right: 10
127+ width: 60
128+
129+ Button
130+ id: cancel
131+ !text: tr('Cancel')
132+ anchors.bottom: parent.bottom
133+ anchors.right: parent.right
134+ width: 60
Top