├── _Loader.lua ├── cavebot ├── actions.lua ├── bank.lua ├── buy_supplies.lua ├── cavebot.lua ├── cavebot.otui ├── clear_tile.lua ├── config.lua ├── config.otui ├── d_withdraw.lua ├── depositor.lua ├── doors.lua ├── editor.lua ├── editor.otui ├── example_functions.lua ├── extension_template.lua ├── imbuing.lua ├── inbox_withdraw.lua ├── lure.lua ├── minimap.lua ├── pos_check.lua ├── recorder.lua ├── sell_all.lua ├── stand_lure.lua ├── supply_check.lua ├── tasker.lua ├── travel.lua ├── walking.lua └── withdraw.lua ├── targetbot ├── creature.lua ├── creature_attack.lua ├── creature_editor.lua ├── creature_editor.otui ├── creature_priority.lua ├── looting.lua ├── looting.otui ├── target.lua ├── target.otui └── walking.lua └── vBot ├── AttackBot.lua ├── AttackBot.otui ├── BotServer.lua ├── BotServer.otui ├── Conditions.lua ├── Conditions.otui ├── Containers.lua ├── Dropper.lua ├── Equipper.lua ├── HealBot.lua ├── HealBot.otui ├── Sio.lua ├── alarms.lua ├── alarms.otui ├── analyzer.lua ├── analyzer.otui ├── antiRs.lua ├── cast_food.lua ├── cavebot.lua ├── cavebot_control_panel.lua ├── combo.lua ├── combo.otui ├── configs.lua ├── depositer_config.lua ├── depositer_config.otui ├── depot_withdraw.lua ├── eat_food.lua ├── equip.lua ├── equipper.otui ├── exeta.lua ├── extras.lua ├── extras.otui ├── hold_target.lua ├── ingame_editor.lua ├── items.lua ├── main.lua ├── new_cavebot_lib.lua ├── new_healer.lua ├── new_healer.otui ├── npc_talk.lua ├── playerlist.lua ├── playerlist.otui ├── pushmax.lua ├── pushmax.otui ├── quiver_label.lua ├── quiver_manager.lua ├── siolist.otui ├── spy_level.lua ├── supplies.lua ├── supplies.otui ├── tools.lua ├── version.txt ├── vlib.lua └── xeno_menu.lua /_Loader.lua: -------------------------------------------------------------------------------- 1 | -- load all otui files, order doesn't matter 2 | local configName = modules.game_bot.contentsPanel.config:getCurrentOption().text 3 | 4 | local configFiles = g_resources.listDirectoryFiles("/bot/" .. configName .. "/vBot", true, false) 5 | for i, file in ipairs(configFiles) do 6 | local ext = file:split(".") 7 | if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then 8 | g_ui.importStyle(file) 9 | end 10 | end 11 | 12 | local function loadScript(name) 13 | return dofile("/vBot/" .. name .. ".lua") 14 | end 15 | 16 | -- here you can set manually order of scripts 17 | -- libraries should be loaded first 18 | local luaFiles = { 19 | "main", 20 | "items", 21 | "vlib", 22 | "new_cavebot_lib", 23 | "configs", -- do not change this and above 24 | "extras", 25 | "cavebot", 26 | "playerlist", 27 | "BotServer", 28 | "alarms", 29 | "Conditions", 30 | "Equipper", 31 | "pushmax", 32 | "combo", 33 | "HealBot", 34 | "new_healer", 35 | "AttackBot", -- last of major modules 36 | "ingame_editor", 37 | "Dropper", 38 | "Containers", 39 | "quiver_manager", 40 | "quiver_label", 41 | "tools", 42 | "antiRs", 43 | "depot_withdraw", 44 | "eat_food", 45 | "equip", 46 | "exeta", 47 | "analyzer", 48 | "spy_level", 49 | "supplies", 50 | "depositer_config", 51 | "npc_talk", 52 | "xeno_menu", 53 | "hold_target", 54 | "cavebot_control_panel" 55 | } 56 | 57 | for i, file in ipairs(luaFiles) do 58 | loadScript(file) 59 | end 60 | 61 | setDefaultTab("Main") 62 | UI.Separator() 63 | UI.Label("Private Scripts:") 64 | UI.Separator() 65 | -------------------------------------------------------------------------------- /cavebot/bank.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Bank = {} 2 | 3 | local balance = 0 4 | 5 | CaveBot.Extensions.Bank.setup = function() 6 | CaveBot.registerAction("bank", "#db5a5a", function(value, retries) 7 | local data = string.split(value, ",") 8 | local waitVal = 300 9 | local amount = 0 10 | local actionType 11 | local npcName 12 | local transferName 13 | local balanceLeft 14 | if #data ~= 3 and #data ~= 2 and #data ~= 4 then 15 | warn("CaveBot[Bank]: incorrect value!") 16 | return false 17 | else 18 | actionType = data[1]:trim():lower() 19 | npcName = data[2]:trim() 20 | if #data == 3 then 21 | amount = tonumber(data[3]:trim()) 22 | end 23 | if #data == 4 then 24 | transferName = data[3]:trim() 25 | balanceLeft = tonumber(data[4]:trim()) 26 | end 27 | end 28 | 29 | if actionType ~= "withdraw" and actionType ~= "deposit" and actionType ~= "transfer" then 30 | warn("CaveBot[Bank]: incorrect action type! should be withdraw/deposit/transfer, is: " .. actionType) 31 | return false 32 | elseif actionType == "withdraw" then 33 | local value = tonumber(amount) 34 | if not value then 35 | warn("CaveBot[Bank]: incorrect amount value! should be number, is: " .. amount) 36 | return false 37 | end 38 | end 39 | 40 | if retries > 5 then 41 | print("CaveBot[Bank]: too many tries, skipping") 42 | return false 43 | end 44 | 45 | local npc = getCreatureByName(npcName) 46 | if not npc then 47 | print("CaveBot[Bank]: NPC not found, skipping") 48 | return false 49 | end 50 | 51 | if not CaveBot.ReachNPC(npcName) then 52 | return "retry" 53 | end 54 | 55 | if actionType == "deposit" then 56 | CaveBot.Conversation("hi", "deposit all", "yes") 57 | CaveBot.delay(storage.extras.talkDelay*3) 58 | return true 59 | elseif actionType == "withdraw" then 60 | CaveBot.Conversation("hi", "withdraw", value, "yes") 61 | CaveBot.delay(storage.extras.talkDelay*4) 62 | return true 63 | else 64 | -- first check balance 65 | CaveBot.Conversation("hi", "balance") 66 | schedule(5000, function() 67 | local amountToTransfer = balance - balanceLeft 68 | if amountToTransfer <= 0 then 69 | warn("CaveBot[Bank] Not enough gold to transfer! proceeding") 70 | return false 71 | end 72 | CaveBot.Conversation("hi", "transfer", amountToTransfer, transferName, "yes") 73 | warn("CaveBot[Bank] transferred "..amountToTransfer.." gold to: "..transferName) 74 | end) 75 | CaveBot.delay(storage.extras.talkDelay*11) 76 | return true 77 | end 78 | end) 79 | 80 | CaveBot.Editor.registerAction("bank", "bank", { 81 | value="action, NPC name", 82 | title="Banker", 83 | description="action type(withdraw/deposit/transfer), NPC name, (if withdraw: amount|if transfer: name, balance left)", 84 | }) 85 | end 86 | 87 | 88 | onTalk(function(name, level, mode, text, channelId, pos) 89 | if mode == 51 and text:find("Your account balance is") then 90 | balance = getFirstNumberInText(text) 91 | end 92 | end) -------------------------------------------------------------------------------- /cavebot/buy_supplies.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.BuySupplies = {} 2 | 3 | CaveBot.Extensions.BuySupplies.setup = function() 4 | CaveBot.registerAction("BuySupplies", "#C300FF", function(value, retries) 5 | local possibleItems = {} 6 | 7 | local val = string.split(value, ",") 8 | local waitVal 9 | if #val == 0 or #val > 2 then 10 | warn("CaveBot[BuySupplies]: incorrect BuySupplies value") 11 | return false 12 | elseif #val == 2 then 13 | waitVal = tonumber(val[2]:trim()) 14 | end 15 | 16 | local npcName = val[1]:trim() 17 | local npc = getCreatureByName(npcName) 18 | if not npc then 19 | print("CaveBot[BuySupplies]: NPC not found") 20 | return false 21 | end 22 | 23 | if not waitVal and #val == 2 then 24 | warn("CaveBot[BuySupplies]: incorrect delay values!") 25 | elseif waitVal and #val == 2 then 26 | delay(waitVal) 27 | end 28 | 29 | if retries > 50 then 30 | print("CaveBot[BuySupplies]: Too many tries, can't buy") 31 | return false 32 | end 33 | 34 | if not CaveBot.ReachNPC(npcName) then 35 | return "retry" 36 | end 37 | 38 | if not NPC.isTrading() then 39 | CaveBot.OpenNpcTrade() 40 | CaveBot.delay(storage.extras.talkDelay*2) 41 | return "retry" 42 | end 43 | 44 | -- get items from npc 45 | local npcItems = NPC.getBuyItems() 46 | for i,v in pairs(npcItems) do 47 | table.insert(possibleItems, v.id) 48 | end 49 | 50 | for id, values in pairs(Supplies.getItemsData()) do 51 | id = tonumber(id) 52 | if table.find(possibleItems, id) then 53 | local max = values.max 54 | local current = player:getItemsCount(id) 55 | local toBuy = max - current 56 | 57 | if toBuy > 0 then 58 | toBuy = math.min(100, toBuy) 59 | 60 | NPC.buy(id, math.min(100, toBuy)) 61 | print("CaveBot[BuySupplies]: bought " .. toBuy .. "x " .. id) 62 | return "retry" 63 | end 64 | end 65 | end 66 | 67 | print("CaveBot[BuySupplies]: bought everything, proceeding") 68 | return true 69 | end) 70 | 71 | CaveBot.Editor.registerAction("buysupplies", "buy supplies", { 72 | value="NPC name", 73 | title="Buy Supplies", 74 | description="NPC Name, delay(in ms, optional)", 75 | }) 76 | end -------------------------------------------------------------------------------- /cavebot/cavebot.otui: -------------------------------------------------------------------------------- 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 49 | 50 | BotSwitch 51 | id: showConfig 52 | margin-top: 2 53 | 54 | $on: 55 | text: Hide config 56 | 57 | $!on: 58 | text: Show config -------------------------------------------------------------------------------- /cavebot/clear_tile.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.ClearTile = {} 2 | 3 | CaveBot.Extensions.ClearTile.setup = function() 4 | CaveBot.registerAction("ClearTile", "#00FFFF", function(value, retries) 5 | local data = string.split(value, ",") 6 | local pos = {x=tonumber(data[1]), y=tonumber(data[2]), z=tonumber(data[3])} 7 | local doors = false 8 | local stand = false 9 | local pPos = player:getPosition() 10 | 11 | 12 | for i, value in ipairs(data) do 13 | value = value:lower():trim() 14 | if value == "stand" then 15 | stand = true 16 | elseif value == "doors" then 17 | doors = true 18 | end 19 | end 20 | 21 | 22 | if not #pos == 3 then 23 | warn("CaveBot[ClearTile]: invalid value. It should be position (x,y,z), is: " .. value) 24 | return false 25 | end 26 | 27 | if retries >= 20 then 28 | print("CaveBot[ClearTile]: too many tries, can't clear it") 29 | return false -- tried 20 times, can't clear it 30 | end 31 | 32 | if getDistanceBetween(player:getPosition(), pos) == 0 then 33 | print("CaveBot[ClearTile]: tile reached, proceeding") 34 | return true 35 | end 36 | local tile = g_map.getTile(pos) 37 | if not tile then 38 | print("CaveBot[ClearTile]: can't find tile or tile is unreachable, skipping") 39 | return false 40 | end 41 | local tPos = tile:getPosition() 42 | 43 | -- no items on tile and walkability means we are done 44 | if tile:isWalkable() and tile:getTopUseThing():isNotMoveable() and not tile:hasCreature() and not doors then 45 | if stand then 46 | if not CaveBot.MatchPosition(tPos, 0) then 47 | CaveBot.GoTo(tPos, 0) 48 | return "retry" 49 | end 50 | end 51 | print("CaveBot[ClearTile]: tile clear, proceeding") 52 | return true 53 | end 54 | 55 | if not CaveBot.MatchPosition(tPos, 3) then 56 | CaveBot.GoTo(tPos, 3) 57 | return "retry" 58 | end 59 | 60 | if retries > 0 then 61 | delay(1100) 62 | end 63 | 64 | -- monster 65 | if tile:hasCreature() then 66 | local c = tile:getCreatures()[1] 67 | if c:isMonster() then 68 | attack(c) 69 | return "retry" 70 | end 71 | end 72 | 73 | -- moveable item 74 | local item = tile:getTopMoveThing() 75 | if item:isItem() then 76 | if item and not item:isNotMoveable() then 77 | print("CaveBot[ClearTile]: moving item... " .. item:getId().. " from tile") 78 | g_game.move(item, pPos, item:getCount()) 79 | return "retry" 80 | end 81 | end 82 | 83 | -- player 84 | 85 | -- push creature 86 | if tile:hasCreature() then 87 | local c = tile:getCreatures()[1] 88 | if c and c:isPlayer() then 89 | 90 | local candidates = {} 91 | for _, tile in ipairs(g_map.getTiles(posz())) do 92 | local tPos = tile:getPosition() 93 | if getDistanceBetween(c:getPosition(), tPos) == 1 and tPos ~= pPos and tile:isWalkable() then 94 | table.insert(candidates, tPos) 95 | end 96 | end 97 | 98 | if #candidates == 0 then 99 | print("CaveBot[ClearTile]: can't find tile to push, cannot clear way, skipping") 100 | return false 101 | else 102 | print("CaveBot[ClearTile]: pushing player... " .. c:getName() .. " out of the way") 103 | local pos = candidates[math.random(1,#candidates)] 104 | local tile = g_map.getTile(pos) 105 | tile:setText("here") 106 | schedule(500, function() tile:setText("") end) 107 | g_game.move(c, pos, 1) 108 | return "retry" 109 | end 110 | end 111 | end 112 | 113 | -- doors 114 | if doors then 115 | use(tile:getTopUseThing()) 116 | return "retry" 117 | end 118 | 119 | return "retry" 120 | end) 121 | 122 | CaveBot.Editor.registerAction("cleartile", "clear tile", { 123 | value=function() return posx() .. "," .. posy() .. "," .. posz() end, 124 | title="position of tile to clear", 125 | description="tile position (x,y,z), doors/stand - optional", 126 | multiline=false 127 | }) 128 | end -------------------------------------------------------------------------------- /cavebot/config.lua: -------------------------------------------------------------------------------- 1 | -- config for bot 2 | CaveBot.Config = {} 3 | CaveBot.Config.values = {} 4 | CaveBot.Config.default_values = {} 5 | CaveBot.Config.value_setters = {} 6 | 7 | CaveBot.Config.setup = function() 8 | CaveBot.Config.ui = UI.createWidget("CaveBotConfigPanel") 9 | local ui = CaveBot.Config.ui 10 | local add = CaveBot.Config.add 11 | 12 | add("ping", "Server ping", 100) 13 | add("walkDelay", "Walk delay", 10) 14 | add("mapClick", "Use map click", false) 15 | add("mapClickDelay", "Map click delay", 100) 16 | add("ignoreFields", "Ignore fields", false) 17 | add("skipBlocked", "Skip blocked path", false) 18 | add("useDelay", "Delay after use", 400) 19 | end 20 | 21 | CaveBot.Config.show = function() 22 | CaveBot.Config.ui:show() 23 | end 24 | 25 | CaveBot.Config.hide = function() 26 | CaveBot.Config.ui:hide() 27 | end 28 | 29 | CaveBot.Config.onConfigChange = function(configName, isEnabled, configData) 30 | for k, v in pairs(CaveBot.Config.default_values) do 31 | CaveBot.Config.value_setters[k](v) 32 | end 33 | if not configData then return end 34 | for k, v in pairs(configData) do 35 | if CaveBot.Config.value_setters[k] then 36 | CaveBot.Config.value_setters[k](v) 37 | end 38 | end 39 | end 40 | 41 | CaveBot.Config.save = function() 42 | return CaveBot.Config.values 43 | end 44 | 45 | CaveBot.Config.add = function(id, title, defaultValue) 46 | if CaveBot.Config.values[id] then 47 | return warn("Duplicated config key: " .. id) 48 | end 49 | 50 | local panel 51 | local setter -- sets value 52 | if type(defaultValue) == "number" then 53 | panel = UI.createWidget("CaveBotConfigNumberValuePanel", CaveBot.Config.ui) 54 | panel:setId(id) 55 | setter = function(value) 56 | CaveBot.Config.values[id] = value 57 | panel.value:setText(value, true) 58 | end 59 | setter(defaultValue) 60 | panel.value.onTextChange = function(widget, newValue) 61 | newValue = tonumber(newValue) 62 | if newValue then 63 | CaveBot.Config.values[id] = newValue 64 | CaveBot.save() 65 | end 66 | end 67 | elseif type(defaultValue) == "boolean" then 68 | panel = UI.createWidget("CaveBotConfigBooleanValuePanel", CaveBot.Config.ui) 69 | panel:setId(id) 70 | setter = function(value) 71 | CaveBot.Config.values[id] = value 72 | panel.value:setOn(value, true) 73 | end 74 | setter(defaultValue) 75 | panel.value.onClick = function(widget) 76 | widget:setOn(not widget:isOn()) 77 | CaveBot.Config.values[id] = widget:isOn() 78 | CaveBot.save() 79 | end 80 | else 81 | return warn("Invalid default value of config for key " .. id .. ", should be number or boolean") 82 | end 83 | 84 | panel.title:setText(tr(title) .. ":") 85 | 86 | CaveBot.Config.value_setters[id] = setter 87 | CaveBot.Config.values[id] = defaultValue 88 | CaveBot.Config.default_values[id] = defaultValue 89 | end 90 | 91 | CaveBot.Config.get = function(id) 92 | if CaveBot.Config.values[id] == nil then 93 | return warn("Invalid CaveBot.Config.get, id: " .. id) 94 | end 95 | return CaveBot.Config.values[id] 96 | end 97 | 98 | CaveBot.Config.set = function(id, value) 99 | local valueType = CaveBot.Config.get(id) 100 | local panel = CaveBot.Config.ui[id] 101 | 102 | if valueType == 'boolean' then 103 | CaveBot.Config.values[id] = value 104 | panel.value:setOn(value, true) 105 | CaveBot.save() 106 | else 107 | CaveBot.Config.values[id] = value 108 | panel.value:setText(value, true) 109 | CaveBot.save() 110 | end 111 | end -------------------------------------------------------------------------------- /cavebot/config.otui: -------------------------------------------------------------------------------- 1 | CaveBotConfigPanel < Panel 2 | id: cavebotEditor 3 | visible: false 4 | 5 | layout: 6 | type: verticalBox 7 | fit-children: true 8 | 9 | HorizontalSeparator 10 | margin-top: 5 11 | 12 | Label 13 | text-align: center 14 | text: CaveBot Config 15 | margin-top: 5 16 | 17 | CaveBotConfigNumberValuePanel < Panel 18 | height: 20 19 | margin-top: 5 20 | 21 | BotTextEdit 22 | id: value 23 | anchors.right: parent.right 24 | anchors.top: parent.top 25 | anchors.bottom: parent.bottom 26 | margin-right: 5 27 | width: 50 28 | 29 | Label 30 | id: title 31 | anchors.left: parent.left 32 | anchors.verticalCenter: prev.verticalCenter 33 | margin-left: 5 34 | 35 | CaveBotConfigBooleanValuePanel < Panel 36 | height: 20 37 | margin-top: 5 38 | 39 | BotSwitch 40 | id: value 41 | anchors.right: parent.right 42 | anchors.top: parent.top 43 | anchors.bottom: parent.bottom 44 | margin-right: 5 45 | width: 50 46 | 47 | $on: 48 | text: On 49 | 50 | $!on: 51 | text: Off 52 | 53 | Label 54 | id: title 55 | anchors.left: parent.left 56 | anchors.verticalCenter: prev.verticalCenter 57 | margin-left: 5 -------------------------------------------------------------------------------- /cavebot/d_withdraw.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.DWithdraw = {} 2 | 3 | CaveBot.Extensions.DWithdraw.setup = function() 4 | CaveBot.registerAction("dpwithdraw", "#002FFF", function(value, retries) 5 | local capLimit 6 | local data = string.split(value, ",") 7 | if retries > 600 then 8 | print("CaveBot[DepotWithdraw]: actions limit reached, proceeding") 9 | return false 10 | end 11 | local destContainer 12 | local depotContainer 13 | delay(70) 14 | 15 | -- input validation 16 | if not value or #data ~= 3 and #data ~= 4 then 17 | warn("CaveBot[DepotWithdraw]: incorrect value!") 18 | return false 19 | end 20 | local indexDp = tonumber(data[1]:trim()) 21 | local destName = data[2]:trim():lower() 22 | local destId = tonumber(data[3]:trim()) 23 | if #data == 4 then 24 | capLimit = tonumber(data[4]:trim()) 25 | end 26 | 27 | 28 | -- cap check 29 | if freecap() < (capLimit or 200) then 30 | for i, container in ipairs(getContainers()) do 31 | if container:getName():lower():find("depot") or container:getName():lower():find("locker") then 32 | g_game.close(container) 33 | end 34 | end 35 | print("CaveBot[DepotWithdraw]: cap limit reached, proceeding") 36 | return false 37 | end 38 | 39 | -- containers 40 | for i, container in ipairs(getContainers()) do 41 | local cName = container:getName():lower() 42 | if destName == cName then 43 | destContainer = container 44 | elseif cName:find("depot box") then 45 | depotContainer = container 46 | end 47 | end 48 | 49 | if not destContainer then 50 | print("CaveBot[DepotWithdraw]: container not found!") 51 | return false 52 | end 53 | 54 | if containerIsFull(destContainer) then 55 | for i, item in pairs(destContainer:getItems()) do 56 | if item:getId() == destId then 57 | g_game.open(item, destContainer) 58 | return "retry" 59 | end 60 | end 61 | end 62 | 63 | -- stash validation 64 | if depotContainer and #depotContainer:getItems() == 0 then 65 | print("CaveBot[DepotWithdraw]: all items withdrawn") 66 | g_game.close(depotContainer) 67 | return true 68 | end 69 | 70 | if containerIsFull(destContainer) then 71 | for i, item in pairs(destContainer:getItems()) do 72 | if item:getId() == destId then 73 | g_game.open(foundNextContainer, destContainer) 74 | return "retry" 75 | end 76 | end 77 | print("CaveBot[DepotWithdraw]: loot containers full!") 78 | return false 79 | end 80 | 81 | if not CaveBot.OpenDepotBox(indexDp) then 82 | return "retry" 83 | end 84 | 85 | CaveBot.PingDelay(2) 86 | 87 | for i, container in pairs(g_game.getContainers()) do 88 | if string.find(container:getName():lower(), "depot box") then 89 | for j, item in ipairs(container:getItems()) do 90 | statusMessage("[D_Withdraw] witdhrawing item: "..item:getId()) 91 | g_game.move(item, destContainer:getSlotPosition(destContainer:getItemsCount()), item:getCount()) 92 | return "retry" 93 | end 94 | end 95 | end 96 | 97 | return "retry" 98 | end) 99 | 100 | CaveBot.Editor.registerAction("dpwithdraw", "dpwithdraw", { 101 | value="1, shopping bag, 21411", 102 | title="Loot Withdraw", 103 | description="insert index, destination container name and it's ID", 104 | }) 105 | end -------------------------------------------------------------------------------- /cavebot/depositor.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Depositor = {} 2 | 3 | --local variables 4 | local destination = nil 5 | local lootTable = nil 6 | local reopenedContainers = false 7 | 8 | local function resetCache() 9 | reopenedContainers = false 10 | destination = nil 11 | lootTable = nil 12 | 13 | for i, container in ipairs(getContainers()) do 14 | if container:getName():lower():find("depot") or container:getName():lower():find("locker") then 15 | g_game.close(container) 16 | end 17 | end 18 | 19 | if storage.caveBot.backStop then 20 | storage.caveBot.backStop = false 21 | CaveBot.setOff() 22 | elseif storage.caveBot.backTrainers then 23 | storage.caveBot.backTrainers = false 24 | CaveBot.gotoLabel('toTrainers') 25 | elseif storage.caveBot.backOffline then 26 | storage.caveBot.backOffline = false 27 | CaveBot.gotoLabel('toOfflineTraining') 28 | end 29 | end 30 | 31 | 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" 32 | 33 | CaveBot.Extensions.Depositor.setup = function() 34 | CaveBot.registerAction("depositor", "#002FFF", function(value, retries) 35 | -- version check, TODO old tibia 36 | if g_game.getClientVersion() < 960 then 37 | resetCache() 38 | warn("CaveBot[Depositor]: unsupported Tibia version, will be added in near future") 39 | return false 40 | end 41 | 42 | -- loot list check 43 | lootTable = lootTable or CaveBot.GetLootItems() 44 | if #lootTable == 0 then 45 | print("CaveBot[Depositor]: no items in loot list. Wrong TargetBot Config? Proceeding") 46 | resetCache() 47 | return true 48 | end 49 | 50 | delay(70) 51 | 52 | -- backpacks etc 53 | if value:lower() == "yes" then 54 | if not reopenedContainers then 55 | CaveBot.CloseAllLootContainers() 56 | delay(3000) 57 | reopenedContainers = true 58 | return "retry" 59 | end 60 | -- open next backpacks if no more loot 61 | if not CaveBot.HasLootItems() then 62 | local lootContainers = CaveBot.GetLootContainers() 63 | for _, container in ipairs(getContainers()) do 64 | local cId = container:getContainerItem():getId() 65 | if table.find(lootContainers, cId) then 66 | for i, item in ipairs(container:getItems()) do 67 | if item:getId() == cId then 68 | g_game.open(item, container) 69 | delay(100) 70 | return "retry" 71 | end 72 | end 73 | end 74 | end 75 | -- couldn't find next container, so we done 76 | print("CaveBot[Depositor]: all items stashed, no backpack to open next, proceeding") 77 | CaveBot.CloseAllLootContainers() 78 | delay(3000) 79 | resetCache() 80 | return true 81 | end 82 | end 83 | 84 | -- first check items 85 | if retries == 0 then 86 | if not CaveBot.HasLootItems() then -- resource consuming function 87 | print("CaveBot[Depositor]: no items to stash, proceeding") 88 | resetCache() 89 | return true 90 | end 91 | end 92 | 93 | -- next check retries 94 | if retries > 400 then 95 | print("CaveBot[Depositor]: Depositor actions limit reached, proceeding") 96 | resetCache() 97 | return true 98 | end 99 | 100 | -- reaching and opening depot 101 | if not CaveBot.ReachAndOpenDepot() then 102 | return "retry" 103 | end 104 | 105 | -- add delay to prevent bugging 106 | CaveBot.PingDelay(2) 107 | 108 | -- prep time and stashing 109 | destination = destination or getContainerByName("Depot chest") 110 | if not destination then return "retry" end 111 | 112 | for _, container in pairs(getContainers()) do 113 | local name = container:getName():lower() 114 | if not name:find("depot") and not name:find("your inbox") then 115 | for _, item in pairs(container:getItems()) do 116 | local id = item:getId() 117 | if table.find(lootTable, id) then 118 | local index = getStashingIndex(id) or item:isStackable() and 1 or 0 119 | statusMessage("[Depositer] stashing item: " ..id.. " to depot: "..index+1) 120 | CaveBot.StashItem(item, index, destination) 121 | return "retry" 122 | end 123 | end 124 | end 125 | end 126 | 127 | -- we gucci 128 | resetCache() 129 | return true 130 | end) 131 | 132 | CaveBot.Editor.registerAction("depositor", "depositor", { 133 | value="no", 134 | title="Depositor", 135 | description=description, 136 | validation="(yes|Yes|YES|no|No|NO)" 137 | }) 138 | end -------------------------------------------------------------------------------- /cavebot/doors.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.OpenDoors = {} 2 | 3 | CaveBot.Extensions.OpenDoors.setup = function() 4 | CaveBot.registerAction("OpenDoors", "#00FFFF", function(value, retries) 5 | local pos = string.split(value, ",") 6 | local key = nil 7 | if #pos == 4 then 8 | key = tonumber(pos[4]) 9 | end 10 | if not pos[1] then 11 | warn("CaveBot[OpenDoors]: invalid value. It should be position (x,y,z), is: " .. value) 12 | return false 13 | end 14 | 15 | if retries >= 5 then 16 | print("CaveBot[OpenDoors]: too many tries, can't open doors") 17 | return false -- tried 5 times, can't open 18 | end 19 | 20 | pos = {x=tonumber(pos[1]), y=tonumber(pos[2]), z=tonumber(pos[3])} 21 | 22 | local doorTile 23 | if not doorTile then 24 | for i, tile in ipairs(g_map.getTiles(posz())) do 25 | if tile:getPosition().x == pos.x and tile:getPosition().y == pos.y and tile:getPosition().z == pos.z then 26 | doorTile = tile 27 | end 28 | end 29 | end 30 | 31 | if not doorTile then 32 | return false 33 | end 34 | 35 | if not doorTile:isWalkable() then 36 | if not key then 37 | use(doorTile:getTopUseThing()) 38 | delay(200) 39 | return "retry" 40 | else 41 | useWith(key, doorTile:getTopUseThing()) 42 | delay(200) 43 | return "retry" 44 | end 45 | else 46 | print("CaveBot[OpenDoors]: possible to cross, proceeding") 47 | return true 48 | end 49 | end) 50 | 51 | CaveBot.Editor.registerAction("opendoors", "open doors", { 52 | value=function() return posx() .. "," .. posy() .. "," .. posz() end, 53 | title="Door position", 54 | description="doors position (x,y,z) and key id (optional)", 55 | multiline=false, 56 | validation=[[\d{1,5},\d{1,5},\d{1,2}(?:,\d{1,5}$|$)]] 57 | }) 58 | end -------------------------------------------------------------------------------- /cavebot/editor.lua: -------------------------------------------------------------------------------- 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 warn("CaveBot editor warn: 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),randomness (in percent-optional)", 93 | multiline=false, 94 | validation="^[0-9]{1,10}$|^[0-9]{1,10},[0-9]{1,4}$" 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]+),?\\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("follow", { 129 | value="NPC name", 130 | title="Follow Creature", 131 | description="insert creature name to follow", 132 | multiline=false 133 | }) 134 | registerAction("npcsay", { 135 | value="text", 136 | title="NPC Say", 137 | description="Enter text to NPC say", 138 | multiline=false 139 | }) 140 | registerAction("function", { 141 | title="Edit bot function", 142 | multiline=true, 143 | value=CaveBot.Editor.ExampleFunctions[1][2], 144 | examples=CaveBot.Editor.ExampleFunctions, 145 | width=650 146 | }) 147 | 148 | ui.autoRecording.onClick = function() 149 | if ui.autoRecording:isOn() then 150 | CaveBot.Recorder.disable() 151 | else 152 | CaveBot.Recorder.enable() 153 | end 154 | end 155 | 156 | -- callbacks 157 | onPlayerPositionChange(function(pos) 158 | ui.pos:setText("Position: " .. pos.x .. ", " .. pos.y .. ", " .. pos.z) 159 | end) 160 | ui.pos:setText("Position: " .. posx() .. ", " .. posy() .. ", " .. posz()) 161 | end 162 | 163 | CaveBot.Editor.show = function() 164 | CaveBot.Editor.ui:show() 165 | end 166 | 167 | 168 | CaveBot.Editor.hide = function() 169 | CaveBot.Editor.ui:hide() 170 | end 171 | 172 | CaveBot.Editor.edit = function(action, value, callback) -- callback = function(action, value) 173 | local params = CaveBot.Editor.Actions[action] 174 | if not params then return end 175 | if not value then 176 | if type(params.value) == 'function' then 177 | value = params.value() 178 | elseif type(params.value) == 'string' then 179 | value = params.value 180 | end 181 | end 182 | 183 | UI.EditorWindow(value, params, function(newText) 184 | callback(action, newText) 185 | end) 186 | end 187 | -------------------------------------------------------------------------------- /cavebot/editor.otui: -------------------------------------------------------------------------------- 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/") 45 | -------------------------------------------------------------------------------- /cavebot/example_functions.lua: -------------------------------------------------------------------------------- 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("Check for PZ and wait until dropped", [[ 24 | if retries > 25 or not isPzLocked() then 25 | return true 26 | else 27 | if isPoisioned() then 28 | say("exana pox") 29 | end 30 | if isPzLocked() then 31 | delay(8000) 32 | end 33 | return "retry" 34 | end 35 | ]]) 36 | 37 | addExampleFunction("Check for stamina and imbues", [[ 38 | if stamina() < 900 or player:getSkillLevel(11) == 0 then CaveBot.setOff() return false else return true end 39 | ]]) 40 | 41 | addExampleFunction("buy 200 mana potion from npc Eryn", [[ 42 | --buy 200 mana potions 43 | local npc = getCreatureByName("Eryn") 44 | if not npc then 45 | return false 46 | end 47 | if retries > 10 then 48 | return false 49 | end 50 | local pos = player:getPosition() 51 | local npcPos = npc:getPosition() 52 | if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then 53 | autoWalk(npcPos, {precision=3}) 54 | delay(300) 55 | return "retry" 56 | end 57 | if not NPC.isTrading() then 58 | NPC.say("hi") 59 | NPC.say("trade") 60 | delay(200) 61 | return "retry" 62 | end 63 | NPC.buy(268, 100) 64 | schedule(1000, function() 65 | -- buy again in 1s 66 | NPC.buy(268, 100) 67 | NPC.closeTrade() 68 | NPC.say("bye") 69 | end) 70 | delay(1200) 71 | return true 72 | ]]) 73 | 74 | addExampleFunction("Say hello 5 times with some delay", [[ 75 | --say hello 76 | if retries > 5 then 77 | return true -- finish 78 | end 79 | say("hello") 80 | delay(100 + retries * 100) 81 | return "retry" 82 | ]]) 83 | 84 | addExampleFunction("Disable TargetBot", [[ 85 | TargetBot.setOff() 86 | return true 87 | ]]) 88 | 89 | addExampleFunction("Enable TargetBot", [[ 90 | TargetBot.setOn() 91 | return true 92 | ]]) 93 | 94 | addExampleFunction("Enable TargetBot luring", [[ 95 | TargetBot.enableLuring() 96 | return true 97 | ]]) 98 | 99 | addExampleFunction("Disable TargetBot luring", [[ 100 | TargetBot.disableLuring() 101 | return true 102 | ]]) 103 | 104 | addExampleFunction("Logout", [[ 105 | g_game.safeLogout() 106 | delay(1000) 107 | return "retry" 108 | ]]) 109 | 110 | addExampleFunction("Close Loot Containers", [[ 111 | CaveBot.CloseAllLootContainers() 112 | delay(3000) 113 | return true 114 | ]]) -------------------------------------------------------------------------------- /cavebot/extension_template.lua: -------------------------------------------------------------------------------- 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 59 | -------------------------------------------------------------------------------- /cavebot/imbuing.lua: -------------------------------------------------------------------------------- 1 | -- imbuing window should be handled separatly 2 | -- reequiping should be handled separatly (ie. equipment manager) 3 | 4 | CaveBot.Extensions.Imbuing = {} 5 | 6 | local SHRINES = {25060, 25061, 25182, 25183} 7 | local currentIndex = 1 8 | local shrine = nil 9 | local item = nil 10 | local currentId = 0 11 | local triedToTakeOff = false 12 | local destination = nil 13 | 14 | local function reset() 15 | EquipManager.setOn() 16 | shrine = nil 17 | currentIndex = 1 18 | item = nil 19 | currentId = 0 20 | triedToTakeOff = false 21 | destination = nil 22 | end 23 | 24 | CaveBot.Extensions.Imbuing.setup = function() 25 | CaveBot.registerAction("imbuing", "red", function(value, retries) 26 | local data = string.split(value, ",") 27 | local ids = {} 28 | 29 | if #data == 0 and value ~= 'name' then 30 | warn("CaveBot[Imbuing] no items added, proceeding") 31 | reset() 32 | return false 33 | end 34 | 35 | -- setting of equipment manager so it wont disturb imbuing process 36 | EquipManager.setOff() 37 | 38 | if value == 'name' then 39 | local imbuData = AutoImbueTable[player:getName()] 40 | for id, imbues in pairs(imbuData) do 41 | table.insert(ids, id) 42 | end 43 | else 44 | -- convert to number 45 | for i, id in ipairs(data) do 46 | id = tonumber(id) 47 | if not table.find(ids, id) then 48 | table.insert(ids, id) 49 | end 50 | end 51 | end 52 | 53 | -- all items imbued, can proceed 54 | if currentIndex > #ids then 55 | warn("CaveBot[Imbuing] used shrine on all items, proceeding") 56 | reset() 57 | return true 58 | end 59 | 60 | for _, tile in ipairs(g_map.getTiles(posz())) do 61 | for _, item in ipairs(tile:getItems()) do 62 | local id = item:getId() 63 | if table.find(SHRINES, id) then 64 | shrine = item 65 | break 66 | end 67 | end 68 | end 69 | 70 | -- if not shrine 71 | if not shrine then 72 | warn("CaveBot[Imbuing] shrine not found! proceeding") 73 | reset() 74 | return false 75 | end 76 | 77 | destination = shrine:getPosition() 78 | 79 | currentId = ids[currentIndex] 80 | item = findItem(currentId) 81 | 82 | -- maybe equipped? try to take off 83 | if not item then 84 | -- did try before, still not found so item is unavailable 85 | if triedToTakeOff then 86 | warn("CaveBot[Imbuing] item not found! skipping: "..currentId) 87 | triedToTakeOff = false 88 | currentIndex = currentIndex + 1 89 | return "retry" 90 | end 91 | triedToTakeOff = true 92 | g_game.equipItemId(currentId) 93 | delay(1000) 94 | return "retry" 95 | end 96 | 97 | -- we are past unequiping so just in case we were forced before, reset var 98 | triedToTakeOff = false 99 | 100 | -- reaching shrine 101 | if not CaveBot.MatchPosition(destination, 1) then 102 | CaveBot.GoTo(destination, 1) 103 | delay(200) 104 | return "retry" 105 | end 106 | 107 | useWith(shrine, item) 108 | currentIndex = currentIndex + 1 109 | warn("CaveBot[Imbuing] Using shrine on item: "..currentId) 110 | delay(4000) 111 | return "retry" 112 | end) 113 | 114 | CaveBot.Editor.registerAction("imbuing", "imbuing", { 115 | value="name", 116 | title="Auto Imbuing", 117 | description="insert below item ids to be imbued, separated by comma\nor 'name' to load from file", 118 | }) 119 | end -------------------------------------------------------------------------------- /cavebot/inbox_withdraw.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.InWithdraw = {} 2 | 3 | CaveBot.Extensions.InWithdraw.setup = function() 4 | CaveBot.registerAction("inwithdraw", "#002FFF", function(value, retries) 5 | local data = string.split(value, ",") 6 | local withdrawId 7 | local amount 8 | 9 | -- validation 10 | if #data ~= 2 then 11 | warn("CaveBot[InboxWithdraw]: incorrect withdraw value") 12 | return false 13 | else 14 | withdrawId = tonumber(data[1]) 15 | amount = tonumber(data[2]) 16 | end 17 | 18 | local currentAmount = itemAmount(withdrawId) 19 | 20 | if currentAmount >= amount then 21 | print("CaveBot[InboxWithdraw]: enough items, proceeding") 22 | return true 23 | end 24 | 25 | if retries > 400 then 26 | print("CaveBot[InboxWithdraw]: actions limit reached, proceeding") 27 | return true 28 | end 29 | 30 | -- actions 31 | local inboxContainer = getContainerByName("your inbox") 32 | delay(100) 33 | if not inboxContainer then 34 | if not CaveBot.ReachAndOpenInbox() then 35 | return "retry" 36 | end 37 | end 38 | local inboxAmount = 0 39 | if not inboxContainer then 40 | return "retry" 41 | end 42 | for i, item in pairs(inboxContainer:getItems()) do 43 | if item:getId() == withdrawId then 44 | inboxAmount = inboxAmount + item:getCount() 45 | end 46 | end 47 | if inboxAmount == 0 then 48 | warn("CaveBot[InboxWithdraw]: not enough items in inbox container, proceeding") 49 | g_game.close(inboxContainer) 50 | return true 51 | end 52 | 53 | local destination 54 | for i, container in pairs(getContainers()) do 55 | 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 56 | destination = container 57 | end 58 | end 59 | 60 | if not destination then 61 | print("CaveBot[InboxWithdraw]: couldn't find proper destination container, skipping") 62 | g_game.close(inboxContainer) 63 | return false 64 | end 65 | 66 | CaveBot.PingDelay(2) 67 | 68 | for i, container in pairs(getContainers()) do 69 | if string.find(container:getName():lower(), "your inbox") then 70 | for j, item in pairs(container:getItems()) do 71 | if item:getId() == withdrawId then 72 | if item:isStackable() then 73 | g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), math.min(item:getCount(), (amount - currentAmount))) 74 | return "retry" 75 | else 76 | g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), 1) 77 | return "retry" 78 | end 79 | return "retry" 80 | end 81 | end 82 | end 83 | end 84 | end) 85 | 86 | CaveBot.Editor.registerAction("inwithdraw", "in withdraw", { 87 | value="id,amount", 88 | title="Withdraw Items", 89 | description="insert item id and amount", 90 | }) 91 | end -------------------------------------------------------------------------------- /cavebot/lure.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Lure = {} 2 | 3 | CaveBot.Extensions.Lure.setup = function() 4 | CaveBot.registerAction("lure", "#FF0090", function(value, retries) 5 | value = value:lower() 6 | if value == "start" then 7 | TargetBot.setOff() 8 | elseif value == "stop" then 9 | TargetBot.setOn() 10 | elseif value == "toggle" then 11 | if TargetBot.isOn() then 12 | TargetBot.setOff() 13 | else 14 | TargetBot.setOn() 15 | end 16 | else 17 | warn("incorrect lure value!") 18 | end 19 | return true 20 | end) 21 | 22 | CaveBot.Editor.registerAction("lure", "lure", { 23 | value="toggle", 24 | title="Lure", 25 | description="TargetBot: start, stop, toggle", 26 | multiline=false, 27 | validation=[[(start|stop|toggle)$]] 28 | }) 29 | end -------------------------------------------------------------------------------- /cavebot/minimap.lua: -------------------------------------------------------------------------------- 1 | local minimap = modules.game_minimap.minimapWidget 2 | 3 | minimap.onMouseRelease = function(widget,pos,button) 4 | if not minimap.allowNextRelease then return true end 5 | minimap.allowNextRelease = false 6 | 7 | local mapPos = minimap:getTilePosition(pos) 8 | if not mapPos then return end 9 | 10 | if button == 1 then 11 | local player = g_game.getLocalPlayer() 12 | if minimap.autowalk then 13 | player:autoWalk(mapPos) 14 | end 15 | return true 16 | elseif button == 2 then 17 | local menu = g_ui.createWidget('PopupMenu') 18 | menu:setId("minimapMenu") 19 | menu:setGameMenu(true) 20 | menu:addOption(tr('Create mark'), function() minimap:createFlagWindow(mapPos) end) 21 | menu:addOption(tr('Add CaveBot GoTo'), function() CaveBot.addAction("goto", mapPos.x .. "," .. mapPos.y .. "," .. mapPos.z, true) CaveBot.save() end) 22 | menu:display(pos) 23 | return true 24 | end 25 | return false 26 | end -------------------------------------------------------------------------------- /cavebot/pos_check.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.PosCheck = {} 2 | 3 | local posCheckRetries = 0 4 | CaveBot.Extensions.PosCheck.setup = function() 5 | CaveBot.registerAction("PosCheck", "#00FFFF", function(value, retries) 6 | local tilePos 7 | local data = string.split(value, ",") 8 | if #data ~= 5 then 9 | warn("wrong travel format, should be: label, distance, x, y, z") 10 | return false 11 | end 12 | 13 | local tilePos = player:getPosition() 14 | 15 | tilePos.x = tonumber(data[3]) 16 | tilePos.y = tonumber(data[4]) 17 | tilePos.z = tonumber(data[5]) 18 | 19 | if posCheckRetries > 10 then 20 | posCheckRetries = 0 21 | print("CaveBot[CheckPos]: waypoints locked, too many tries, unclogging cavebot and proceeding") 22 | return false 23 | elseif (tilePos.z == player:getPosition().z) and (getDistanceBetween(player:getPosition(), tilePos) <= tonumber(data[2])) then 24 | posCheckRetries = 0 25 | print("CaveBot[CheckPos]: position reached, proceeding") 26 | return true 27 | else 28 | posCheckRetries = posCheckRetries + 1 29 | if data[1] == "last" then 30 | CaveBot.gotoFirstPreviousReachableWaypoint() 31 | print("CaveBot[CheckPos]: position not-reached, going back to first reachable waypoint.") 32 | return false 33 | else 34 | CaveBot.gotoLabel(data[1]) 35 | print("CaveBot[CheckPos]: position not-reached, going back to label: " .. data[1]) 36 | return false 37 | end 38 | end 39 | end) 40 | 41 | CaveBot.Editor.registerAction("poscheck", "pos check", { 42 | value=function() return "last" .. "," .. "10" .. "," .. posx() .. "," .. posy() .. "," .. posz() end, 43 | title="Location Check", 44 | description="label name, accepted dist from coordinates, x, y, z", 45 | multiline=false, 46 | }) 47 | end -------------------------------------------------------------------------------- /cavebot/recorder.lua: -------------------------------------------------------------------------------- 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 | local function addStairs(pos) 13 | CaveBot.addAction("goto", pos.x .. "," .. pos.y .. "," .. pos.z .. ",0", true) 14 | lastPos = pos 15 | end 16 | 17 | onPlayerPositionChange(function(newPos, oldPos) 18 | if CaveBot.isOn() or not isEnabled then return end 19 | if not lastPos then 20 | -- first step 21 | addPosition(oldPos) 22 | elseif newPos.z ~= oldPos.z or math.abs(oldPos.x - newPos.x) > 1 or math.abs(oldPos.y - newPos.y) > 1 then 23 | -- stairs/teleport 24 | addStairs(oldPos) 25 | elseif math.max(math.abs(lastPos.x - newPos.x), math.abs(lastPos.y - newPos.y)) > 5 then 26 | -- 5 steps from last pos 27 | addPosition(newPos) 28 | end 29 | end) 30 | 31 | onUse(function(pos, itemId, stackPos, subType) 32 | if CaveBot.isOn() or not isEnabled then return end 33 | if pos.x ~= 0xFFFF then 34 | lastPos = pos 35 | CaveBot.addAction("use", pos.x .. "," .. pos.y .. "," .. pos.z, true) 36 | end 37 | end) 38 | 39 | onUseWith(function(pos, itemId, target, subType) 40 | if CaveBot.isOn() or not isEnabled then return end 41 | if not target:isItem() then return end 42 | local targetPos = target:getPosition() 43 | if targetPos.x == 0xFFFF then return end 44 | lastPos = pos 45 | CaveBot.addAction("usewith", itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z, true) 46 | end) 47 | end 48 | 49 | CaveBot.Recorder.isOn = function() 50 | return isEnabled 51 | end 52 | 53 | CaveBot.Recorder.enable = function() 54 | CaveBot.setOff() 55 | if isEnabled == nil then 56 | setup() 57 | end 58 | CaveBot.Editor.ui.autoRecording:setOn(true) 59 | isEnabled = true 60 | lastPos = nil 61 | end 62 | 63 | CaveBot.Recorder.disable = function() 64 | if isEnabled == true then 65 | isEnabled = false 66 | end 67 | CaveBot.Editor.ui.autoRecording:setOn(false) 68 | CaveBot.save() 69 | end -------------------------------------------------------------------------------- /cavebot/sell_all.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.SellAll = {} 2 | 3 | local sellAllCap = 0 4 | CaveBot.Extensions.SellAll.setup = function() 5 | CaveBot.registerAction("SellAll", "#C300FF", function(value, retries) 6 | local val = string.split(value, ",") 7 | local wait 8 | 9 | -- table formatting 10 | for i, v in ipairs(val) do 11 | v = v:trim() 12 | v = tonumber(v) or v 13 | val[i] = v 14 | end 15 | 16 | if table.find(val, "yes", true) then 17 | wait = true 18 | end 19 | 20 | local npcName = val[1] 21 | local npc = getCreatureByName(npcName) 22 | if not npc then 23 | print("CaveBot[SellAll]: NPC not found! skipping") 24 | return false 25 | end 26 | 27 | if retries > 10 then 28 | print("CaveBot[SellAll]: can't sell, skipping") 29 | return false 30 | end 31 | 32 | if freecap() == sellAllCap then 33 | sellAllCap = 0 34 | print("CaveBot[SellAll]: Sold everything, proceeding") 35 | return true 36 | end 37 | 38 | delay(800) 39 | if not CaveBot.ReachNPC(npcName) then 40 | return "retry" 41 | end 42 | 43 | if not NPC.isTrading() then 44 | CaveBot.OpenNpcTrade() 45 | delay(storage.extras.talkDelay*2) 46 | return "retry" 47 | else 48 | sellAllCap = freecap() 49 | end 50 | 51 | storage.cavebotSell = storage.cavebotSell or {} 52 | for i, item in ipairs(storage.cavebotSell) do 53 | local data = type(item) == 'number' and item or item.id 54 | if not table.find(val, data) then 55 | table.insert(val, data) 56 | end 57 | end 58 | 59 | table.dump(val) 60 | 61 | modules.game_npctrade.sellAll(wait, val) 62 | if wait then 63 | print("CaveBot[SellAll]: Sold All with delay") 64 | else 65 | print("CaveBot[SellAll]: Sold All without delay") 66 | end 67 | 68 | return "retry" 69 | end) 70 | 71 | CaveBot.Editor.registerAction("sellall", "sell all", { 72 | value="NPC", 73 | title="Sell All", 74 | description="NPC Name, 'yes' if sell with delay, exceptions: id separated by comma", 75 | }) 76 | end -------------------------------------------------------------------------------- /cavebot/stand_lure.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.StandLure = {} 2 | local enable = nil 3 | 4 | local function modPos(dir) 5 | local y = 0 6 | local x = 0 7 | 8 | if dir == 0 then 9 | y = -1 10 | elseif dir == 1 then 11 | x = 1 12 | elseif dir == 2 then 13 | y = 1 14 | elseif dir == 3 then 15 | x = -1 16 | elseif dir == 4 then 17 | y = -1 18 | x = 1 19 | elseif dir == 5 then 20 | y = 1 21 | x = 1 22 | elseif dir == 6 then 23 | y = 1 24 | x = -1 25 | elseif dir == 7 then 26 | y = -1 27 | x = -1 28 | end 29 | 30 | return {x, y} 31 | end 32 | local function reset(delay) 33 | if type(Supplies.hasEnough()) == 'table' then 34 | return 35 | end 36 | delay = delay or 0 37 | CaveBot.delay(delay) 38 | if delay == nil then 39 | enable = nil 40 | end 41 | end 42 | 43 | local resetRetries = false 44 | CaveBot.Extensions.StandLure.setup = function() 45 | CaveBot.registerAction( 46 | "rushlure", 47 | "#FF0090", 48 | function(value, retries) 49 | local nextPos = nil 50 | local data = string.split(value, ",") 51 | if not data[1] then 52 | warn("Invalid cavebot lure action value. It should be position (x,y,z), delay(ms) is: " .. value) 53 | return false 54 | end 55 | 56 | if type(Supplies.hasEnough()) == 'table' then -- do not execute if no supplies 57 | return false 58 | end 59 | 60 | local pos = {x = tonumber(data[1]), y = tonumber(data[2]), z = tonumber(data[3])} 61 | 62 | local delayTime = data[4] and tonumber(data[4]) or 1000 63 | if not data[5] then 64 | enable = nil 65 | elseif data[5] == "yes" then 66 | enable = true 67 | else 68 | enable = false 69 | end 70 | 71 | delay(100) 72 | 73 | if retries > 50 and not resetRetries then 74 | reset() 75 | warn("[Rush Lure] Too many tries, can't reach position") 76 | return false -- can't stand on tile 77 | end 78 | 79 | if resetRetries then 80 | resetRetries = false 81 | end 82 | 83 | if distanceFromPlayer(pos) > 30 then 84 | reset() 85 | return false -- not reachable 86 | end 87 | 88 | local playerPos = player:getPosition() 89 | local pathWithoutMonsters = findPath(playerPos, pos, 30, { ignoreFields = true, ignoreNonPathable = true, ignoreCreatures = true, precision = 0}) 90 | local pathWithMonsters = findPath(playerPos, pos, maxDist, { ignoreFields = true, ignoreNonPathable = true, ignoreCreatures = false, precision = 0 }) 91 | 92 | if not pathWithoutMonsters then 93 | reset() 94 | warn("[Rush Lure] No possible path to reach position, skipping.") 95 | return false -- spot is unreachable 96 | elseif pathWithoutMonsters and not pathWithMonsters then 97 | local foundMonster = false 98 | for i, dir in ipairs(pathWithoutMonsters) do 99 | local dirs = modPos(dir) 100 | nextPos = nextPos or playerPos 101 | nextPos.x = nextPos.x + dirs[1] 102 | nextPos.y = nextPos.y + dirs[2] 103 | 104 | 105 | local tile = g_map.getTile(nextPos) 106 | if tile then 107 | if tile:hasCreature() then 108 | local creature = tile:getCreatures()[1] 109 | local hppc = creature:getHealthPercent() 110 | if creature:isMonster() and (hppc and hppc > 0) and (oldTibia or creature:getType() < 3) then 111 | -- real blocking creature can not meet those conditions - ie. it could be player, so just in case check if the next creature is reachable 112 | local path = findPath(playerPos, creature:getPosition(), 7, { ignoreNonPathable = true, precision = 1 }) 113 | if path then 114 | creature:setMarked('#00FF00') 115 | if g_game.getAttackingCreature() ~= creature then 116 | attack(creature) 117 | end 118 | g_game.setChaseMode(1) 119 | resetRetries = true -- reset retries, we are trying to unclog the cavebot 120 | delay(100) 121 | return "retry" 122 | end 123 | end 124 | end 125 | end 126 | end 127 | 128 | if not g_game.getAttackingCreature() then 129 | reset() 130 | warn("[Rush Lure] No path, no blocking monster, skipping.") 131 | return false -- no other way 132 | end 133 | end 134 | 135 | -- reaching position, delay targetbot in process 136 | if not CaveBot.MatchPosition(pos, 0) then 137 | TargetBot.delay(300) 138 | CaveBot.walkTo(pos, 30, { ignoreCreatures = false, ignoreFields = true, ignoreNonPathable = true, precision = 0}) 139 | delay(100) 140 | resetRetries = true 141 | return "retry" 142 | end 143 | 144 | TargetBot.setOn() 145 | reset(delayTime) 146 | return true 147 | end 148 | ) 149 | 150 | CaveBot.Editor.registerAction( 151 | "rushlure", 152 | "rush lure", 153 | { 154 | value = function() 155 | return posx() .. "," .. posy() .. "," .. posz() .. ",1000" 156 | end, 157 | title = "Stand Lure", 158 | description = "Run to position(x,y,z), delay(ms), targetbot on/off (yes/no)", 159 | multiline = false, 160 | validation = [[\d{1,5},\d{1,5},\d{1,2},\d{1,5}(?:,(yes|no)$|$)]] 161 | } 162 | ) 163 | end 164 | 165 | local next = false 166 | schedule(5, function() -- delay because cavebot.lua is loaded after this file 167 | modules.game_bot.connect(CaveBotList(), { 168 | onChildFocusChange = function(widget, newChild, oldChild) 169 | 170 | if oldChild and oldChild.action == "rushlure" then 171 | next = true 172 | return 173 | end 174 | 175 | if next then 176 | if enable then 177 | TargetBot.setOn() 178 | elseif enable == false then 179 | TargetBot.setOff() 180 | end 181 | 182 | enable = nil -- reset 183 | next = false 184 | end 185 | end}) 186 | end) -------------------------------------------------------------------------------- /cavebot/supply_check.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.SupplyCheck = {} 2 | 3 | local supplyRetries = 0 4 | local missedChecks = 0 5 | local rawRound = 0 6 | local time = now 7 | vBot.CaveBotData = 8 | vBot.CaveBotData or 9 | { 10 | refills = 0, 11 | rounds = 0, 12 | time = {}, 13 | lastRefill = os.time(), 14 | refillTime = {} 15 | } 16 | 17 | local function setCaveBotData(hunting) 18 | if hunting then 19 | supplyRetries = supplyRetries + 1 20 | else 21 | supplyRetries = 0 22 | table.insert(vBot.CaveBotData.refillTime, os.difftime(os.time() - vBot.CaveBotData.lastRefill)) 23 | vBot.CaveBotData.lastRefill = os.time() 24 | vBot.CaveBotData.refills = vBot.CaveBotData.refills + 1 25 | end 26 | 27 | table.insert(vBot.CaveBotData.time, rawRound) 28 | vBot.CaveBotData.rounds = vBot.CaveBotData.rounds + 1 29 | missedChecks = 0 30 | end 31 | 32 | CaveBot.Extensions.SupplyCheck.setup = function() 33 | CaveBot.registerAction( 34 | "supplyCheck", 35 | "#db5a5a", 36 | function(value) 37 | local data = string.split(value, ",") 38 | local round = 0 39 | rawRound = 0 40 | local label = data[1]:trim() 41 | local pos = nil 42 | if #data == 4 then 43 | pos = {x = tonumber(data[2]), y = tonumber(data[3]), z = tonumber(data[4])} 44 | end 45 | 46 | if pos then 47 | if missedChecks >= 4 then 48 | missedChecks = 0 49 | supplyRetries = 0 50 | print("CaveBot[SupplyCheck]: Missed 5 supply checks, proceeding with waypoints") 51 | return true 52 | end 53 | if getDistanceBetween(player:getPosition(), pos) > 10 then 54 | missedChecks = missedChecks + 1 55 | print("CaveBot[SupplyCheck]: Missed supply check! " .. 5 - missedChecks .. " tries left before skipping.") 56 | return CaveBot.gotoLabel(label) 57 | end 58 | end 59 | 60 | if time then 61 | rawRound = math.ceil((now - time) / 1000) 62 | round = rawRound .. "s" 63 | else 64 | round = "" 65 | end 66 | time = now 67 | 68 | local softCount = itemAmount(6529) + itemAmount(3549) 69 | local supplyData = Supplies.hasEnough() 70 | local supplyInfo = Supplies.getAdditionalData() 71 | 72 | if storage.caveBot.forceRefill then 73 | print("CaveBot[SupplyCheck]: User forced, going back on refill. Last round took: " .. round) 74 | storage.caveBot.forceRefill = false 75 | supplyRetries = 0 76 | missedChecks = 0 77 | return false 78 | elseif storage.caveBot.backStop then 79 | print("CaveBot[SupplyCheck]: User forced, going back to city and turning off CaveBot. Last round took: " .. round) 80 | supplyRetries = 0 81 | missedChecks = 0 82 | return false 83 | elseif storage.caveBot.backTrainers then 84 | print("CaveBot[SupplyCheck]: User forced, going back to city, then on trainers. Last round took: " .. round) 85 | supplyRetries = 0 86 | missedChecks = 0 87 | return false 88 | elseif storage.caveBot.backOffline then 89 | print("CaveBot[SupplyCheck]: User forced, going back to city, then on offline training. Last round took: " .. round) 90 | supplyRetries = 0 91 | missedChecks = 0 92 | return false 93 | elseif supplyRetries > (storage.extras.huntRoutes or 50) then 94 | print("CaveBot[SupplyCheck]: Round limit reached, going back on refill. Last round took: " .. round) 95 | setCaveBotData() 96 | return false 97 | elseif (supplyInfo.imbues.enabled and player:getSkillLevel(11) == 0) then 98 | print("CaveBot[SupplyCheck]: Imbues ran out. Going on refill. Last round took: " .. round) 99 | setCaveBotData() 100 | return false 101 | elseif (supplyInfo.stamina.enabled and stamina() < tonumber(supplyInfo.stamina.value)) then 102 | print("CaveBot[SupplyCheck]: Stamina ran out. Going on refill. Last round took: " .. round) 103 | setCaveBotData() 104 | return false 105 | elseif (supplyInfo.softBoots.enabled and softCount < 1) then 106 | print("CaveBot[SupplyCheck]: No soft boots left. Going on refill. Last round took: " .. round) 107 | setCaveBotData() 108 | return false 109 | elseif type(supplyData) == "table" then 110 | print("CaveBot[SupplyCheck]: Not enough item: " .. supplyData.id .. "(only " .. supplyData.amount .. " left). Going on refill. Last round took: " .. round) 111 | setCaveBotData() 112 | return false 113 | elseif (supplyInfo.capacity.enabled and freecap() < tonumber(supplyInfo.capacity.value)) then 114 | print("CaveBot[SupplyCheck]: Not enough capacity. Going on refill. Last round took: " .. round) 115 | setCaveBotData() 116 | return false 117 | else 118 | print("CaveBot[SupplyCheck]: Enough supplies. Hunting. Round (" .. supplyRetries .. "/" .. (storage.extras.huntRoutes or 50) .. "). Last round took: " .. round) 119 | setCaveBotData(true) 120 | return CaveBot.gotoLabel(label) 121 | end 122 | end 123 | ) 124 | 125 | CaveBot.Editor.registerAction( 126 | "supplycheck", 127 | "supply check", 128 | { 129 | value = function() 130 | return "startHunt," .. posx() .. "," .. posy() .. "," .. posz() 131 | end, 132 | title = "Supply check label", 133 | description = "Insert here hunting start label", 134 | validation = [[[^,]+,\d{1,5},\d{1,5},\d{1,2}$]] 135 | } 136 | ) 137 | end 138 | -------------------------------------------------------------------------------- /cavebot/tasker.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Tasker = {} 2 | 3 | local dataValidationFailed = function() 4 | print("CaveBot[Tasker]: data validation failed! incorrect data, check cavebot/tasker for more info") 5 | return false 6 | end 7 | 8 | -- miniconfig 9 | local talkDelay = storage.extras.talkDelay 10 | if not storage.caveBotTasker then 11 | storage.caveBotTasker = { 12 | inProgress = false, 13 | monster = "", 14 | taskName = "", 15 | count = 0, 16 | max = 0 17 | } 18 | end 19 | 20 | local resetTaskData = function() 21 | storage.caveBotTasker.inProgress = false 22 | storage.caveBotTasker.monster = "" 23 | storage.caveBotTasker.monster2 = "" 24 | storage.caveBotTasker.taskName = "" 25 | storage.caveBotTasker.count = 0 26 | storage.caveBotTasker.max = 0 27 | end 28 | 29 | CaveBot.Extensions.Tasker.setup = function() 30 | CaveBot.registerAction("Tasker", "#FF0090", function(value, retries) 31 | local taskName = "" 32 | local monster = "" 33 | local monster2 = "" 34 | local count = 0 35 | local label1 = "" 36 | local label2 = "" 37 | local task 38 | 39 | local data = string.split(value, ",") 40 | if not data or #data < 1 then 41 | dataValidationFailed() 42 | end 43 | local marker = tonumber(data[1]) 44 | 45 | if not marker then 46 | dataValidationFailed() 47 | resetTaskData() 48 | elseif marker == 1 then 49 | if getNpcs(3) == 0 then 50 | print("CaveBot[Tasker]: no NPC found in range! skipping") 51 | return false 52 | end 53 | if #data ~= 4 and #data ~= 5 then 54 | dataValidationFailed() 55 | resetTaskData() 56 | else 57 | taskName = data[2]:lower():trim() 58 | count = tonumber(data[3]:trim()) 59 | monster = data[4]:lower():trim() 60 | if #data == 5 then 61 | monster2 = data[5]:lower():trim() 62 | end 63 | end 64 | elseif marker == 2 then 65 | if #data ~= 3 then 66 | dataValidationFailed() 67 | else 68 | label1 = data[2]:lower():trim() 69 | label2 = data[3]:lower():trim() 70 | end 71 | elseif marker == 3 then 72 | if getNpcs(3) == 0 then 73 | print("CaveBot[Tasker]: no NPC found in range! skipping") 74 | return false 75 | end 76 | if #data ~= 1 then 77 | dataValidationFailed() 78 | end 79 | end 80 | 81 | -- let's cover markers now 82 | if marker == 1 then -- starting task 83 | CaveBot.Conversation("hi", "task", taskName, "yes") 84 | delay(talkDelay*4) 85 | 86 | storage.caveBotTasker.monster = monster 87 | if monster2 then storage.caveBotTasker.monster2 = monster2 end 88 | storage.caveBotTasker.taskName = taskName 89 | storage.caveBotTasker.inProgress = true 90 | storage.caveBotTasker.max = count 91 | storage.caveBotTasker.count = 0 92 | 93 | print("CaveBot[Tasker]: taken task for: " .. monster .. " x" .. count) 94 | return true 95 | elseif marker == 2 then -- only checking 96 | if not storage.caveBotTasker.inProgress then 97 | CaveBot.gotoLabel(label2) 98 | print("CaveBot[Tasker]: there is no task in progress so going to take one.") 99 | return true 100 | end 101 | 102 | local max = storage.caveBotTasker.max 103 | local count = storage.caveBotTasker.count 104 | 105 | if count >= max then 106 | CaveBot.gotoLabel(label2) 107 | print("CaveBot[Tasker]: task completed: " .. storage.caveBotTasker.taskName) 108 | return true 109 | else 110 | CaveBot.gotoLabel(label1) 111 | print("CaveBot[Tasker]: task in progress, left: " .. max - count .. " " .. storage.caveBotTasker.taskName) 112 | return true 113 | end 114 | 115 | 116 | elseif marker == 3 then -- reporting task 117 | CaveBot.Conversation("hi", "report", "task") 118 | delay(talkDelay*3) 119 | 120 | resetTaskData() 121 | print("CaveBot[Tasker]: task reported, done") 122 | return true 123 | end 124 | 125 | end) 126 | 127 | CaveBot.Editor.registerAction("tasker", "tasker", { 128 | value=[[ There is 3 scenarios for this extension, as example we will use medusa: 129 | 130 | 1. start task, 131 | parameters: 132 | - scenario for extension: 1 133 | - task name in gryzzly adams: medusae 134 | - monster count: 500 135 | - monster name to track: medusa 136 | - optional, monster name 2: 137 | 2. check status, 138 | to be used on refill to decide whether to go back or spawn or go give task back 139 | parameters: 140 | - scenario for extension: 2 141 | - label if task in progress: skipTask 142 | - label if task done: taskDone 143 | 3. report task, 144 | parameters: 145 | - scenario for extension: 3 146 | 147 | Strong suggestion, almost mandatory - USE POS CHECK to verify position! this module will only check if there is ANY npc in range! 148 | 149 | when begin remove all the text and leave just a single string of parameters 150 | some examples: 151 | 152 | 2, skipReport, goReport 153 | 3 154 | 1, drakens, 500, draken warmaster, draken spellweaver 155 | 1, medusae, 500, medusa]], 156 | title="Tasker", 157 | multiline = true 158 | }) 159 | end 160 | 161 | local regex = "Loot of ([a-z])* ([a-z A-Z]*):" 162 | local regex2 = "Loot of ([a-z A-Z]*):" 163 | onTextMessage(function(mode, text) 164 | -- if CaveBot.isOff() then return end 165 | if not text:lower():find("loot of") then return end 166 | if #regexMatch(text, regex) == 1 and #regexMatch(text, regex)[1] == 3 then 167 | monster = regexMatch(text, regex)[1][3] 168 | elseif #regexMatch(text, regex2) == 1 and #regexMatch(text, regex2)[1] == 2 then 169 | monster = regexMatch(text, regex2)[1][2] 170 | end 171 | 172 | local m1 = storage.caveBotTasker.monster 173 | local m2 = storage.caveBotTasker.monster2 174 | 175 | if monster == m1 or monster == m2 and storage.caveBotTasker.count then 176 | storage.caveBotTasker.count = storage.caveBotTasker.count + 1 177 | end 178 | end) 179 | -------------------------------------------------------------------------------- /cavebot/travel.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Travel = {} 2 | 3 | CaveBot.Extensions.Travel.setup = function() 4 | CaveBot.registerAction("Travel", "#db5a5a", function(value, retries) 5 | local data = string.split(value, ",") 6 | if #data < 2 then 7 | warn("CaveBot[Travel]: incorrect travel value!") 8 | return false 9 | end 10 | 11 | local npcName = data[1]:trim() 12 | local dest = data[2]:trim() 13 | 14 | if retries > 5 then 15 | print("CaveBot[Travel]: too many tries, can't travel") 16 | return false 17 | end 18 | 19 | local npc = getCreatureByName(npcName) 20 | if not npc then 21 | print("CaveBot[Travel]: NPC not found, can't travel") 22 | return false 23 | end 24 | 25 | if not CaveBot.ReachNPC(npcName) then 26 | return "retry" 27 | end 28 | 29 | CaveBot.Travel(dest) 30 | delay(storage.extras.talkDelay*3) 31 | print("CaveBot[Travel]: travel action finished") 32 | return true 33 | end) 34 | 35 | CaveBot.Editor.registerAction("travel", "travel", { 36 | value="NPC name, city", 37 | title="Travel", 38 | description="NPC name, City name, delay in ms(default is 200ms)", 39 | }) 40 | end -------------------------------------------------------------------------------- /cavebot/walking.lua: -------------------------------------------------------------------------------- 1 | -- walking 2 | local expectedDirs = {} 3 | local isWalking = {} 4 | local walkPath = {} 5 | local walkPathIter = 0 6 | 7 | CaveBot.resetWalking = function() 8 | expectedDirs = {} 9 | walkPath = {} 10 | isWalking = false 11 | end 12 | 13 | CaveBot.doWalking = function() 14 | if CaveBot.Config.get("mapClick") then 15 | return false 16 | end 17 | if #expectedDirs == 0 then 18 | return false 19 | end 20 | if #expectedDirs >= 3 then 21 | CaveBot.resetWalking() 22 | end 23 | local dir = walkPath[walkPathIter] 24 | if dir then 25 | g_game.walk(dir, false) 26 | table.insert(expectedDirs, dir) 27 | walkPathIter = walkPathIter + 1 28 | CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir)) 29 | return true 30 | end 31 | return false 32 | end 33 | 34 | -- called when player position has been changed (step has been confirmed by server) 35 | onPlayerPositionChange(function(newPos, oldPos) 36 | if not oldPos or not newPos then return end 37 | 38 | local dirs = {{NorthWest, North, NorthEast}, {West, 8, East}, {SouthWest, South, SouthEast}} 39 | local dir = dirs[newPos.y - oldPos.y + 2] 40 | if dir then 41 | dir = dir[newPos.x - oldPos.x + 2] 42 | end 43 | if not dir then 44 | dir = 8 -- 8 is invalid dir, it's fine 45 | end 46 | 47 | if not isWalking or not expectedDirs[1] then 48 | -- some other walk action is taking place (for example use on ladder), wait 49 | walkPath = {} 50 | CaveBot.delay(CaveBot.Config.get("ping") + player:getStepDuration(false, dir) + 150) 51 | return 52 | end 53 | 54 | if expectedDirs[1] ~= dir then 55 | if CaveBot.Config.get("mapClick") then 56 | CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir)) 57 | else 58 | CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir)) 59 | end 60 | return 61 | end 62 | 63 | table.remove(expectedDirs, 1) 64 | if CaveBot.Config.get("mapClick") and #expectedDirs > 0 then 65 | CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir)) 66 | end 67 | end) 68 | 69 | CaveBot.walkTo = function(dest, maxDist, params) 70 | local path = getPath(player:getPosition(), dest, maxDist, params) 71 | if not path or not path[1] then 72 | return false 73 | end 74 | local dir = path[1] 75 | 76 | if CaveBot.Config.get("mapClick") then 77 | local ret = autoWalk(path) 78 | if ret then 79 | isWalking = true 80 | expectedDirs = path 81 | CaveBot.delay(CaveBot.Config.get("mapClickDelay") + math.max(CaveBot.Config.get("ping") + player:getStepDuration(false, dir), player:getStepDuration(false, dir) * 2)) 82 | end 83 | return ret 84 | end 85 | 86 | g_game.walk(dir, false) 87 | isWalking = true 88 | walkPath = path 89 | walkPathIter = 2 90 | expectedDirs = { dir } 91 | CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir)) 92 | return true 93 | end 94 | -------------------------------------------------------------------------------- /cavebot/withdraw.lua: -------------------------------------------------------------------------------- 1 | CaveBot.Extensions.Withdraw = {} 2 | 3 | CaveBot.Extensions.Withdraw.setup = function() 4 | CaveBot.registerAction("withdraw", "#002FFF", function(value, retries) 5 | -- validation 6 | local data = string.split(value, ",") 7 | if #data ~= 3 then 8 | print("CaveBot[Withdraw]: incorrect data! skipping") 9 | return false 10 | end 11 | 12 | -- variables declaration 13 | local source = tonumber(data[1]) 14 | local id = tonumber(data[2]) 15 | local amount = tonumber(data[3]) 16 | 17 | -- validation for correct values 18 | if not id or not amount then 19 | print("CaveBot[Withdraw]: incorrect id or amount! skipping") 20 | return false 21 | end 22 | 23 | -- check for retries 24 | if retries > 100 then 25 | print("CaveBot[Withdraw]: actions limit reached, proceeding") 26 | for i, container in ipairs(getContainers()) do 27 | if container:getName():lower():find("depot") or container:getName():lower():find("locker") then 28 | g_game.close(container) 29 | end 30 | end 31 | return true 32 | end 33 | 34 | -- check for items 35 | if itemAmount(id) >= amount then 36 | print("CaveBot[Withdraw]: enough items, proceeding") 37 | for i, container in ipairs(getContainers()) do 38 | if container:getName():lower():find("depot") or container:getName():lower():find("locker") then 39 | g_game.close(container) 40 | end 41 | end 42 | return true 43 | end 44 | 45 | statusMessage("[Withdraw] withdrawing item: " ..id.. " x"..amount) 46 | CaveBot.WithdrawItem(id, amount, source) 47 | CaveBot.PingDelay() 48 | return "retry" 49 | end) 50 | 51 | CaveBot.Editor.registerAction("withdraw", "withdraw", { 52 | value="source,id,amount", 53 | title="Withdraw Items", 54 | description="index/inbox, item id and amount", 55 | }) 56 | end -------------------------------------------------------------------------------- /targetbot/creature.lua: -------------------------------------------------------------------------------- 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 = "" 24 | for part in string.gmatch(config.name, "[^,]+") do 25 | if config.regex:len() > 0 then 26 | config.regex = config.regex .. "|" 27 | end 28 | config.regex = config.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$" 29 | end 30 | end 31 | 32 | local widget = UI.createWidget("TargetBotEntry", TargetBot.targetList) 33 | widget:setText(config.name) 34 | widget.value = config 35 | 36 | widget.onDoubleClick = function(entry) -- edit on double click 37 | schedule(20, function() -- schedule to have correct focus 38 | TargetBot.Creature.edit(entry.value, function(newConfig) 39 | entry:setText(newConfig.name) 40 | entry.value = newConfig 41 | TargetBot.Creature.resetConfigsCache() 42 | TargetBot.save() 43 | end) 44 | end) 45 | end 46 | 47 | if focus then 48 | widget:focus() 49 | TargetBot.targetList:ensureChildVisible(widget) 50 | end 51 | return widget 52 | end 53 | 54 | TargetBot.Creature.getConfigs = function(creature) 55 | if not creature then return {} end 56 | local name = creature:getName():trim():lower() 57 | -- this function may be slow, so it will be using cache 58 | if TargetBot.Creature.configsCache[name] then 59 | return TargetBot.Creature.configsCache[name] 60 | end 61 | local configs = {} 62 | for _, config in ipairs(TargetBot.targetList:getChildren()) do 63 | if regexMatch(name, config.value.regex)[1] then 64 | table.insert(configs, config.value) 65 | end 66 | end 67 | if TargetBot.Creature.cached > 1000 then 68 | TargetBot.Creature.resetConfigsCache() -- too big cache size, reset 69 | end 70 | TargetBot.Creature.configsCache[name] = configs -- add to cache 71 | TargetBot.Creature.cached = TargetBot.Creature.cached + 1 72 | return configs 73 | end 74 | 75 | TargetBot.Creature.calculateParams = function(creature, path) 76 | local configs = TargetBot.Creature.getConfigs(creature) 77 | local priority = 0 78 | local danger = 0 79 | local selectedConfig = nil 80 | for _, config in ipairs(configs) do 81 | local config_priority = TargetBot.Creature.calculatePriority(creature, config, path) 82 | if config_priority > priority then 83 | priority = config_priority 84 | danger = TargetBot.Creature.calculateDanger(creature, config, path) 85 | selectedConfig = config 86 | end 87 | end 88 | return { 89 | config = selectedConfig, 90 | creature = creature, 91 | danger = danger, 92 | priority = priority 93 | } 94 | end 95 | 96 | TargetBot.Creature.calculateDanger = function(creature, config, path) 97 | -- config is based on creature_editor 98 | return config.danger 99 | end 100 | -------------------------------------------------------------------------------- /targetbot/creature_attack.lua: -------------------------------------------------------------------------------- 1 | local targetBotLure = false 2 | local targetCount = 0 3 | local delayValue = 0 4 | local lureMax = 0 5 | local anchorPosition = nil 6 | local lastCall = now 7 | local delayFrom = nil 8 | local dynamicLureDelay = false 9 | 10 | function getWalkableTilesCount(position) 11 | local count = 0 12 | 13 | for i, tile in pairs(getNearTiles(position)) do 14 | if tile:isWalkable() or tile:hasCreature() then 15 | count = count + 1 16 | end 17 | end 18 | 19 | return count 20 | end 21 | 22 | function rePosition(minTiles) 23 | minTiles = minTiles or 8 24 | if now - lastCall < 500 then return end 25 | local pPos = player:getPosition() 26 | local tiles = getNearTiles(pPos) 27 | local playerTilesCount = getWalkableTilesCount(pPos) 28 | local tilesTable = {} 29 | 30 | if playerTilesCount > minTiles then return end 31 | for i, tile in ipairs(tiles) do 32 | tilesTable[tile] = not tile:hasCreature() and tile:isWalkable() and getWalkableTilesCount(tile:getPosition()) or nil 33 | end 34 | 35 | local best = 0 36 | local target = nil 37 | for k,v in pairs(tilesTable) do 38 | if v > best and v > playerTilesCount then 39 | best = v 40 | target = k:getPosition() 41 | end 42 | end 43 | 44 | if target then 45 | lastCall = now 46 | return CaveBot.GoTo(target, 0) 47 | end 48 | end 49 | 50 | TargetBot.Creature.attack = function(params, targets, isLooting) -- params {config, creature, danger, priority} 51 | if player:isWalking() then 52 | lastWalk = now 53 | end 54 | 55 | local config = params.config 56 | local creature = params.creature 57 | 58 | if g_game.getAttackingCreature() ~= creature then 59 | g_game.attack(creature) 60 | end 61 | 62 | if not isLooting then -- walk only when not looting 63 | TargetBot.Creature.walk(creature, config, targets) 64 | end 65 | 66 | -- attacks 67 | local mana = player:getMana() 68 | if config.useGroupAttack and config.groupAttackSpell:len() > 1 and mana > config.minManaGroup then 69 | local creatures = g_map.getSpectatorsInRange(player:getPosition(), false, config.groupAttackRadius, config.groupAttackRadius) 70 | local playersAround = false 71 | local monsters = 0 72 | for _, creature in ipairs(creatures) do 73 | if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then 74 | playersAround = true 75 | elseif creature:isMonster() then 76 | monsters = monsters + 1 77 | end 78 | end 79 | if monsters >= config.groupAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then 80 | if TargetBot.sayAttackSpell(config.groupAttackSpell, config.groupAttackDelay) then 81 | return 82 | end 83 | end 84 | end 85 | 86 | if config.useGroupAttackRune and config.groupAttackRune > 100 then 87 | local creatures = g_map.getSpectatorsInRange(creature:getPosition(), false, config.groupRuneAttackRadius, config.groupRuneAttackRadius) 88 | local playersAround = false 89 | local monsters = 0 90 | for _, creature in ipairs(creatures) do 91 | if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then 92 | playersAround = true 93 | elseif creature:isMonster() then 94 | monsters = monsters + 1 95 | end 96 | end 97 | if monsters >= config.groupRuneAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then 98 | if TargetBot.useAttackItem(config.groupAttackRune, 0, creature, config.groupRuneAttackDelay) then 99 | return 100 | end 101 | end 102 | end 103 | if config.useSpellAttack and config.attackSpell:len() > 1 and mana > config.minMana then 104 | if TargetBot.sayAttackSpell(config.attackSpell, config.attackSpellDelay) then 105 | return 106 | end 107 | end 108 | if config.useRuneAttack and config.attackRune > 100 then 109 | if TargetBot.useAttackItem(config.attackRune, 0, creature, config.attackRuneDelay) then 110 | return 111 | end 112 | end 113 | end 114 | 115 | TargetBot.Creature.walk = function(creature, config, targets) 116 | local cpos = creature:getPosition() 117 | local pos = player:getPosition() 118 | 119 | local isTrapped = true 120 | local pos = player:getPosition() 121 | local dirs = {{-1,1}, {0,1}, {1,1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}} 122 | for i=1,#dirs do 123 | local tile = g_map.getTile({x=pos.x-dirs[i][1],y=pos.y-dirs[i][2],z=pos.z}) 124 | if tile and tile:isWalkable(false) then 125 | isTrapped = false 126 | end 127 | end 128 | 129 | -- data for external dynamic lure 130 | if config.lureMin and config.lureMax and config.dynamicLure then 131 | if config.lureMin >= targets then 132 | targetBotLure = true 133 | elseif targets >= config.lureMax then 134 | targetBotLure = false 135 | end 136 | end 137 | targetCount = targets 138 | delayValue = config.lureDelay 139 | 140 | if config.lureMax then 141 | lureMax = config.lureMax 142 | end 143 | 144 | dynamicLureDelay = config.dynamicLureDelay 145 | delayFrom = config.delayFrom 146 | 147 | -- luring 148 | if config.closeLure and config.closeLureAmount <= getMonsters(1) then 149 | return TargetBot.allowCaveBot(150) 150 | end 151 | 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 152 | if targetBotLure then 153 | anchorPosition = nil 154 | return TargetBot.allowCaveBot(150) 155 | else 156 | if targets < config.lureCount then 157 | if config.lureCavebot then 158 | anchorPosition = nil 159 | return TargetBot.allowCaveBot(150) 160 | else 161 | local path = findPath(pos, cpos, 5, {ignoreNonPathable=true, precision=2}) 162 | if path then 163 | return TargetBot.walkTo(cpos, 10, {marginMin=5, marginMax=6, ignoreNonPathable=true}) 164 | end 165 | end 166 | end 167 | end 168 | end 169 | 170 | local currentDistance = findPath(pos, cpos, 10, {ignoreCreatures=true, ignoreNonPathable=true, ignoreCost=true}) 171 | 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 172 | return rePosition(config.rePositionAmount or 6) 173 | end 174 | if ((storage.extras.killUnder > 1 and (creature:getHealthPercent() < storage.extras.killUnder)) or config.chase) and not config.keepDistance then 175 | if #currentDistance > 1 then 176 | return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, precision=1}) 177 | end 178 | elseif config.keepDistance then 179 | if not anchorPosition or distanceFromPlayer(anchorPosition) > config.anchorRange then 180 | anchorPosition = pos 181 | end 182 | if #currentDistance ~= config.keepDistanceRange and #currentDistance ~= config.keepDistanceRange + 1 then 183 | if config.anchor and anchorPosition and getDistanceBetween(pos, anchorPosition) <= config.anchorRange*2 then 184 | return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1, maxDistanceFrom={anchorPosition, config.anchorRange}}) 185 | else 186 | return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1}) 187 | end 188 | end 189 | end 190 | 191 | --target only movement 192 | if config.avoidAttacks then 193 | local diffx = cpos.x - pos.x 194 | local diffy = cpos.y - pos.y 195 | local candidates = {} 196 | if math.abs(diffx) == 1 and diffy == 0 then 197 | candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x, y=pos.y+1, z=pos.z}} 198 | elseif diffx == 0 and math.abs(diffy) == 1 then 199 | candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}} 200 | end 201 | for _, candidate in ipairs(candidates) do 202 | local tile = g_map.getTile(candidate) 203 | if tile and tile:isWalkable() then 204 | return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true}) 205 | end 206 | end 207 | elseif config.faceMonster then 208 | local diffx = cpos.x - pos.x 209 | local diffy = cpos.y - pos.y 210 | local candidates = {} 211 | if diffx == 1 and diffy == 1 then 212 | candidates = {{x=pos.x+1, y=pos.y, z=pos.z}, {x=pos.x, y=pos.y-1, z=pos.z}} 213 | elseif diffx == -1 and diffy == 1 then 214 | candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x, y=pos.y-1, z=pos.z}} 215 | elseif diffx == -1 and diffy == -1 then 216 | candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x-1, y=pos.y, z=pos.z}} 217 | elseif diffx == 1 and diffy == -1 then 218 | candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}} 219 | else 220 | local dir = player:getDirection() 221 | if diffx == 1 and dir ~= 1 then turn(1) 222 | elseif diffx == -1 and dir ~= 3 then turn(3) 223 | elseif diffy == 1 and dir ~= 2 then turn(2) 224 | elseif diffy == -1 and dir ~= 0 then turn(0) 225 | end 226 | end 227 | for _, candidate in ipairs(candidates) do 228 | local tile = g_map.getTile(candidate) 229 | if tile and tile:isWalkable() then 230 | return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true}) 231 | end 232 | end 233 | end 234 | end 235 | 236 | onPlayerPositionChange(function(newPos, oldPos) 237 | if CaveBot.isOff() then return end 238 | if TargetBot.isOff() then return end 239 | if not lureMax then return end 240 | if storage.TargetBotDelayWhenPlayer then return end 241 | if not dynamicLureDelay then return end 242 | 243 | if targetCount < (delayFrom or lureMax/2) or not target() then return end 244 | CaveBot.delay(delayValue or 0) 245 | end) -------------------------------------------------------------------------------- /targetbot/creature_editor.lua: -------------------------------------------------------------------------------- 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.content.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.content.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.content.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.content.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 | 66 | newConfig.regex = "" 67 | for part in string.gmatch(newConfig.name, "[^,]+") do 68 | if newConfig.regex:len() > 0 then 69 | newConfig.regex = newConfig.regex .. "|" 70 | end 71 | newConfig.regex = newConfig.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$" 72 | end 73 | 74 | editor:destroy() 75 | callback(newConfig) 76 | end 77 | 78 | -- values 79 | addScrollBar("priority", "Priority", 0, 10, 1) 80 | addScrollBar("danger", "Danger", 0, 10, 1) 81 | addScrollBar("maxDistance", "Max distance", 1, 10, 10) 82 | addScrollBar("keepDistanceRange", "Keep distance", 1, 5, 1) 83 | addScrollBar("anchorRange", "Anchoring Range", 1, 10, 3) 84 | addScrollBar("lureCount", "Classic Lure", 0, 5, 1) 85 | addScrollBar("lureMin", "Dynamic lure min", 0, 29, 1) 86 | addScrollBar("lureMax", "Dynamic lure max", 1, 30, 3) 87 | addScrollBar("lureDelay", "Dynamic lure delay", 100, 1000, 250) 88 | addScrollBar("delayFrom", "Start delay when monsters", 1, 29, 2) 89 | addScrollBar("rePositionAmount", "Min tiles to rePosition", 0, 7, 5) 90 | addScrollBar("closeLureAmount", "Close Pull Until", 0, 8, 3) 91 | 92 | addCheckBox("chase", "Chase", true) 93 | addCheckBox("keepDistance", "Keep Distance", false) 94 | addCheckBox("anchor", "Anchoring", false) 95 | addCheckBox("dontLoot", "Don't loot", false) 96 | addCheckBox("lure", "Lure", false) 97 | addCheckBox("lureCavebot", "Lure using cavebot", false) 98 | addCheckBox("faceMonster", "Face monsters", false) 99 | addCheckBox("avoidAttacks", "Avoid wave attacks", false) 100 | addCheckBox("dynamicLure", "Dynamic lure", false) 101 | addCheckBox("dynamicLureDelay", "Dynamic lure delay", false) 102 | addCheckBox("diamondArrows", "D-Arrows priority", false) 103 | addCheckBox("rePosition", "rePosition to better tile", false) 104 | addCheckBox("closeLure", "Close Pulling Monsters", false) 105 | addCheckBox("rpSafe", "RP PVP SAFE - (DA)", false) 106 | end 107 | -------------------------------------------------------------------------------- /targetbot/creature_editor.otui: -------------------------------------------------------------------------------- 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: 425 68 | 69 | $mobile: 70 | height: 300 71 | 72 | Label 73 | anchors.left: parent.left 74 | anchors.right: parent.right 75 | anchors.top: parent.top 76 | text-align: center 77 | !text: tr('You can use * (any characters) and ? (any character) in target name') 78 | 79 | Label 80 | anchors.left: parent.left 81 | anchors.right: parent.right 82 | anchors.top: prev.bottom 83 | text-align: center 84 | !text: tr('You can also enter multiple targets, separate them by ,') 85 | 86 | TextEdit 87 | id: name 88 | anchors.top: prev.bottom 89 | anchors.left: parent.left 90 | anchors.right: parent.right 91 | margin-left: 90 92 | margin-top: 5 93 | 94 | Label 95 | anchors.verticalCenter: prev.verticalCenter 96 | anchors.left: parent.left 97 | text: Target name: 98 | 99 | VerticalScrollBar 100 | id: contentScroll 101 | anchors.top: name.bottom 102 | anchors.right: parent.right 103 | anchors.bottom: help.top 104 | step: 28 105 | pixels-scroll: true 106 | margin-right: -10 107 | margin-top: 5 108 | margin-bottom: 5 109 | 110 | ScrollablePanel 111 | id: content 112 | anchors.top: name.bottom 113 | anchors.left: parent.left 114 | anchors.right: parent.right 115 | anchors.bottom: help.top 116 | vertical-scrollbar: contentScroll 117 | margin-bottom: 10 118 | 119 | Panel 120 | id: left 121 | anchors.top: parent.top 122 | anchors.left: parent.left 123 | anchors.right: parent.horizontalCenter 124 | margin-top: 5 125 | margin-left: 10 126 | margin-right: 10 127 | layout: 128 | type: verticalBox 129 | fit-children: true 130 | 131 | Panel 132 | id: right 133 | anchors.top: parent.top 134 | anchors.left: parent.horizontalCenter 135 | anchors.right: parent.right 136 | margin-top: 5 137 | margin-left: 10 138 | margin-right: 10 139 | layout: 140 | type: verticalBox 141 | fit-children: true 142 | 143 | Button 144 | id: help 145 | !text: tr('Help & Tutorials') 146 | anchors.bottom: parent.bottom 147 | anchors.left: parent.left 148 | width: 150 149 | @onClick: g_platform.openUrl("http://bot.otclient.ovh/") 150 | 151 | Button 152 | id: ok 153 | !text: tr('Ok') 154 | anchors.bottom: parent.bottom 155 | anchors.right: next.left 156 | margin-right: 10 157 | width: 60 158 | 159 | Button 160 | id: cancel 161 | !text: tr('Cancel') 162 | anchors.bottom: parent.bottom 163 | anchors.right: parent.right 164 | width: 60 165 | -------------------------------------------------------------------------------- /targetbot/creature_priority.lua: -------------------------------------------------------------------------------- 1 | TargetBot.Creature.calculatePriority = function(creature, config, path) 2 | -- config is based on creature_editor 3 | local priority = 0 4 | local currentTarget = g_game.getAttackingCreature() 5 | 6 | -- extra priority if it's current target 7 | if currentTarget == creature then 8 | priority = priority + 1 9 | end 10 | 11 | -- check if distance is ok 12 | if #path > config.maxDistance then 13 | if config.rpSafe then 14 | if currentTarget == creature then 15 | g_game.cancelAttackAndFollow() -- if not, stop attack (pvp safe) 16 | end 17 | end 18 | return priority 19 | end 20 | 21 | -- add config priority 22 | priority = priority + config.priority 23 | 24 | -- extra priority for close distance 25 | local path_length = #path 26 | if path_length == 1 then 27 | priority = priority + 10 28 | elseif path_length <= 3 then 29 | priority = priority + 5 30 | end 31 | 32 | -- extra priority for paladin diamond arrows 33 | if config.diamondArrows then 34 | local mobCount = getCreaturesInArea(creature:getPosition(), diamondArrowArea, 2) 35 | priority = priority + (mobCount * 4) 36 | 37 | if config.rpSafe then 38 | if getCreaturesInArea(creature:getPosition(), largeRuneArea, 3) > 0 then 39 | if currentTarget == creature then 40 | g_game.cancelAttackAndFollow() 41 | end 42 | return 0 -- pvp safe 43 | end 44 | end 45 | end 46 | 47 | -- extra priority for low health 48 | if config.chase and creature:getHealthPercent() < 30 then 49 | priority = priority + 5 50 | elseif creature:getHealthPercent() < 20 then 51 | priority = priority + 2.5 52 | elseif creature:getHealthPercent() < 40 then 53 | priority = priority + 1.5 54 | elseif creature:getHealthPercent() < 60 then 55 | priority = priority + 0.5 56 | elseif creature:getHealthPercent() < 80 then 57 | priority = priority + 0.2 58 | end 59 | 60 | return priority 61 | end -------------------------------------------------------------------------------- /targetbot/looting.otui: -------------------------------------------------------------------------------- 1 | TargetBotLootingPanel < Panel 2 | layout: 3 | type: verticalBox 4 | fit-children: true 5 | 6 | HorizontalSeparator 7 | margin-top: 5 8 | 9 | Label 10 | margin-top: 5 11 | text: Items to loot 12 | text-align: center 13 | 14 | BotContainer 15 | id: items 16 | margin-top: 3 17 | 18 | BotSwitch 19 | id: everyItem 20 | !text: tr("Loot every item") 21 | margin-top: 2 22 | 23 | Label 24 | margin-top: 5 25 | text: Containers for loot 26 | text-align: center 27 | 28 | BotContainer 29 | id: containers 30 | margin-top: 3 31 | height: 45 32 | 33 | Panel 34 | id: maxDangerPanel 35 | height: 20 36 | margin-top: 5 37 | 38 | BotTextEdit 39 | id: value 40 | anchors.right: parent.right 41 | anchors.top: parent.top 42 | anchors.bottom: parent.bottom 43 | margin-right: 6 44 | width: 80 45 | 46 | Label 47 | anchors.left: parent.left 48 | anchors.verticalCenter: prev.verticalCenter 49 | text: Max. danger: 50 | margin-left: 5 51 | 52 | Panel 53 | id: minCapacityPanel 54 | height: 20 55 | margin-top: 3 56 | 57 | BotTextEdit 58 | id: value 59 | anchors.right: parent.right 60 | anchors.top: parent.top 61 | anchors.bottom: parent.bottom 62 | margin-right: 6 63 | width: 80 64 | 65 | Label 66 | anchors.left: parent.left 67 | anchors.verticalCenter: prev.verticalCenter 68 | text: Min. capacity: 69 | margin-left: 5 -------------------------------------------------------------------------------- /targetbot/target.otui: -------------------------------------------------------------------------------- 1 | TargetBotEntry < Label 2 | background-color: alpha 3 | text-offset: 2 0 4 | focusable: true 5 | 6 | $focus: 7 | background-color: #00000055 8 | 9 | TargetBotDualLabel < Panel 10 | height: 18 11 | margin-left: 3 12 | margin-right: 4 13 | 14 | Label 15 | id: left 16 | anchors.top: parent.top 17 | anchors.left: parent.left 18 | text-auto-resize: true 19 | 20 | Label 21 | id: right 22 | anchors.top: parent.top 23 | anchors.right: parent.right 24 | text-auto-resize: true 25 | 26 | TargetBotPanel < Panel 27 | layout: 28 | type: verticalBox 29 | fit-children: true 30 | 31 | HorizontalSeparator 32 | margin-top: 2 33 | margin-bottom: 5 34 | 35 | TargetBotDualLabel 36 | id: status 37 | TargetBotDualLabel 38 | id: target 39 | TargetBotDualLabel 40 | id: config 41 | TargetBotDualLabel 42 | id: danger 43 | 44 | Panel 45 | id: listPanel 46 | height: 40 47 | 48 | TextList 49 | id: list 50 | anchors.fill: parent 51 | vertical-scrollbar: listScrollbar 52 | margin-right: 15 53 | focusable: false 54 | auto-focus: first 55 | 56 | VerticalScrollBar 57 | id: listScrollbar 58 | anchors.top: parent.top 59 | anchors.bottom: parent.bottom 60 | anchors.right: parent.right 61 | pixels-scroll: true 62 | step: 10 63 | 64 | BotSwitch 65 | id: configButton 66 | @onClick: | 67 | self:setOn(not self:isOn()) 68 | self:getParent().listPanel:setHeight(self:isOn() and 100 or 40) 69 | self:getParent().editor:setVisible(self:isOn()) 70 | 71 | $on: 72 | text: Hide target editor 73 | 74 | $!on: 75 | text: Show target editor 76 | 77 | Panel 78 | id: editor 79 | visible: false 80 | layout: 81 | type: verticalBox 82 | fit-children: true 83 | 84 | Panel 85 | id: buttons 86 | height: 20 87 | margin-top: 2 88 | 89 | Button 90 | id: add 91 | anchors.top: parent.top 92 | anchors.bottom: parent.bottom 93 | anchors.left: parent.left 94 | text: Add 95 | width: 56 96 | 97 | Button 98 | id: edit 99 | anchors.top: parent.top 100 | anchors.bottom: parent.bottom 101 | anchors.horizontalCenter: parent.horizontalCenter 102 | text: Edit 103 | width: 56 104 | 105 | Button 106 | id: remove 107 | anchors.top: parent.top 108 | anchors.bottom: parent.bottom 109 | anchors.right: parent.right 110 | text: Remove 111 | width: 56 112 | 113 | BotSwitch 114 | id: debug 115 | text: Show target priority 116 | -------------------------------------------------------------------------------- /targetbot/walking.lua: -------------------------------------------------------------------------------- 1 | local dest 2 | local maxDist 3 | local params 4 | 5 | TargetBot.walkTo = function(_dest, _maxDist, _params) 6 | dest = _dest 7 | maxDist = _maxDist 8 | params = _params 9 | end 10 | 11 | -- called every 100ms if targeting or looting is active 12 | TargetBot.walk = function() 13 | if not dest then return end 14 | if player:isWalking() then return end 15 | local pos = player:getPosition() 16 | if pos.z ~= dest.z then return end 17 | local dist = math.max(math.abs(pos.x-dest.x), math.abs(pos.y-dest.y)) 18 | if params.precision and params.precision >= dist then return end 19 | if params.marginMin and params.marginMax then 20 | if dist >= params.marginMin and dist <= params.marginMax then 21 | return 22 | end 23 | end 24 | local path = getPath(pos, dest, maxDist, params) 25 | if path then 26 | walk(path[1]) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /vBot/BotServer.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Main") 2 | local regex = [["(.*?)"]] 3 | local panelName = "BOTserver" 4 | local ui = setupUI([[ 5 | Panel 6 | height: 18 7 | 8 | Button 9 | id: botServer 10 | anchors.left: parent.left 11 | anchors.right: parent.right 12 | text-align: center 13 | height: 18 14 | !text: tr('BotServer') 15 | ]]) 16 | ui:setId(panelName) 17 | 18 | if not storage[panelName] then 19 | storage[panelName] = { 20 | manaInfo = true, 21 | mwallInfo = true, 22 | vocation = true, 23 | outfit = false, 24 | broadcasts = true 25 | } 26 | end 27 | 28 | local config = storage[panelName] 29 | config.mwalls = {} 30 | 31 | if not storage.BotServerChannel then 32 | math.randomseed(os.time()) 33 | storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999)) 34 | end 35 | 36 | local channel = tostring(storage.BotServerChannel) 37 | if config.enabled then 38 | BotServer.init(name(), channel) 39 | end 40 | 41 | vBot.BotServerMembers = {} 42 | 43 | rootWidget = g_ui.getRootWidget() 44 | if rootWidget then 45 | botServerWindow = UI.createWindow('BotServerWindow') 46 | botServerWindow:hide() 47 | 48 | 49 | botServerWindow.enabled:setOn(config.enabled) 50 | botServerWindow.enabled.onClick = function() 51 | config.enabled = not config.enabled 52 | botServerWindow.enabled:setOn(config.enabled) 53 | if config.enabled then 54 | channel = tostring(storage.BotServerChannel) 55 | BotServer.init(name(), channel) 56 | botServerWindow.Data.ServerStatus:setText("CONNECTING...") 57 | ui.botServer:setColor('#FFF380') 58 | botServerWindow.Data.ServerStatus:setColor('#FFF380') 59 | else 60 | if BotServer._websocket then 61 | BotServer.terminate() 62 | end 63 | botServerWindow.Data.ServerStatus:setText("DISCONNECTED") 64 | ui.botServer:setColor('#E3242B') 65 | botServerWindow.Data.ServerStatus:setColor('#E3242B') 66 | botServerWindow.Data.Participants:setText("-") 67 | botServerWindow.Data.Members:setTooltip('') 68 | ServerMembers = {} 69 | serverCount = {} 70 | end 71 | initBotServerListenFunctions() 72 | schedule(2000, updateStatusText) 73 | end 74 | 75 | botServerWindow.Data.Channel:setText(storage.BotServerChannel) 76 | botServerWindow.Data.Channel.onTextChange = function(widget, text) 77 | storage.BotServerChannel = text 78 | end 79 | botServerWindow.Data.Random.onClick = function(widget) 80 | storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999)) 81 | botServerWindow.Data.Channel:setText(storage.BotServerChannel) 82 | end 83 | botServerWindow.Features.Feature1:setOn(config.manaInfo) 84 | botServerWindow.Features.Feature1.onClick = function(widget) 85 | config.manaInfo = not config.manaInfo 86 | widget:setOn(config.manaInfo) 87 | end 88 | botServerWindow.Features.Feature2:setOn(config.mwallInfo) 89 | botServerWindow.Features.Feature2.onClick = function(widget) 90 | config.mwallInfo = not config.mwallInfo 91 | widget:setOn(config.mwallInfo) 92 | end 93 | botServerWindow.Features.Feature3:setOn(config.vocation) 94 | botServerWindow.Features.Feature3.onClick = function(widget) 95 | config.vocation = not config.vocation 96 | if config.vocation then 97 | BotServer.send("voc", player:getVocation()) 98 | end 99 | widget:setOn(config.vocation) 100 | end 101 | botServerWindow.Features.Feature4:setOn(config.outfit) 102 | botServerWindow.Features.Feature4.onClick = function(widget) 103 | config.outfit = not config.outfit 104 | widget:setOn(config.outfit) 105 | end 106 | botServerWindow.Features.Feature5:setOn(config.broadcasts) 107 | botServerWindow.Features.Feature5.onClick = function(widget) 108 | config.broadcasts = not config.broadcasts 109 | widget:setOn(config.broadcasts) 110 | end 111 | botServerWindow.Features.Broadcast.onClick = function(widget) 112 | if BotServer._websocket then 113 | BotServer.send("broadcast", botServerWindow.Features.broadcastText:getText()) 114 | end 115 | botServerWindow.Features.broadcastText:setText('') 116 | end 117 | end 118 | 119 | function initBotServerListenFunctions() 120 | if not BotServer._websocket then return end 121 | if not config.enabled then return end 122 | 123 | -- list 124 | BotServer.listen("list", function(name, data) 125 | serverCount = regexMatch(json.encode(data), regex) 126 | ServerMembers = json.encode(data) 127 | end) 128 | 129 | -- mwalls 130 | BotServer.listen("mwall", function(name, message) 131 | if config.mwallInfo then 132 | if not config.mwalls[message["pos"]] or config.mwalls[message["pos"]] < now then 133 | config.mwalls[message["pos"]] = now + message["duration"] - 150 -- 150 is latency correction 134 | end 135 | end 136 | end) 137 | 138 | -- mana 139 | BotServer.listen("mana", function(name, message) 140 | if config.manaInfo then 141 | local creature = getPlayerByName(name) 142 | if creature then 143 | creature:setManaPercent(message["mana"]) 144 | end 145 | end 146 | end) 147 | 148 | -- vocation 149 | BotServer.listen("voc", function(name, message) 150 | if message == "yes" and config.vocation then 151 | BotServer.send("voc", player:getVocation()) 152 | else 153 | vBot.BotServerMembers[name] = message 154 | end 155 | end) 156 | 157 | -- broadcast 158 | BotServer.listen("broadcast", function(name, message) 159 | if config.broadcasts then 160 | broadcastMessage(name..": "..message) 161 | end 162 | end) 163 | end 164 | initBotServerListenFunctions() 165 | 166 | function updateStatusText() 167 | if BotServer._websocket then 168 | botServerWindow.Data.ServerStatus:setText("CONNECTED") 169 | botServerWindow.Data.ServerStatus:setColor('#03AC13') 170 | ui.botServer:setColor('#03AC13') 171 | if serverCount then 172 | botServerWindow.Data.Participants:setText(#serverCount) 173 | if ServerMembers then 174 | local text = "" 175 | local regex = [["([a-z 'A-z-]*)"*]] 176 | local re = regexMatch(ServerMembers, regex) 177 | --re[name][2] 178 | for i=1,#re do 179 | if i == 1 then 180 | text = re[i][2] 181 | else 182 | text = text .. "\n" .. re[i][2] 183 | end 184 | end 185 | botServerWindow.Data.Members:setTooltip(text) 186 | end 187 | end 188 | else 189 | botServerWindow.Data.ServerStatus:setText("DISCONNECTED") 190 | ui.botServer:setColor('#E3242B') 191 | botServerWindow.Data.ServerStatus:setColor('#E3242B') 192 | botServerWindow.Data.Participants:setText("-") 193 | end 194 | end 195 | 196 | macro(1000, function() 197 | if BotServer._websocket then 198 | BotServer.send("list") 199 | end 200 | updateStatusText() 201 | delay(9000) 202 | end) 203 | 204 | ui.botServer.onClick = function(widget) 205 | botServerWindow:show() 206 | botServerWindow:raise() 207 | botServerWindow:focus() 208 | end 209 | 210 | botServerWindow.closeButton.onClick = function(widget) 211 | botServerWindow:hide() 212 | end 213 | 214 | 215 | onAddThing(function(tile, thing) 216 | if config.mwallInfo and BotServer._websocket then 217 | if thing:isItem() and thing:getId() == 2129 then 218 | local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z 219 | if not config.mwalls[pos] or config.mwalls[pos] < now then 220 | config.mwalls[pos] = now + 20000 221 | BotServer.send("mwall", {pos=pos, duration=20000}) 222 | end 223 | end 224 | end 225 | end) 226 | 227 | -- mana 228 | local lastMana = 0 229 | macro(500, function() 230 | if config.manaInfo and BotServer._websocket then 231 | if manapercent() ~= lastMana then 232 | lastMana = manapercent() 233 | BotServer.send("mana", {mana=lastMana}) 234 | end 235 | end 236 | end) 237 | 238 | -- vocation 239 | if config.vocation and BotServer._websocket then 240 | BotServer.send("voc", player:getVocation()) 241 | BotServer.send("voc", "yes") 242 | end 243 | 244 | addSeparator() -------------------------------------------------------------------------------- /vBot/BotServer.otui: -------------------------------------------------------------------------------- 1 | BotServerData < Panel 2 | size: 340 70 3 | image-source: /images/ui/window 4 | image-border: 6 5 | padding: 3 6 | 7 | Label 8 | id: label 9 | anchors.top: parent.top 10 | anchors.left: parent.left 11 | anchors.right: parent.right 12 | text-align: center 13 | !text: tr("BotServer Data") 14 | 15 | Label 16 | id: label 17 | anchors.top: parent.top 18 | anchors.left: parent.left 19 | margin-top: 23 20 | text-align: center 21 | text: Channel Name: 22 | margin-left: 6 23 | 24 | TextEdit 25 | id: Channel 26 | anchors.top: parent.top 27 | anchors.left: prev.right 28 | margin-top: 20 29 | width: 150 30 | margin-left: 5 31 | text-align: center 32 | 33 | Button 34 | id: Random 35 | anchors.left: prev.right 36 | anchors.top: prev.top 37 | anchors.right: parent.right 38 | text-align: center 39 | text: Randomize 40 | margin-left: 6 41 | margin-right: 6 42 | 43 | Label 44 | id: label 45 | anchors.left: parent.left 46 | anchors.bottom: parent.bottom 47 | margin-left: 6 48 | margin-bottom: 4 49 | text-align: center 50 | text: Status: 51 | 52 | Label 53 | id: ServerStatus 54 | anchors.left: prev.right 55 | anchors.bottom: parent.bottom 56 | margin-left: 10 57 | width: 150 58 | margin-bottom: 4 59 | text-align: left 60 | 61 | Label 62 | id: Participants 63 | anchors.right: parent.right 64 | anchors.bottom: parent.bottom 65 | width: 20 66 | margin-right: 8 67 | margin-bottom: 4 68 | text-align: center 69 | 70 | UIWidget 71 | id: Members 72 | anchors.right: Participants.left 73 | anchors.bottom: parent.bottom 74 | size: 80 21 75 | text-align: center 76 | text: Members: 77 | 78 | FeaturePanel < Panel 79 | size: 340 150 80 | image-source: /images/ui/panel_flat 81 | image-border: 5 82 | padding: 3 83 | 84 | Label 85 | id: title 86 | anchors.top: parent.top 87 | anchors.horizontalCenter: parent.horizontalCenter 88 | text-align: center 89 | text: Features 90 | 91 | HorizontalSeparator 92 | id: sep 93 | anchors.top: prev.bottom 94 | anchors.left: parent.left 95 | anchors.right: parent.right 96 | margin-top: 2 97 | 98 | BotSwitch 99 | id: Feature1 100 | anchors.top: prev.bottom 101 | anchors.left: parent.left 102 | margin-left: 3 103 | margin-top: 5 104 | text: Mana info 105 | 106 | BotSwitch 107 | id: Feature2 108 | anchors.top: sep.bottom 109 | anchors.left: prev.right 110 | margin-top: 5 111 | margin-left: 5 112 | text: MWall info 113 | 114 | BotSwitch 115 | id: Feature3 116 | anchors.top: sep.bottom 117 | anchors.left: prev.right 118 | margin-top: 5 119 | margin-left: 5 120 | text: Send Vocation 121 | 122 | BotSwitch 123 | id: Feature4 124 | anchors.top: prev.bottom 125 | anchors.left: parent.left 126 | margin-top: 3 127 | margin-left: 3 128 | text: Outfit Vocation 129 | 130 | BotSwitch 131 | id: Feature5 132 | anchors.bottom: prev.bottom 133 | anchors.left: prev.right 134 | margin-top: 3 135 | margin-left: 5 136 | text: Broadcasts 137 | 138 | 139 | TextEdit 140 | id: broadcastText 141 | anchors.bottom: parent.bottom 142 | anchors.left: parent.left 143 | anchors.right: parent.right 144 | margin-left: 3 145 | margin-bottom: 3 146 | margin-right: 80 147 | 148 | Button 149 | id: Broadcast 150 | anchors.top: prev.top 151 | anchors.left: prev.right 152 | anchors.right: parent.right 153 | margin-right: 3 154 | margin-left: 3 155 | height: 22 156 | text: Broadcast 157 | 158 | BotServerWindow < MainWindow 159 | !text: tr('BotServer') 160 | size: 370 310 161 | @onEscape: self:hide() 162 | 163 | BotServerData 164 | id: Data 165 | anchors.top: parent.top 166 | anchors.horizontalCenter: parent.horizontalCenter 167 | 168 | FeaturePanel 169 | id: Features 170 | anchors.top: prev.bottom 171 | anchors.horizontalCenter: parent.horizontalCenter 172 | margin-top: 10 173 | 174 | HorizontalSeparator 175 | id: separator 176 | anchors.right: parent.right 177 | anchors.left: parent.left 178 | anchors.bottom: closeButton.top 179 | margin-bottom: 8 180 | 181 | Button 182 | id: closeButton 183 | !text: tr('Close') 184 | font: cipsoftFont 185 | anchors.right: parent.right 186 | anchors.bottom: parent.bottom 187 | size: 45 21 188 | margin-top: 15 189 | margin-right: 5 190 | 191 | BotSwitch 192 | id: enabled 193 | anchors.verticalCenter: prev.verticalCenter 194 | anchors.left: parent.left 195 | margin-left: 5 196 | height: 21 197 | 198 | $!on: 199 | text: BotServer: OFF 200 | 201 | $on: 202 | text: BotServer: ON -------------------------------------------------------------------------------- /vBot/Dropper.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Tools") 2 | 3 | local ui = setupUI([[ 4 | Panel 5 | height: 19 6 | 7 | BotSwitch 8 | id: title 9 | anchors.top: parent.top 10 | anchors.left: parent.left 11 | text-align: center 12 | width: 130 13 | !text: tr('Dropper') 14 | 15 | Button 16 | id: edit 17 | anchors.top: prev.top 18 | anchors.left: prev.right 19 | anchors.right: parent.right 20 | margin-left: 3 21 | height: 17 22 | text: Edit 23 | ]]) 24 | 25 | local edit = setupUI([[ 26 | Panel 27 | height: 150 28 | 29 | Label 30 | anchors.top: parent.top 31 | anchors.left: parent.left 32 | anchors.right: parent.right 33 | margin-top: 5 34 | text-align: center 35 | text: Trash: 36 | 37 | BotContainer 38 | id: TrashItems 39 | anchors.top: prev.bottom 40 | anchors.left: parent.left 41 | anchors.right: parent.right 42 | height: 32 43 | 44 | Label 45 | anchors.top: prev.bottom 46 | margin-top: 5 47 | anchors.left: parent.left 48 | anchors.right: parent.right 49 | text-align: center 50 | text: Use: 51 | 52 | BotContainer 53 | id: UseItems 54 | anchors.top: prev.bottom 55 | anchors.left: parent.left 56 | anchors.right: parent.right 57 | height: 32 58 | 59 | Label 60 | anchors.top: prev.bottom 61 | margin-top: 5 62 | anchors.left: parent.left 63 | anchors.right: parent.right 64 | text-align: center 65 | text: Drop if below 150 cap: 66 | 67 | BotContainer 68 | id: CapItems 69 | anchors.top: prev.bottom 70 | anchors.left: parent.left 71 | anchors.right: parent.right 72 | height: 32 73 | ]]) 74 | edit:hide() 75 | 76 | if not storage.dropper then 77 | storage.dropper = { 78 | enabled = false, 79 | trashItems = { 283, 284, 285 }, 80 | useItems = { 21203, 14758 }, 81 | capItems = { 21175 } 82 | } 83 | end 84 | 85 | local config = storage.dropper 86 | 87 | local showEdit = false 88 | ui.edit.onClick = function(widget) 89 | showEdit = not showEdit 90 | if showEdit then 91 | edit:show() 92 | else 93 | edit:hide() 94 | end 95 | end 96 | 97 | ui.title:setOn(config.enabled) 98 | ui.title.onClick = function(widget) 99 | config.enabled = not config.enabled 100 | ui.title:setOn(config.enabled) 101 | end 102 | 103 | UI.Container(function() 104 | config.trashItems = edit.TrashItems:getItems() 105 | end, true, nil, edit.TrashItems) 106 | edit.TrashItems:setItems(config.trashItems) 107 | 108 | UI.Container(function() 109 | config.useItems = edit.UseItems:getItems() 110 | end, true, nil, edit.UseItems) 111 | edit.UseItems:setItems(config.useItems) 112 | 113 | UI.Container(function() 114 | config.capItems = edit.CapItems:getItems() 115 | end, true, nil, edit.CapItems) 116 | edit.CapItems:setItems(config.capItems) 117 | 118 | local function properTable(t) 119 | local r = {} 120 | 121 | for _, entry in pairs(t) do 122 | table.insert(r, entry.id) 123 | end 124 | return r 125 | end 126 | 127 | macro(200, function() 128 | if not config.enabled then return end 129 | local tables = {properTable(config.capItems), properTable(config.useItems), properTable(config.trashItems)} 130 | 131 | local containers = getContainers() 132 | for i=1,3 do 133 | for _, container in pairs(containers) do 134 | for __, item in ipairs(container:getItems()) do 135 | for ___, userItem in ipairs(tables[i]) do 136 | if item:getId() == userItem then 137 | return i == 1 and freecap() < 150 and dropItem(item) or 138 | i == 2 and use(item) or 139 | i == 3 and dropItem(item) 140 | end 141 | end 142 | end 143 | end 144 | end 145 | 146 | end) -------------------------------------------------------------------------------- /vBot/Sio.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Main") 2 | local panelName = "advancedFriendHealer" 3 | local ui = setupUI([[ 4 | Panel 5 | height: 19 6 | 7 | BotSwitch 8 | id: title 9 | anchors.top: parent.top 10 | anchors.left: parent.left 11 | text-align: center 12 | width: 130 13 | !text: tr('Friend Healer') 14 | 15 | Button 16 | id: editList 17 | anchors.top: prev.top 18 | anchors.left: prev.right 19 | anchors.right: parent.right 20 | margin-left: 3 21 | height: 17 22 | text: Setup 23 | 24 | ]], parent) 25 | ui:setId(panelName) 26 | 27 | if not storage[panelName] then 28 | storage[panelName] = { 29 | minMana = 60, 30 | minFriendHp = 40, 31 | customSpellName = "exura max sio", 32 | customSpell = false, 33 | distance = 8, 34 | itemHeal = false, 35 | id = 3160, 36 | exuraSio = false, 37 | exuraGranSio = false, 38 | exuraMasRes = false, 39 | healEk = false, 40 | healRp = false, 41 | healEd = false, 42 | healMs = false 43 | } 44 | end 45 | 46 | local config = storage[panelName] 47 | 48 | -- basic elements 49 | ui.title:setOn(config.enabled) 50 | ui.title.onClick = function(widget) 51 | config.enabled = not config.enabled 52 | widget:setOn(config.enabled) 53 | end 54 | ui.editList.onClick = function(widget) 55 | sioListWindow:show() 56 | sioListWindow:raise() 57 | sioListWindow:focus() 58 | end 59 | 60 | rootWidget = g_ui.getRootWidget() 61 | if rootWidget then 62 | sioListWindow = UI.createWindow('SioListWindow', rootWidget) 63 | sioListWindow:hide() 64 | 65 | -- TextWindow 66 | sioListWindow.spellName:setText(config.customSpellName) 67 | sioListWindow.spellName.onTextChange = function(widget, text) 68 | config.customSpellName = text 69 | end 70 | 71 | -- botswitches 72 | sioListWindow.spell:setOn(config.customSpell) 73 | sioListWindow.spell.onClick = function(widget) 74 | config.customSpell = not config.customSpell 75 | widget:setOn(config.customSpell) 76 | end 77 | sioListWindow.item:setOn(config.itemHeal) 78 | sioListWindow.item.onClick = function(widget) 79 | config.itemHeal = not config.itemHeal 80 | widget:setOn(config.itemHeal) 81 | end 82 | sioListWindow.exuraSio:setOn(config.exuraSio) 83 | sioListWindow.exuraSio.onClick = function(widget) 84 | config.exuraSio = not config.exuraSio 85 | widget:setOn(config.exuraSio) 86 | end 87 | sioListWindow.exuraGranSio:setOn(config.exuraGranSio) 88 | sioListWindow.exuraGranSio.onClick = function(widget) 89 | config.exuraGranSio = not config.exuraGranSio 90 | widget:setOn(config.exuraGranSio) 91 | end 92 | sioListWindow.exuraMasRes:setOn(config.exuraMasRes) 93 | sioListWindow.exuraMasRes.onClick = function(widget) 94 | config.exuraMasRes = not config.exuraMasRes 95 | widget:setOn(config.exuraMasRes) 96 | end 97 | sioListWindow.vocation.ED:setOn(config.healEd) 98 | sioListWindow.vocation.ED.onClick = function(widget) 99 | config.healEd = not config.healEd 100 | widget:setOn(config.healEd) 101 | end 102 | sioListWindow.vocation.MS:setOn(config.healMs) 103 | sioListWindow.vocation.MS.onClick = function(widget) 104 | config.healMs = not config.healMs 105 | widget:setOn(config.healMs) 106 | end 107 | sioListWindow.vocation.EK:setOn(config.healEk) 108 | sioListWindow.vocation.EK.onClick = function(widget) 109 | config.healEk = not config.healEk 110 | widget:setOn(config.healEk) 111 | end 112 | sioListWindow.vocation.RP:setOn(config.healRp) 113 | sioListWindow.vocation.RP.onClick = function(widget) 114 | config.healRp = not config.healRp 115 | widget:setOn(config.healRp) 116 | end 117 | 118 | -- functions 119 | local updateMinManaText = function() 120 | sioListWindow.manaInfo:setText("Minimum Mana >= " .. config.minMana .. "%") 121 | end 122 | local updateFriendHpText = function() 123 | sioListWindow.friendHp:setText("Heal Friend Below " .. config.minFriendHp .. "% hp") 124 | end 125 | local updateDistanceText = function() 126 | sioListWindow.distText:setText("Max Distance: " .. config.distance) 127 | end 128 | 129 | -- scrollbars and text updates 130 | sioListWindow.Distance:setValue(config.distance) 131 | sioListWindow.Distance.onValueChange = function(scroll, value) 132 | config.distance = value 133 | updateDistanceText() 134 | end 135 | updateDistanceText() 136 | 137 | sioListWindow.minMana:setValue(config.minMana) 138 | sioListWindow.minMana.onValueChange = function(scroll, value) 139 | config.minMana = value 140 | updateMinManaText() 141 | end 142 | updateMinManaText() 143 | 144 | sioListWindow.minFriendHp:setValue(config.minFriendHp) 145 | sioListWindow.minFriendHp.onValueChange = function(scroll, value) 146 | config.minFriendHp = value 147 | updateFriendHpText() 148 | end 149 | updateFriendHpText() 150 | 151 | sioListWindow.itemId:setItemId(config.id) 152 | sioListWindow.itemId.onItemChange = function(widget) 153 | config.id = widget:getItemId() 154 | end 155 | 156 | sioListWindow.closeButton.onClick = function(widget) 157 | sioListWindow:hide() 158 | end 159 | 160 | end 161 | 162 | -- local variables 163 | local newTibia = g_game.getClientVersion() >= 960 164 | 165 | local function isValid(name) 166 | if not newTibia then return true end 167 | 168 | local voc = vBot.BotServerMembers[name] 169 | if not voc then return true end 170 | 171 | if voc == 11 then voc = 1 172 | elseif voc == 12 then voc = 2 173 | elseif voc == 13 then voc = 3 174 | elseif voc == 14 then voc = 4 175 | end 176 | 177 | local isOk = false 178 | if voc == 1 and config.healEk then 179 | isOk = true 180 | elseif voc == 2 and config.healRp then 181 | isOk = true 182 | elseif voc == 3 and config.healMs then 183 | isOk = true 184 | elseif voc == 4 and config.healEd then 185 | isOk = true 186 | end 187 | 188 | return isOk 189 | end 190 | 191 | macro(200, function() 192 | if not config.enabled then return end 193 | if modules.game_cooldown.isGroupCooldownIconActive(2) then return end 194 | 195 | --[[ 196 | 1. custom spell 197 | 2. exura gran sio - at 50% of minHpValue 198 | 3. exura gran mas res 199 | 4. exura sio 200 | 5. item healing 201 | --]] 202 | 203 | -- exura gran sio & custom spell 204 | if config.customSpell or config.exuraGranSio then 205 | for i, spec in ipairs(getSpectators()) do 206 | if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then 207 | if isFriend(spec) then 208 | if config.customSpell and spec:getHealthPercent() <= config.minFriendHp then 209 | return cast(config.customSpellName .. ' "' .. spec:getName() .. '"', 1000) 210 | end 211 | if config.exuraGranSio and spec:getHealthPercent() <= config.minFriendHp/3 then 212 | if canCast('exura gran sio "' .. spec:getName() ..'"') then 213 | return cast('exura gran sio "' .. spec:getName() ..'"', 60000) 214 | end 215 | end 216 | end 217 | end 218 | end 219 | end 220 | 221 | -- exura gran mas res and standard sio 222 | local friends = 0 223 | if config.exuraMasRes then 224 | for i, spec in ipairs(getSpectators(player, largeRuneArea)) do 225 | if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then 226 | if isFriend(spec) and spec:getHealthPercent() <= config.minFriendHp then 227 | friends = friends + 1 228 | end 229 | end 230 | end 231 | if friends > 1 then 232 | return cast('exura gran mas res', 2000) 233 | end 234 | end 235 | if config.exuraSio or config.itemHeal then 236 | for i, spec in ipairs(getSpectators()) do 237 | if spec:isPlayer() and spec ~= player and isValid(spec:getName()) and spec:canShoot() then 238 | if isFriend(spec) then 239 | if spec:getHealthPercent() <= config.minFriendHp then 240 | if config.exuraSio then 241 | return cast('exura sio "' .. spec:getName() .. '"', 1000) 242 | elseif findItem(config.id) and distanceFromPlayer(spec:getPosition()) <= config.distance then 243 | return useWith(config.id, spec) 244 | end 245 | end 246 | end 247 | end 248 | end 249 | end 250 | 251 | end) 252 | addSeparator() -------------------------------------------------------------------------------- /vBot/alarms.lua: -------------------------------------------------------------------------------- 1 | local panelName = "alarms" 2 | local ui = setupUI([[ 3 | Panel 4 | height: 19 5 | 6 | BotSwitch 7 | id: title 8 | anchors.top: parent.top 9 | anchors.left: parent.left 10 | text-align: center 11 | width: 130 12 | !text: tr('Alarms') 13 | 14 | Button 15 | id: alerts 16 | anchors.top: prev.top 17 | anchors.left: prev.right 18 | anchors.right: parent.right 19 | margin-left: 3 20 | height: 17 21 | text: Edit 22 | 23 | ]]) 24 | ui:setId(panelName) 25 | 26 | if not storage[panelName] then 27 | storage[panelName] = {} 28 | end 29 | 30 | local config = storage[panelName] 31 | 32 | ui.title:setOn(config.enabled) 33 | ui.title.onClick = function(widget) 34 | config.enabled = not config.enabled 35 | widget:setOn(config.enabled) 36 | end 37 | 38 | local window = UI.createWindow("AlarmsWindow") 39 | window:hide() 40 | 41 | ui.alerts.onClick = function() 42 | window:show() 43 | window:raise() 44 | window:focus() 45 | end 46 | 47 | local widgets = 48 | { 49 | "AlarmCheckBox", 50 | "AlarmCheckBoxAndSpinBox", 51 | "AlarmCheckBoxAndTextEdit" 52 | } 53 | 54 | local parents = 55 | { 56 | window.list, 57 | window.settingsList 58 | } 59 | 60 | 61 | -- type 62 | addAlarm = function(id, title, defaultValue, alarmType, parent, tooltip) 63 | local widget = UI.createWidget(widgets[alarmType], parents[parent]) 64 | widget:setId(id) 65 | 66 | if type(config[id]) ~= 'table' then 67 | config[id] = {} 68 | end 69 | 70 | widget.tick:setText(title) 71 | widget.tick:setChecked(config[id].enabled) 72 | widget.tick:setTooltip(tooltip) 73 | widget.tick.onClick = function() 74 | config[id].enabled = not config[id].enabled 75 | widget.tick:setChecked(config[id].enabled) 76 | end 77 | 78 | if alarmType > 1 and type(config[id].value) == 'nil' then 79 | config[id].value = defaultValue 80 | end 81 | 82 | if alarmType == 2 then 83 | widget.value:setValue(config[id].value) 84 | widget.value.onValueChange = function(widget, value) 85 | config[id].value = value 86 | end 87 | elseif alarmType == 3 then 88 | widget.text:setText(config[id].value) 89 | widget.text.onTextChange = function(widget, newText) 90 | config[id].value = newText 91 | end 92 | end 93 | 94 | end 95 | 96 | -- settings 97 | addAlarm("ignoreFriends", "Ignore Friends", true, 1, 2) 98 | addAlarm("flashClient", "Flash Client", true, 1, 2) 99 | 100 | -- alarm list 101 | addAlarm("damageTaken", "Damage Taken", false, 1, 1) 102 | addAlarm("lowHealth", "Low Health", 20, 2, 1) 103 | addAlarm("lowMana", "Low Mana", 20, 2, 1) 104 | addAlarm("playerAttack", "Player Attack", false, 1, 1) 105 | 106 | UI.Separator(window.list) 107 | 108 | addAlarm("privateMsg", "Private Message", false, 1, 1) 109 | addAlarm("defaultMsg", "Default Message", false, 1, 1) 110 | 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.") 111 | 112 | UI.Separator(window.list) 113 | 114 | addAlarm("creatureDetected", "Creature Detected", false, 1, 1) 115 | addAlarm("playerDetected", "Player Detected", false, 1, 1) 116 | 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.") 117 | 118 | 119 | local lastCall = now 120 | local function alarm(file, windowText) 121 | if now - lastCall < 2000 then return end -- 2s delay 122 | lastCall = now 123 | 124 | if not g_resources.fileExists(file) then 125 | file = "/sounds/alarm.ogg" 126 | lastCall = now + 4000 -- alarm.ogg length is 6s 127 | end 128 | 129 | 130 | if modules.game_bot.g_app.getOs() == "windows" and config.flashClient.enabled then 131 | g_window.flash() 132 | end 133 | g_window.setTitle(player:getName() .. " - " .. windowText) 134 | playSound(file) 135 | end 136 | 137 | -- damage taken & custom message 138 | onTextMessage(function(mode, text) 139 | if not config.enabled then return end 140 | if mode == 22 and config.damageTaken.enabled then 141 | return alarm('/sounds/magnum.ogg', "Damage Received!") 142 | end 143 | 144 | if config.customMessage.enabled then 145 | local alertText = config.customMessage.value 146 | if alertText:len() > 0 then 147 | text = text:lower() 148 | local parts = string.split(alertText, ",") 149 | 150 | for i=1,#parts do 151 | local part = parts[i] 152 | part = part:trim() 153 | part = part:lower() 154 | 155 | if text:find(part) then 156 | return alarm('/sounds/magnum.ogg', "Special Message!") 157 | end 158 | end 159 | end 160 | end 161 | end) 162 | 163 | -- default & private message 164 | onTalk(function(name, level, mode, text, channelId, pos) 165 | if not config.enabled then return end 166 | if name == player:getName() then return end -- ignore self messages 167 | if config.ignoreFriends.enabled and isFriend(name) then return end -- ignore friends if enabled 168 | 169 | if mode == 1 and config.defaultMsg.enabled then 170 | return alarm("/sounds/magnum.ogg", "Default Message!") 171 | end 172 | 173 | if mode == 4 and config.privateMsg.enabled then 174 | return alarm("/sounds/Private_Message.ogg", "Private Message!") 175 | end 176 | end) 177 | 178 | -- health & mana 179 | macro(100, function() 180 | if not config.enabled then return end 181 | if config.lowHealth.enabled then 182 | if hppercent() < config.lowHealth.value then 183 | return alarm("/sounds/Low_Health.ogg", "Low Health!") 184 | end 185 | end 186 | 187 | if config.lowMana.enabled then 188 | if hppercent() < config.lowMana.value then 189 | return alarm("/sounds/Low_Mana.ogg", "Low Mana!") 190 | end 191 | end 192 | 193 | for i, spec in ipairs(getSpectators()) do 194 | if not spec:isLocalPlayer() and not (config.ignoreFriends.enabled and isFriend(spec)) then 195 | 196 | if config.creatureDetected.enabled then 197 | return alarm("/sounds/magnum.ogg", "Creature Detected!") 198 | end 199 | 200 | if spec:isPlayer() then 201 | if spec:isTimedSquareVisible() and config.playerAttack.enabled then 202 | return alarm("/sounds/Player_Attack.ogg", "Player Attack!") 203 | end 204 | if config.playerDetected.enabled then 205 | return alarm("/sounds/Player_Detected.ogg", "Player Detected!") 206 | end 207 | end 208 | 209 | if config.creatureName.enabled then 210 | local name = spec:getName():lower() 211 | local fragments = string.split(config.creatureName.value, ",") 212 | 213 | for i=1,#fragments do 214 | local frag = fragments[i]:trim():lower() 215 | 216 | if name:lower():find(frag) then 217 | return alarm("/sounds/alarm.ogg", "Special Creature Detected!") 218 | end 219 | end 220 | end 221 | end 222 | end 223 | end) -------------------------------------------------------------------------------- /vBot/alarms.otui: -------------------------------------------------------------------------------- 1 | AlarmCheckBox < Panel 2 | height: 20 3 | margin-top: 2 4 | 5 | CheckBox 6 | id: tick 7 | anchors.fill: parent 8 | margin-top: 4 9 | font: verdana-11px-rounded 10 | text: Player Attack 11 | text-offset: 17 -3 12 | 13 | AlarmCheckBoxAndSpinBox < Panel 14 | height: 20 15 | margin-top: 2 16 | 17 | CheckBox 18 | id: tick 19 | anchors.fill: parent 20 | anchors.right: next.left 21 | margin-top: 4 22 | font: verdana-11px-rounded 23 | text: Player Attack 24 | text-offset: 17 -3 25 | 26 | SpinBox 27 | id: value 28 | anchors.top: parent.top 29 | margin-top: 1 30 | margin-bottom: 1 31 | anchors.bottom: parent.bottom 32 | anchors.right: parent.right 33 | width: 40 34 | minimum: 0 35 | maximum: 100 36 | step: 1 37 | editable: true 38 | focusable: true 39 | 40 | AlarmCheckBoxAndTextEdit < Panel 41 | height: 20 42 | margin-top: 2 43 | 44 | CheckBox 45 | id: tick 46 | anchors.fill: parent 47 | anchors.right: next.left 48 | margin-top: 4 49 | font: verdana-11px-rounded 50 | text: Creature Name 51 | text-offset: 17 -3 52 | 53 | BotTextEdit 54 | id: text 55 | anchors.right: parent.right 56 | anchors.top: parent.top 57 | anchors.bottom: parent.bottom 58 | width: 150 59 | font: terminus-10px 60 | margin-top: 1 61 | margin-bottom: 1 62 | 63 | AlarmsWindow < MainWindow 64 | !text: tr('Alarms') 65 | size: 330 400 66 | padding: 15 67 | @onEscape: self:hide() 68 | 69 | FlatPanel 70 | id: list 71 | anchors.fill: parent 72 | anchors.bottom: settingsList.top 73 | margin-bottom: 20 74 | margin-top: 10 75 | layout: verticalBox 76 | padding: 10 77 | padding-top: 5 78 | 79 | FlatPanel 80 | id: settingsList 81 | anchors.left: parent.left 82 | anchors.right: parent.right 83 | anchors.bottom: separator.top 84 | margin-bottom: 5 85 | margin-top: 10 86 | padding: 5 87 | padding-left: 10 88 | layout: 89 | type: verticalBox 90 | fit-children: true 91 | 92 | Label 93 | anchors.verticalCenter: settingsList.top 94 | anchors.left: settingsList.left 95 | margin-left: 5 96 | width: 200 97 | text: Alarms Settings 98 | font: verdana-11px-rounded 99 | color: #9f5031 100 | 101 | Label 102 | anchors.verticalCenter: list.top 103 | anchors.left: list.left 104 | margin-left: 5 105 | width: 200 106 | text: Active Alarms 107 | font: verdana-11px-rounded 108 | color: #9f5031 109 | 110 | HorizontalSeparator 111 | id: separator 112 | anchors.right: parent.right 113 | anchors.left: parent.left 114 | anchors.bottom: closeButton.top 115 | margin-bottom: 8 116 | 117 | ResizeBorder 118 | id: bottomResizeBorder 119 | anchors.fill: separator 120 | height: 3 121 | minimum: 260 122 | maximum: 600 123 | margin-left: 3 124 | margin-right: 3 125 | background: #ffffff88 126 | 127 | Button 128 | id: closeButton 129 | !text: tr('Close') 130 | font: cipsoftFont 131 | anchors.right: parent.right 132 | anchors.bottom: parent.bottom 133 | size: 45 21 134 | margin-right: 5 135 | @onClick: self:getParent():hide() -------------------------------------------------------------------------------- /vBot/antiRs.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Tools") 2 | g_game.cancelAttackAndFollow() 3 | 4 | local frags = 0 5 | local unequip = false 6 | local m = macro(50, "AntiRS & Msg", function() end) 7 | 8 | function safeExit() 9 | CaveBot.setOff() 10 | TargetBot.setOff() 11 | g_game.cancelAttackAndFollow() 12 | g_game.cancelAttackAndFollow() 13 | g_game.cancelAttackAndFollow() 14 | modules.game_interface.forceExit() 15 | end 16 | 17 | onTextMessage(function(mode, text) 18 | if not m.isOn() then return end 19 | if not text:find("Warning! The murder of") then return end 20 | frags = frags + 1 21 | if killsToRs() < 6 or frags > 1 then 22 | EquipManager.setOff() 23 | schedule(100, function() 24 | local id = getLeft() and getLeft():getId() 25 | 26 | if id and not unequip then 27 | unequip = true 28 | g_game.equipItemId(id) 29 | end 30 | safeExit() 31 | end) 32 | end 33 | end) -------------------------------------------------------------------------------- /vBot/cast_food.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("HP") 2 | if voc() ~= 1 and voc() ~= 11 then 3 | if storage.foodItems then 4 | local t = {} 5 | for i, v in pairs(storage.foodItems) do 6 | if not table.find(t, v.id) then 7 | table.insert(t, v.id) 8 | end 9 | end 10 | local foodItems = { 3607, 3585, 3592, 3600, 3601 } 11 | for i, item in pairs(foodItems) do 12 | if not table.find(t, item) then 13 | table.insert(storage.foodItems, item) 14 | end 15 | end 16 | end 17 | macro(500, "Cast Food", function() 18 | if player:getRegenerationTime() <= 400 then 19 | cast("exevo pan", 5000) 20 | end 21 | end) 22 | end -------------------------------------------------------------------------------- /vBot/cavebot.lua: -------------------------------------------------------------------------------- 1 | -- Cavebot by otclient@otclient.ovh 2 | -- visit http://bot.otclient.ovh/ 3 | 4 | local cavebotTab = "Cave" 5 | local targetingTab = storage.extras.joinBot and "Cave" or "Target" 6 | 7 | setDefaultTab(cavebotTab) 8 | CaveBot.Extensions = {} 9 | importStyle("/cavebot/cavebot.otui") 10 | importStyle("/cavebot/config.otui") 11 | importStyle("/cavebot/editor.otui") 12 | dofile("/cavebot/actions.lua") 13 | dofile("/cavebot/config.lua") 14 | dofile("/cavebot/editor.lua") 15 | dofile("/cavebot/example_functions.lua") 16 | dofile("/cavebot/recorder.lua") 17 | dofile("/cavebot/walking.lua") 18 | dofile("/cavebot/minimap.lua") 19 | -- in this section you can add extensions, check extension_template.lua 20 | --dofile("/cavebot/extension_template.lua") 21 | dofile("/cavebot/sell_all.lua") 22 | dofile("/cavebot/depositor.lua") 23 | dofile("/cavebot/buy_supplies.lua") 24 | dofile("/cavebot/d_withdraw.lua") 25 | dofile("/cavebot/supply_check.lua") 26 | dofile("/cavebot/travel.lua") 27 | dofile("/cavebot/doors.lua") 28 | dofile("/cavebot/pos_check.lua") 29 | dofile("/cavebot/withdraw.lua") 30 | dofile("/cavebot/inbox_withdraw.lua") 31 | dofile("/cavebot/lure.lua") 32 | dofile("/cavebot/bank.lua") 33 | dofile("/cavebot/clear_tile.lua") 34 | dofile("/cavebot/tasker.lua") 35 | dofile("/cavebot/imbuing.lua") 36 | dofile("/cavebot/stand_lure.lua") 37 | -- main cavebot file, must be last 38 | dofile("/cavebot/cavebot.lua") 39 | 40 | setDefaultTab(targetingTab) 41 | if storage.extras.joinBot then UI.Label("-- [[ TargetBot ]] --") end 42 | TargetBot = {} -- global namespace 43 | importStyle("/targetbot/looting.otui") 44 | importStyle("/targetbot/target.otui") 45 | importStyle("/targetbot/creature_editor.otui") 46 | dofile("/targetbot/creature.lua") 47 | dofile("/targetbot/creature_attack.lua") 48 | dofile("/targetbot/creature_editor.lua") 49 | dofile("/targetbot/creature_priority.lua") 50 | dofile("/targetbot/looting.lua") 51 | dofile("/targetbot/walking.lua") 52 | -- main targetbot file, must be last 53 | dofile("/targetbot/target.lua") 54 | -------------------------------------------------------------------------------- /vBot/cavebot_control_panel.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Cave") 2 | 3 | g_ui.loadUIFromString([[ 4 | CaveBotControlPanel < Panel 5 | margin-top: 5 6 | layout: 7 | type: verticalBox 8 | fit-children: true 9 | 10 | HorizontalSeparator 11 | 12 | Label 13 | text-align: center 14 | text: CaveBot Control Panel 15 | font: verdana-11px-rounded 16 | margin-top: 3 17 | 18 | HorizontalSeparator 19 | 20 | Panel 21 | id: buttons 22 | margin-top: 2 23 | layout: 24 | type: grid 25 | cell-size: 86 20 26 | cell-spacing: 1 27 | flow: true 28 | fit-children: true 29 | 30 | HorizontalSeparator 31 | margin-top: 3 32 | ]]) 33 | 34 | local panel = UI.createWidget("CaveBotControlPanel") 35 | 36 | storage.caveBot = { 37 | forceRefill = false, 38 | backStop = false, 39 | backTrainers = false, 40 | backOffline = false 41 | } 42 | 43 | -- [[ B U T T O N S ]] -- 44 | 45 | local forceRefill = UI.Button("Force Refill", function(widget) 46 | storage.caveBot.forceRefill = true 47 | print("[CaveBot] Going back on refill on next supply check.") 48 | end, panel.buttons) 49 | 50 | local backStop = UI.Button("Back & Stop", function(widget) 51 | storage.caveBot.backStop = true 52 | print("[CaveBot] Going back to city on next supply check and turning off CaveBot on depositer action.") 53 | end, panel.buttons) 54 | 55 | local backTrainers = UI.Button("To Trainers", function(widget) 56 | storage.caveBot.backTrainers = true 57 | print("[CaveBot] Going back to city on next supply check and going to label 'toTrainers' on depositer action.") 58 | end, panel.buttons) 59 | 60 | local backOffline = UI.Button("Offline", function(widget) 61 | storage.caveBot.backOffline = true 62 | print("[CaveBot] Going back to city on next supply check and going to label 'toOfflineTraining' on depositer action.") 63 | end, panel.buttons) -------------------------------------------------------------------------------- /vBot/combo.otui: -------------------------------------------------------------------------------- 1 | AttackComboBoxPopupMenu < ComboBoxPopupMenu 2 | AttackComboBoxPopupMenuButton < ComboBoxPopupMenuButton 3 | AttackComboBox < ComboBox 4 | @onSetup: | 5 | self:addOption("LEADER TARGET") 6 | self:addOption("COMMAND TARGET") 7 | 8 | FollowComboBoxPopupMenu < ComboBoxPopupMenu 9 | FollowComboBoxPopupMenuButton < ComboBoxPopupMenuButton 10 | FollowComboBox < ComboBox 11 | @onSetup: | 12 | self:addOption("LEADER TARGET") 13 | self:addOption("SERVER LEADER TARGET") 14 | self:addOption("LEADER") 15 | self:addOption("SERVER LEADER") 16 | 17 | ComboTrigger < Panel 18 | id: trigger 19 | image-source: /images/ui/panel_flat 20 | image-border: 6 21 | padding: 3 22 | size: 450 72 23 | 24 | Label 25 | id: triggerLabel1 26 | anchors.left: parent.left 27 | anchors.top: parent.top 28 | text: On Say 29 | margin-top: 8 30 | margin-left: 5 31 | color: #ffaa00 32 | 33 | Label 34 | id: leaderLabel 35 | anchors.left: triggerLabel1.right 36 | anchors.top: triggerLabel1.top 37 | text: Leader: 38 | margin-left: 35 39 | 40 | TextEdit 41 | id: onSayLeader 42 | anchors.left: leaderLabel.right 43 | anchors.top: leaderLabel.top 44 | anchors.bottom: leaderLabel.bottom 45 | margin-left: 5 46 | width: 120 47 | font: cipsoftFont 48 | 49 | Label 50 | id: phrase 51 | anchors.left: onSayLeader.right 52 | anchors.top: onSayLeader.top 53 | text: Phrase: 54 | margin-left: 5 55 | 56 | TextEdit 57 | id: onSayPhrase 58 | anchors.left: phrase.right 59 | anchors.top: leaderLabel.top 60 | anchors.bottom: leaderLabel.bottom 61 | margin-left: 5 62 | width: 120 63 | font: cipsoftFont 64 | 65 | CheckBox 66 | id: onSayToggle 67 | anchors.left: onSayPhrase.right 68 | anchors.top: onSayPhrase.top 69 | margin-top: 1 70 | margin-left: 5 71 | 72 | Label 73 | id: triggerLabel2 74 | anchors.left: triggerLabel1.left 75 | anchors.top: triggerLabel1.bottom 76 | text: On Shoot 77 | margin-top: 5 78 | color: #ffaa00 79 | 80 | Label 81 | id: leaderLabel1 82 | anchors.left: triggerLabel2.right 83 | anchors.top: triggerLabel2.top 84 | text: Leader: 85 | margin-left: 24 86 | 87 | TextEdit 88 | id: onShootLeader 89 | anchors.left: leaderLabel1.right 90 | anchors.top: leaderLabel1.top 91 | anchors.bottom: leaderLabel1.bottom 92 | anchors.right: onSayPhrase.right 93 | margin-left: 5 94 | width: 120 95 | font: cipsoftFont 96 | 97 | CheckBox 98 | id: onShootToggle 99 | anchors.left: onShootLeader.right 100 | anchors.top: onShootLeader.top 101 | margin-top: 1 102 | margin-left: 5 103 | 104 | Label 105 | id: triggerLabel3 106 | anchors.left: triggerLabel2.left 107 | anchors.top: triggerLabel2.bottom 108 | text: On Cast 109 | margin-top: 5 110 | color: #ffaa00 111 | 112 | Label 113 | id: leaderLabel2 114 | anchors.left: triggerLabel3.right 115 | anchors.top: triggerLabel3.top 116 | text: Leader: 117 | margin-left: 32 118 | 119 | TextEdit 120 | id: onCastLeader 121 | anchors.left: leaderLabel2.right 122 | anchors.top: leaderLabel2.top 123 | anchors.bottom: leaderLabel2.bottom 124 | anchors.right: onSayPhrase.right 125 | margin-left: 5 126 | width: 120 127 | font: cipsoftFont 128 | 129 | CheckBox 130 | id: onCastToggle 131 | anchors.left: onCastLeader.right 132 | anchors.top: onCastLeader.top 133 | margin-top: 1 134 | margin-left: 5 135 | 136 | ComboActions < Panel 137 | id: actions 138 | image-source: /images/ui/panel_flat 139 | image-border: 6 140 | padding: 3 141 | size: 220 100 142 | 143 | Label 144 | id: label1 145 | anchors.left: parent.left 146 | anchors.top: parent.top 147 | text: Follow: 148 | margin-top: 5 149 | margin-left: 3 150 | height: 15 151 | color: #ffaa00 152 | 153 | FollowComboBox 154 | id: followLeader 155 | anchors.left: prev.right 156 | anchors.top: prev.top 157 | margin-left: 7 158 | height: 15 159 | width: 145 160 | font: cipsoftFont 161 | 162 | CheckBox 163 | id: followLeaderToggle 164 | anchors.left: followLeader.right 165 | anchors.top: followLeader.top 166 | margin-top: 2 167 | margin-left: 5 168 | 169 | Label 170 | id: label2 171 | anchors.left: label1.left 172 | anchors.top: label1.bottom 173 | margin-top: 5 174 | text: Attack: 175 | color: #ffaa00 176 | 177 | AttackComboBox 178 | id: attackLeaderTarget 179 | anchors.left: prev.right 180 | anchors.top: prev.top 181 | margin-left: 5 182 | height: 15 183 | width: 145 184 | font: cipsoftFont 185 | 186 | CheckBox 187 | id: attackLeaderTargetToggle 188 | anchors.left: attackLeaderTarget.right 189 | anchors.top: attackLeaderTarget.top 190 | margin-top: 2 191 | margin-left: 5 192 | 193 | Label 194 | id: label3 195 | anchors.left: label2.left 196 | anchors.top: label2.bottom 197 | margin-top: 5 198 | text: Spell: 199 | color: #ffaa00 200 | 201 | TextEdit 202 | id: attackSpell 203 | anchors.left: prev.right 204 | anchors.top: prev.top 205 | anchors.right: attackLeaderTarget.right 206 | margin-left: 17 207 | height: 15 208 | width: 145 209 | font: cipsoftFont 210 | 211 | CheckBox 212 | id: attackSpellToggle 213 | anchors.left: attackSpell.right 214 | anchors.top: attackSpell.top 215 | margin-top: 2 216 | margin-left: 5 217 | 218 | Label 219 | id: label4 220 | anchors.left: label3.left 221 | anchors.top: label3.bottom 222 | margin-top: 15 223 | text: Attack Item: 224 | color: #ffaa00 225 | 226 | BotItem 227 | id: attackItem 228 | anchors.left: prev.right 229 | anchors.verticalCenter: prev.verticalCenter 230 | margin-left: 10 231 | 232 | CheckBox 233 | id: attackItemToggle 234 | anchors.left: prev.right 235 | anchors.verticalCenter: prev.verticalCenter 236 | margin-left: 5 237 | 238 | BotSwitch 239 | id: commandsToggle 240 | anchors.left: prev.right 241 | anchors.top: attackItem.top 242 | anchors.right: attackSpellToggle.right 243 | anchors.bottom: attackItem.bottom 244 | margin-left: 5 245 | text: Leader Commands 246 | text-wrap: true 247 | multiline: true 248 | 249 | BotServer < Panel 250 | id: server 251 | image-source: /images/ui/panel_flat 252 | image-border: 6 253 | padding: 3 254 | size: 220 100 255 | 256 | Label 257 | id: labelX 258 | anchors.left: parent.left 259 | anchors.top: parent.top 260 | text: Leader: 261 | height: 15 262 | color: #ffaa00 263 | margin-left: 3 264 | margin-top: 5 265 | 266 | TextEdit 267 | id: botServerLeader 268 | anchors.left: prev.right 269 | anchors.top: prev.top 270 | anchors.right: parent.right 271 | margin-right: 3 272 | margin-left: 9 273 | height: 15 274 | font: cipsoftFont 275 | 276 | Button 277 | id: partyButton 278 | anchors.left: labelX.left 279 | anchors.top: botServerLeader.bottom 280 | margin-top: 5 281 | height: 30 282 | text: Join Party 283 | text-wrap: true 284 | multiline: true 285 | 286 | BotSwitch 287 | id: botServerToggle 288 | anchors.left: prev.right 289 | anchors.top: botServerLeader.bottom 290 | anchors.right: parent.right 291 | height: 30 292 | margin-left: 3 293 | margin-right: 3 294 | margin-top: 5 295 | text: Server Enabled 296 | 297 | BotSwitch 298 | id: targetServerLeaderToggle 299 | anchors.left: partyButton.left 300 | anchors.top: partyButton.bottom 301 | anchors.right: partyButton.right 302 | margin-top: 3 303 | height: 30 304 | text: Leader Targets 305 | 306 | BotSwitch 307 | id: Triggers 308 | anchors.left: prev.right 309 | anchors.top: partyButton.bottom 310 | anchors.right: parent.right 311 | margin-top: 3 312 | height: 30 313 | margin-left: 3 314 | margin-right: 3 315 | text: Triggers 316 | 317 | ComboWindow < MainWindow 318 | !text: tr('Combo Options') 319 | size: 500 280 320 | @onEscape: self:hide() 321 | 322 | ComboTrigger 323 | id: trigger 324 | anchors.top: parent.top 325 | anchors.horizontalCenter: parent.horizontalCenter 326 | margin-top: 7 327 | 328 | Label 329 | id: title 330 | anchors.top: parent.top 331 | anchors.left: parent.left 332 | margin-left: 10 333 | text: Combo Trigger 334 | color: #ff7700 335 | 336 | ComboActions 337 | id: actions 338 | anchors.top: trigger.bottom 339 | anchors.left: trigger.left 340 | margin-top: 15 341 | 342 | Label 343 | id: title 344 | anchors.top: parent.top 345 | anchors.left: parent.left 346 | margin-left: 10 347 | margin-top: 85 348 | text: Combo Actions 349 | color: #ff7700 350 | 351 | BotServer 352 | id: server 353 | anchors.top: actions.top 354 | anchors.left: actions.right 355 | margin-left: 10 356 | 357 | Label 358 | id: title 359 | anchors.top: parent.top 360 | anchors.left: server.left 361 | margin-left: 3 362 | margin-top: 85 363 | text: BotServer 364 | color: #ff7700 365 | 366 | HorizontalSeparator 367 | id: separator 368 | anchors.right: parent.right 369 | anchors.left: parent.left 370 | anchors.bottom: closeButton.top 371 | margin-bottom: 8 372 | 373 | Button 374 | id: closeButton 375 | !text: tr('Close') 376 | font: cipsoftFont 377 | anchors.right: parent.right 378 | anchors.bottom: parent.bottom 379 | size: 45 21 380 | margin-top: 15 381 | margin-right: 5 382 | 383 | Button 384 | id: toolsButton 385 | !text: tr('Help') 386 | font: cipsoftFont 387 | anchors.right: closeButton.left 388 | anchors.top: closeButton.top 389 | margin-right: 10 390 | size: 45 21 391 | @onClick: g_platform.openUrl("http://bot.otclient.ovh/books/scripts/page/combobot") -------------------------------------------------------------------------------- /vBot/configs.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Configs for modules 3 | Based on Kondrah storage method 4 | --]] 5 | local configName = modules.game_bot.contentsPanel.config:getCurrentOption().text 6 | 7 | -- make vBot config dir 8 | if not g_resources.directoryExists("/bot/".. configName .."/vBot_configs/") then 9 | g_resources.makeDir("/bot/".. configName .."/vBot_configs/") 10 | end 11 | 12 | -- make profile dirs 13 | for i=1,10 do 14 | local path = "/bot/".. configName .."/vBot_configs/profile_"..i 15 | if not g_resources.directoryExists(path) then 16 | g_resources.makeDir(path) 17 | end 18 | end 19 | 20 | local profile = g_settings.getNumber('profile') 21 | 22 | HealBotConfig = {} 23 | local healBotFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/HealBot.json" 24 | AttackBotConfig = {} 25 | local attackBotFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/AttackBot.json" 26 | SuppliesConfig = {} 27 | local suppliesFile = "/bot/" .. configName .. "/vBot_configs/profile_".. profile .. "/Supplies.json" 28 | 29 | 30 | --healbot 31 | if g_resources.fileExists(healBotFile) then 32 | local status, result = pcall(function() 33 | return json.decode(g_resources.readFileContents(healBotFile)) 34 | end) 35 | if not status then 36 | return onError("Error while reading config file (" .. healBotFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result) 37 | end 38 | HealBotConfig = result 39 | end 40 | 41 | --attackbot 42 | if g_resources.fileExists(attackBotFile) then 43 | local status, result = pcall(function() 44 | return json.decode(g_resources.readFileContents(attackBotFile)) 45 | end) 46 | if not status then 47 | return onError("Error while reading config file (" .. attackBotFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result) 48 | end 49 | AttackBotConfig = result 50 | end 51 | 52 | --supplies 53 | if g_resources.fileExists(suppliesFile) then 54 | local status, result = pcall(function() 55 | return json.decode(g_resources.readFileContents(suppliesFile)) 56 | end) 57 | if not status then 58 | return onError("Error while reading config file (" .. suppliesFile .. "). To fix this problem you can delete HealBot.json. Details: " .. result) 59 | end 60 | SuppliesConfig = result 61 | end 62 | 63 | function vBotConfigSave(file) 64 | -- file can be either 65 | --- heal 66 | --- atk 67 | --- supply 68 | local configFile 69 | local configTable 70 | if not file then return end 71 | file = file:lower() 72 | if file == "heal" then 73 | configFile = healBotFile 74 | configTable = HealBotConfig 75 | elseif file == "atk" then 76 | configFile = attackBotFile 77 | configTable = AttackBotConfig 78 | elseif file == "supply" then 79 | configFile = suppliesFile 80 | configTable = SuppliesConfig 81 | else 82 | return 83 | end 84 | 85 | local status, result = pcall(function() 86 | return json.encode(configTable, 2) 87 | end) 88 | if not status then 89 | return onError("Error while saving config. it won't be saved. Details: " .. result) 90 | end 91 | 92 | if result:len() > 100 * 1024 * 1024 then 93 | return onError("config file is too big, above 100MB, it won't be saved") 94 | end 95 | 96 | g_resources.writeFileContents(configFile, result) 97 | end -------------------------------------------------------------------------------- /vBot/depositer_config.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Cave") 2 | local panelName = "specialDeposit" 3 | local depositerPanel 4 | 5 | UI.Button("Stashing Settings", function() 6 | depositerPanel:show() 7 | depositerPanel:raise() 8 | depositerPanel:focus() 9 | end) 10 | 11 | if not storage[panelName] then 12 | storage[panelName] = { 13 | items = {}, 14 | height = 380 15 | } 16 | end 17 | 18 | local config = storage[panelName] 19 | 20 | depositerPanel = UI.createWindow('DepositerPanel', rootWidget) 21 | depositerPanel:hide() 22 | -- basic one 23 | depositerPanel.CloseButton.onClick = function() 24 | depositerPanel:hide() 25 | end 26 | 27 | depositerPanel:setHeight(config.height or 380) 28 | depositerPanel.onGeometryChange = function(widget, old, new) 29 | if old.height == 0 then return end 30 | config.height = new.height 31 | end 32 | 33 | function arabicToRoman(n) 34 | local t = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XI", "XII", "XIV", "XV", "XVI", "XVII"} 35 | return t[n] 36 | end 37 | 38 | local function refreshEntries() 39 | depositerPanel.DepositerList:destroyChildren() 40 | for _, entry in ipairs(config.items) do 41 | local panel = g_ui.createWidget("StashItem", depositerPanel.DepositerList) 42 | panel.name:setText(Item.create(entry.id):getMarketData().name) 43 | for i, child in ipairs(panel:getChildren()) do 44 | if child:getId() ~= "slot" then 45 | child:setTooltip("Clear item or double click to remove entry.") 46 | child.onDoubleClick = function(widget) 47 | table.remove(config.items, table.find(entry)) 48 | panel:destroy() 49 | end 50 | end 51 | end 52 | panel.item:setItemId(entry.id) 53 | if entry.id > 0 then 54 | panel.item:setImageSource('') 55 | end 56 | panel.item.onItemChange = function(widget) 57 | local id = widget:getItemId() 58 | if id < 100 then 59 | table.remove(config.items, table.find(entry)) 60 | panel:destroy() 61 | else 62 | for i, data in ipairs(config.items) do 63 | if data.id == id then 64 | warn("[Depositer Panel] Item already added!") 65 | return 66 | end 67 | end 68 | entry.id = id 69 | panel.item:setImageSource('') 70 | panel.name:setText(Item.create(entry.id):getMarketData().name) 71 | if entry.index == 0 then 72 | local window = modules.client_textedit.show(panel.slot, { 73 | title = "Set depot for "..panel.name:getText(), 74 | description = "Select depot to which item should be stashed, choose between 3 and 17", 75 | validation = [[^([3-9]|1[0-7])$]] 76 | }) 77 | window.text:setText(entry.index) 78 | schedule(50, function() 79 | window:raise() 80 | window:focus() 81 | end) 82 | end 83 | end 84 | end 85 | if entry.id > 0 then 86 | panel.slot:setText("Stash to depot: ".. entry.index) 87 | end 88 | panel.slot:setTooltip("Click to set stashing destination.") 89 | panel.slot.onClick = function(widget) 90 | local window = modules.client_textedit.show(widget, { 91 | title = "Set depot for "..panel.name:getText(), 92 | description = "Select depot to which item should be stashed, choose between 3 and 17", 93 | validation = [[^([3-9]|1[0-7])$]] 94 | }) 95 | window.text:setText(entry.index) 96 | schedule(50, function() 97 | window:raise() 98 | window:focus() 99 | end) 100 | end 101 | panel.slot.onTextChange = function(widget, text) 102 | local n = tonumber(text) 103 | if n then 104 | entry.index = n 105 | widget:setText("Stash to depot: "..entry.index) 106 | end 107 | end 108 | end 109 | end 110 | refreshEntries() 111 | 112 | depositerPanel.title.onDoubleClick = function(widget) 113 | table.insert(config.items, {id=0, index=0}) 114 | refreshEntries() 115 | end 116 | 117 | function getStashingIndex(id) 118 | for _, v in pairs(config.items) do 119 | if v.id == id then 120 | return v.index - 1 121 | end 122 | end 123 | end 124 | 125 | UI.Separator() 126 | UI.Label("Sell Exeptions") 127 | 128 | if type(storage.cavebotSell) ~= "table" then 129 | storage.cavebotSell = {23544, 3081} 130 | end 131 | 132 | local sellContainer = UI.Container(function(widget, items) 133 | storage.cavebotSell = items 134 | end, true) 135 | sellContainer:setHeight(35) 136 | sellContainer:setItems(storage.cavebotSell) -------------------------------------------------------------------------------- /vBot/depositer_config.otui: -------------------------------------------------------------------------------- 1 | StashItem < Panel 2 | height: 40 3 | 4 | BotItem 5 | id: item 6 | anchors.top: parent.top 7 | margin-top: 2 8 | anchors.left: parent.left 9 | 10 | UIWidget 11 | id: name 12 | anchors.top: prev.top 13 | margin-top: 1 14 | anchors.bottom: prev.verticalCenter 15 | anchors.left: prev.right 16 | anchors.right: parent.right 17 | margin-left: 5 18 | text-align:left 19 | text: item name 20 | font: verdana-11px-rounded 21 | color: #FFFFFF 22 | 23 | UIWidget 24 | id: slot 25 | anchors.top: prev.bottom 26 | margin-top: 3 27 | anchors.bottom: Item.bottom 28 | anchors.left: prev.left 29 | anchors.right: parent.right 30 | font: verdana-11px-rounded 31 | text-align:left 32 | text: Add item to select locker. 33 | color: #CCCCCC 34 | 35 | DepositerPanel < MainWindow 36 | size: 230 380 37 | !text: tr('Depositer Panel') 38 | @onEscape: self:hide() 39 | 40 | UIWidget 41 | id: title 42 | anchors.top: parent.top 43 | anchors.left: parent.left 44 | anchors.right: parent.right 45 | text: Double click here to add item. 46 | text-align: left 47 | font: verdana-11px-rounded 48 | color: #aeaeae 49 | 50 | ScrollablePanel 51 | id: DepositerList 52 | image-source: /images/ui/panel_flat 53 | image-border: 1 54 | anchors.top: prev.bottom 55 | margin-top: 5 56 | anchors.left: parent.left 57 | anchors.right: parent.right 58 | anchors.bottom: sep.top 59 | margin-bottom: 10 60 | padding: 2 61 | padding-left: 4 62 | vertical-scrollbar: DepositerScrollBar 63 | layout: 64 | type: verticalBox 65 | 66 | VerticalScrollBar 67 | id: DepositerScrollBar 68 | anchors.top: DepositerList.top 69 | anchors.bottom: DepositerList.bottom 70 | anchors.right: DepositerList.right 71 | step: 14 72 | pixels-scroll: true 73 | 74 | ResizeBorder 75 | id: bottomResizeBorder 76 | anchors.fill: next 77 | height: 3 78 | minimum: 180 79 | maximum: 800 80 | margin-left: 3 81 | margin-right: 3 82 | background: #ffffff88 83 | 84 | HorizontalSeparator 85 | id: sep 86 | anchors.right: parent.right 87 | anchors.left: parent.left 88 | anchors.bottom: CloseButton.top 89 | margin-bottom: 8 90 | 91 | Button 92 | id: CloseButton 93 | !text: tr('Close') 94 | font: cipsoftFont 95 | anchors.right: parent.right 96 | anchors.bottom: parent.bottom 97 | size: 45 21 98 | margin-right: 5 -------------------------------------------------------------------------------- /vBot/depot_withdraw.lua: -------------------------------------------------------------------------------- 1 | -- config 2 | setDefaultTab("Tools") 3 | local defaultBp = "shopping bag" 4 | local id = 21411 5 | 6 | -- script 7 | 8 | local playerContainer = nil 9 | local depotContainer = nil 10 | local mailContainer = nil 11 | 12 | function reopenLootContainer() 13 | for _, container in pairs(getContainers()) do 14 | if container:getName():lower() == defaultBp:lower() then 15 | g_game.close(container) 16 | end 17 | end 18 | 19 | local lootItem = findItem(id) 20 | if lootItem then 21 | schedule(500, function() g_game.open(lootItem) end) 22 | end 23 | 24 | end 25 | 26 | macro(50, "Depot Withdraw", function() 27 | 28 | -- set the containers 29 | if not potionsContainer or not runesContainer or not ammoContainer then 30 | for i, container in pairs(getContainers()) do 31 | if container:getName() == defaultBp then 32 | playerContainer = container 33 | elseif string.find(container:getName(), "Depot") then 34 | depotContainer = container 35 | elseif string.find(container:getName(), "your inbox") then 36 | mailContainer = container 37 | end 38 | end 39 | end 40 | 41 | if playerContainer and #playerContainer:getItems() == 20 then 42 | for j, item in pairs(playerContainer:getItems()) do 43 | if item:getId() == id then 44 | g_game.open(item, playerContainer) 45 | return 46 | end 47 | end 48 | end 49 | 50 | 51 | if playerContainer and freecap() >= 200 then 52 | local time = 500 53 | if depotContainer then 54 | for i, container in pairs(getContainers()) do 55 | if string.find(container:getName(), "Depot") then 56 | for j, item in pairs(container:getItems()) do 57 | g_game.move(item, playerContainer:getSlotPosition(playerContainer:getItemsCount()), item:getCount()) 58 | return 59 | end 60 | end 61 | end 62 | end 63 | 64 | if mailContainer then 65 | for i, container in pairs(getContainers()) do 66 | if string.find(container:getName(), "your inbox") then 67 | for j, item in pairs(container:getItems()) do 68 | g_game.move(item, playerContainer:getSlotPosition(playerContainer:getItemsCount()), item:getCount()) 69 | return 70 | end 71 | end 72 | end 73 | end 74 | end 75 | 76 | end) -------------------------------------------------------------------------------- /vBot/eat_food.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("HP") 2 | if voc() ~= 1 and voc() ~= 11 then 3 | if storage.foodItems then 4 | local t = {} 5 | for i, v in pairs(storage.foodItems) do 6 | if not table.find(t, v.id) then 7 | table.insert(t, v.id) 8 | end 9 | end 10 | local foodItems = { 3607, 3585, 3592, 3600, 3601 } 11 | for i, item in pairs(foodItems) do 12 | if not table.find(t, item) then 13 | table.insert(storage.foodItems, item) 14 | end 15 | end 16 | end 17 | macro(500, "Cast Food", function() 18 | if player:getRegenerationTime() <= 400 then 19 | cast("exevo pan", 5000) 20 | end 21 | end) 22 | end 23 | 24 | UI.Label("Eatable items:") 25 | if type(storage.foodItems) ~= "table" then 26 | storage.foodItems = {3582, 3577} 27 | end 28 | 29 | local foodContainer = UI.Container(function(widget, items) 30 | storage.foodItems = items 31 | end, true) 32 | foodContainer:setHeight(35) 33 | foodContainer:setItems(storage.foodItems) 34 | 35 | macro(500, "Eat Food", function() 36 | if player:getRegenerationTime() > 400 or not storage.foodItems[1] then return end 37 | -- search for food in containers 38 | for _, container in pairs(g_game.getContainers()) do 39 | for __, item in ipairs(container:getItems()) do 40 | for i, foodItem in ipairs(storage.foodItems) do 41 | if item:getId() == foodItem.id then 42 | return g_game.use(item) 43 | end 44 | end 45 | end 46 | end 47 | end) 48 | UI.Separator() -------------------------------------------------------------------------------- /vBot/equip.lua: -------------------------------------------------------------------------------- 1 | -- config 2 | setDefaultTab("HP") 3 | local scripts = 2 -- if you want more auto equip panels you can change 2 to higher value 4 | 5 | -- script by kondrah, don't edit below unless you know what you are doing 6 | UI.Label("Auto equip") 7 | if type(storage.autoEquip) ~= "table" then 8 | storage.autoEquip = {} 9 | end 10 | for i=1,scripts do 11 | if not storage.autoEquip[i] then 12 | 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} 13 | end 14 | UI.TwoItemsAndSlotPanel(storage.autoEquip[i], function(widget, newParams) 15 | storage.autoEquip[i] = newParams 16 | end) 17 | end 18 | macro(250, function() 19 | local containers = g_game.getContainers() 20 | for index, autoEquip in ipairs(storage.autoEquip) do 21 | if autoEquip.on then 22 | local slotItem = getSlot(autoEquip.slot) 23 | if not slotItem or (slotItem:getId() ~= autoEquip.item1 and slotItem:getId() ~= autoEquip.item2) then 24 | for _, container in pairs(containers) do 25 | for __, item in ipairs(container:getItems()) do 26 | if item:getId() == autoEquip.item1 or item:getId() == autoEquip.item2 then 27 | g_game.move(item, {x=65535, y=autoEquip.slot, z=0}, item:getCount()) 28 | delay(1000) -- don't call it too often 29 | return 30 | end 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end) -------------------------------------------------------------------------------- /vBot/exeta.lua: -------------------------------------------------------------------------------- 1 | local voc = player:getVocation() 2 | if voc == 1 or voc == 11 then 3 | setDefaultTab("Cave") 4 | UI.Separator() 5 | local m = macro(100000, "Exeta when low hp", function() end) 6 | local lastCast = now 7 | onCreatureHealthPercentChange(function(creature, healthPercent) 8 | if m.isOff() then return end 9 | if healthPercent > 15 then return end 10 | if CaveBot.isOff() or TargetBot.isOff() then return end 11 | if modules.game_cooldown.isGroupCooldownIconActive(3) then return end 12 | if creature:getPosition() and getDistanceBetween(pos(),creature:getPosition()) > 1 then return end 13 | if canCast("exeta res") and now - lastCast > 6000 then 14 | say("exeta res") 15 | lastCast = now 16 | end 17 | end) 18 | 19 | macro(500, "ExetaIfPlayer", function() 20 | if CaveBot.isOff() then return end 21 | if getMonsters(1) >= 1 and getPlayers(6) > 0 then 22 | say("exeta res") 23 | delay(6000) 24 | end 25 | end) 26 | UI.Separator() 27 | end -------------------------------------------------------------------------------- /vBot/extras.otui: -------------------------------------------------------------------------------- 1 | ExtrasScrollBar < Panel 2 | height: 28 3 | margin-top: 3 4 | 5 | UIWidget 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 | ExtrasTextEdit < Panel 23 | height: 40 24 | margin-top: 7 25 | 26 | UIWidget 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 | text-align: center 43 | 44 | ExtrasItem < Panel 45 | height: 34 46 | margin-top: 7 47 | margin-left: 25 48 | margin-right: 25 49 | 50 | UIWidget 51 | id: text 52 | anchors.left: parent.left 53 | anchors.verticalCenter: next.verticalCenter 54 | 55 | BotItem 56 | id: item 57 | anchors.top: parent.top 58 | anchors.right: parent.right 59 | 60 | 61 | ExtrasCheckBox < BotSwitch 62 | height: 20 63 | margin-top: 7 64 | 65 | ExtrasWindow < MainWindow 66 | !text: tr('Extras') 67 | size: 440 360 68 | padding: 25 69 | 70 | Label 71 | anchors.left: parent.left 72 | anchors.right: parent.horizontalCenter 73 | anchors.top: parent.top 74 | text-align: center 75 | text: < CaveBot > 76 | 77 | Label 78 | anchors.left: parent.horizontalCenter 79 | anchors.right: parent.right 80 | anchors.top: parent.top 81 | text-align: center 82 | text: < Miscellaneous > 83 | 84 | VerticalScrollBar 85 | id: contentScroll 86 | anchors.top: prev.bottom 87 | margin-top: 3 88 | anchors.right: parent.right 89 | anchors.bottom: separator.top 90 | step: 28 91 | pixels-scroll: true 92 | margin-right: -10 93 | margin-top: 5 94 | margin-bottom: 5 95 | 96 | ScrollablePanel 97 | id: content 98 | anchors.top: prev.top 99 | anchors.left: parent.left 100 | anchors.right: parent.right 101 | anchors.bottom: separator.top 102 | vertical-scrollbar: contentScroll 103 | margin-bottom: 10 104 | 105 | Panel 106 | id: left 107 | anchors.top: parent.top 108 | anchors.left: parent.left 109 | anchors.right: parent.horizontalCenter 110 | margin-top: 5 111 | margin-left: 10 112 | margin-right: 10 113 | layout: 114 | type: verticalBox 115 | fit-children: true 116 | 117 | Panel 118 | id: right 119 | anchors.top: parent.top 120 | anchors.left: parent.horizontalCenter 121 | anchors.right: parent.right 122 | margin-top: 5 123 | margin-left: 10 124 | margin-right: 10 125 | layout: 126 | type: verticalBox 127 | fit-children: true 128 | 129 | VerticalSeparator 130 | anchors.top: parent.top 131 | anchors.bottom: parent.bottom 132 | anchors.left: parent.horizontalCenter 133 | 134 | HorizontalSeparator 135 | id: separator 136 | anchors.right: parent.right 137 | anchors.left: parent.left 138 | anchors.bottom: closeButton.top 139 | margin-bottom: 8 140 | 141 | ResizeBorder 142 | id: bottomResizeBorder 143 | anchors.fill: separator 144 | height: 3 145 | minimum: 260 146 | maximum: 600 147 | margin-left: 3 148 | margin-right: 3 149 | background: #ffffff88 150 | 151 | Button 152 | id: closeButton 153 | !text: tr('Close') 154 | font: cipsoftFont 155 | anchors.right: parent.right 156 | anchors.bottom: parent.bottom 157 | size: 45 21 158 | margin-right: 5 -------------------------------------------------------------------------------- /vBot/hold_target.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Tools") 2 | 3 | local targetID = nil 4 | 5 | -- escape when attacking will reset hold target 6 | onKeyPress(function(keys) 7 | if keys == "Escape" and targetID then 8 | targetID = nil 9 | end 10 | end) 11 | 12 | macro(100, "Hold Target", function() 13 | -- if attacking then save it as target, but check pos z in case of marking by mistake on other floor 14 | if target() and target():getPosition().z == posz() and not target():isNpc() then 15 | targetID = target():getId() 16 | elseif not target() then 17 | -- there is no saved data, do nothing 18 | if not targetID then return end 19 | 20 | -- look for target 21 | for i, spec in ipairs(getSpectators()) do 22 | local sameFloor = spec:getPosition().z == posz() 23 | local oldTarget = spec:getId() == targetID 24 | 25 | if sameFloor and oldTarget then 26 | attack(spec) 27 | end 28 | end 29 | end 30 | end) -------------------------------------------------------------------------------- /vBot/ingame_editor.lua: -------------------------------------------------------------------------------- 1 | setDefaultTab("Tools") 2 | -- allows to test/edit bot lua scripts ingame, you can have multiple scripts like this, just change storage.ingame_lua 3 | UI.Button("Ingame script editor", function(newText) 4 | UI.MultilineEditorWindow(storage.ingame_hotkeys or "", {title="Hotkeys editor", description="You can add your custom scrupts here"}, function(text) 5 | storage.ingame_hotkeys = text 6 | reload() 7 | end) 8 | end) 9 | 10 | UI.Separator() 11 | 12 | for _, scripts in pairs({storage.ingame_hotkeys}) do 13 | if type(scripts) == "string" and scripts:len() > 3 then 14 | local status, result = pcall(function() 15 | assert(load(scripts, "ingame_editor"))() 16 | end) 17 | if not status then 18 | error("Ingame edior error:\n" .. result) 19 | end 20 | end 21 | end 22 | 23 | UI.Separator() -------------------------------------------------------------------------------- /vBot/main.lua: -------------------------------------------------------------------------------- 1 | local version = "4.8" 2 | local currentVersion 3 | local available = false 4 | 5 | storage.checkVersion = storage.checkVersion or 0 6 | 7 | -- check max once per 12hours 8 | if os.time() > storage.checkVersion + (12 * 60 * 60) then 9 | 10 | storage.checkVersion = os.time() 11 | 12 | HTTP.get("https://raw.githubusercontent.com/Vithrax/vBot/main/vBot/version.txt", function(data, err) 13 | if err then 14 | warn("[vBot updater]: Unable to check version:\n" .. err) 15 | return 16 | end 17 | 18 | currentVersion = data 19 | available = true 20 | end) 21 | 22 | end 23 | 24 | UI.Label("vBot v".. version .." \n Vithrax#5814") 25 | UI.Button("Official OTCv8 Discord!", function() g_platform.openUrl("https://discord.gg/yhqBE4A") end) 26 | UI.Separator() 27 | 28 | schedule(5000, function() 29 | 30 | if not available then return end 31 | if currentVersion ~= version then 32 | 33 | UI.Separator() 34 | UI.Label("New vBot is available for download! v"..currentVersion) 35 | UI.Button("Go to vBot GitHub Page", function() g_platform.openUrl("https://github.com/Vithrax/vBot") end) 36 | UI.Separator() 37 | 38 | end 39 | 40 | end) -------------------------------------------------------------------------------- /vBot/npc_talk.lua: -------------------------------------------------------------------------------- 1 | onAttackingCreatureChange(function(creature, OldCreature) 2 | if creature and creature:isNpc() and distanceFromPlayer(creature:getPosition()) <= 3 then 3 | CaveBot.Conversation("hi", "trade") 4 | end 5 | end) -------------------------------------------------------------------------------- /vBot/playerlist.otui: -------------------------------------------------------------------------------- 1 | PlayerLabel < UIWidget 2 | background-color: alpha 3 | text-offset: 3 1 4 | focusable: true 5 | height: 16 6 | font: verdana-11px-rounded 7 | text-align: left 8 | 9 | $focus: 10 | background-color: #00000055 11 | 12 | Button 13 | id: remove 14 | !text: tr('X') 15 | anchors.right: parent.right 16 | anchors.verticalCenter: parent.verticalCenter 17 | width: 14 18 | height: 14 19 | margin-right: 15 20 | text-align: center 21 | text-offset: 0 1 22 | tooltip: Remove profile from the list. 23 | 24 | SettingCheckBox < CheckBox 25 | text-wrap: true 26 | text-auto-resize: true 27 | margin-top: 3 28 | font: verdana-11px-rounded 29 | 30 | Settings < FlatPanel 31 | padding: 6 32 | layout: 33 | type: verticalBox 34 | 35 | Label 36 | text: Additional Settings 37 | text-align: center 38 | font: verdana-11px-rounded 39 | 40 | HorizontalSeparator 41 | 42 | SettingCheckBox 43 | id: Members 44 | margin-top: 6 45 | text: Consider group members as friends. 46 | 47 | SettingCheckBox 48 | id: Outfit 49 | text: Color listed player outfits to red or blue. 50 | 51 | SettingCheckBox 52 | id: NeutralsAreEnemy 53 | text: Consider every non friend player as enemy. 54 | 55 | SettingCheckBox 56 | id: Highlight 57 | text: Hightlight listed players in red or blue color. 58 | 59 | SettingCheckBox 60 | id: AutoAdd 61 | text: Automatically add killed players while cave botting to blacklist. 62 | 63 | tPanel < Panel 64 | margin: 3 65 | padding: 3 66 | 67 | TextList 68 | id: list 69 | height: 200 70 | anchors.left: parent.left 71 | anchors.right: parent.right 72 | anchors.top: parent.top 73 | vertical-scrollbar: listScrollBar 74 | 75 | VerticalScrollBar 76 | id: listScrollBar 77 | anchors.top: list.top 78 | anchors.bottom: list.bottom 79 | anchors.right: list.right 80 | step: 14 81 | pixels-scroll: true 82 | 83 | TextEdit 84 | id: name 85 | anchors.top: list.bottom 86 | margin-top: 3 87 | anchors.left: parent.left 88 | anchors.right: parent.right 89 | 90 | Button 91 | id: add 92 | text: Add Player 93 | anchors.top: prev.bottom 94 | margin-top: 3 95 | anchors.left: parent.left 96 | anchors.right: parent.right 97 | font: verdana-11px-rounded 98 | 99 | PlayerListWindow < MainWindow 100 | !text: tr('Player List') 101 | size: 405 356 102 | @onEscape: self:hide() 103 | 104 | TabBar 105 | id: tmpTabBar 106 | anchors.top: parent.top 107 | anchors.left: parent.left 108 | width: 180 109 | 110 | FlatPanel 111 | id: tmpTabContent 112 | anchors.top: tmpTabBar.bottom 113 | anchors.left: parent.left 114 | width: 180 115 | anchors.bottom: separator.top 116 | margin-bottom: 5 117 | 118 | VerticalSeparator 119 | id: verticalSep 120 | anchors.top: parent.top 121 | anchors.bottom: separator.top 122 | margin-bottom: 5 123 | anchors.horizontalCenter: parent.horizontalCenter 124 | 125 | Settings 126 | id: settings 127 | anchors.left: prev.right 128 | anchors.top: parent.top 129 | anchors.right: parent.right 130 | anchors.bottom: next.top 131 | margin: 3 132 | margin-left: 6 133 | margin-bottom: 4 134 | 135 | HorizontalSeparator 136 | id: separator 137 | anchors.right: parent.right 138 | anchors.left: parent.left 139 | anchors.bottom: closeButton.top 140 | margin-bottom: 8 141 | 142 | Button 143 | id: closeButton 144 | !text: tr('Close') 145 | font: cipsoftFont 146 | anchors.right: parent.right 147 | anchors.bottom: parent.bottom 148 | size: 45 21 149 | margin-top: 15 150 | margin-right: 5 151 | @onClick: self:getParent():hide() -------------------------------------------------------------------------------- /vBot/pushmax.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: undefined-global 2 | setDefaultTab("Main") 3 | 4 | local panelName = "pushmax" 5 | local ui = setupUI([[ 6 | Panel 7 | height: 19 8 | 9 | BotSwitch 10 | id: title 11 | anchors.top: parent.top 12 | anchors.left: parent.left 13 | text-align: center 14 | width: 130 15 | !text: tr('PUSHMAX') 16 | 17 | Button 18 | id: push 19 | anchors.top: prev.top 20 | anchors.left: prev.right 21 | anchors.right: parent.right 22 | margin-left: 3 23 | height: 17 24 | text: Setup 25 | 26 | ]]) 27 | ui:setId(panelName) 28 | 29 | if not storage[panelName] then 30 | storage[panelName] = { 31 | enabled = true, 32 | pushDelay = 1060, 33 | pushMaxRuneId = 3188, 34 | mwallBlockId = 2128, 35 | pushMaxKey = "PageUp" 36 | } 37 | end 38 | 39 | local config = storage[panelName] 40 | 41 | ui.title:setOn(config.enabled) 42 | ui.title.onClick = function(widget) 43 | config.enabled = not config.enabled 44 | widget:setOn(config.enabled) 45 | end 46 | 47 | ui.push.onClick = function(widget) 48 | pushWindow:show() 49 | pushWindow:raise() 50 | pushWindow:focus() 51 | end 52 | 53 | rootWidget = g_ui.getRootWidget() 54 | if rootWidget then 55 | pushWindow = UI.createWindow('PushMaxWindow', rootWidget) 56 | pushWindow:hide() 57 | 58 | pushWindow.closeButton.onClick = function(widget) 59 | pushWindow:hide() 60 | end 61 | 62 | local updateDelayText = function() 63 | pushWindow.delayText:setText("Push Delay: ".. config.pushDelay) 64 | end 65 | updateDelayText() 66 | pushWindow.delay.onValueChange = function(scroll, value) 67 | config.pushDelay = value 68 | updateDelayText() 69 | end 70 | pushWindow.delay:setValue(config.pushDelay) 71 | 72 | pushWindow.runeId.onItemChange = function(widget) 73 | config.pushMaxRuneId = widget:getItemId() 74 | end 75 | pushWindow.runeId:setItemId(config.pushMaxRuneId) 76 | pushWindow.mwallId.onItemChange = function(widget) 77 | config.mwallBlockId = widget:getItemId() 78 | end 79 | pushWindow.mwallId:setItemId(config.mwallBlockId) 80 | 81 | pushWindow.hotkey.onTextChange = function(widget, text) 82 | config.pushMaxKey = text 83 | end 84 | pushWindow.hotkey:setText(config.pushMaxKey) 85 | end 86 | 87 | 88 | -- variables for config 89 | local fieldTable = {2118, 105, 2122} 90 | local cleanTile = nil 91 | 92 | -- scripts 93 | 94 | local targetTile 95 | local pushTarget 96 | 97 | local resetData = function() 98 | for i, tile in pairs(g_map.getTiles(posz())) do 99 | if tile:getText() == "TARGET" or tile:getText() == "DEST" or tile:getText() == "CLEAR" then 100 | tile:setText('') 101 | end 102 | end 103 | pushTarget = nil 104 | targetTile = nil 105 | cleanTile = nil 106 | end 107 | 108 | local getCreatureById = function(id) 109 | for i, spec in ipairs(getSpectators()) do 110 | if spec:getId() == id then 111 | return spec 112 | end 113 | end 114 | return false 115 | end 116 | 117 | local isNotOk = function(t,tile) 118 | local tileItems = {} 119 | 120 | for i, item in pairs(tile:getItems()) do 121 | table.insert(tileItems, item:getId()) 122 | end 123 | for i, field in ipairs(t) do 124 | if table.find(tileItems, field) then 125 | return true 126 | end 127 | end 128 | return false 129 | end 130 | 131 | local isOk = function(a,b) 132 | return getDistanceBetween(a,b) == 1 133 | end 134 | 135 | -- to mark 136 | local hold = 0 137 | onKeyDown(function(keys) 138 | if not config.enabled then return end 139 | if keys ~= config.pushMaxKey then return end 140 | hold = now 141 | local tile = getTileUnderCursor() 142 | if not tile then return end 143 | if pushTarget and targetTile then 144 | resetData() 145 | return 146 | end 147 | local creature = tile:getCreatures()[1] 148 | if not pushTarget and creature then 149 | pushTarget = creature 150 | if pushTarget then 151 | tile:setText('TARGET') 152 | pushTarget:setMarked('#00FF00') 153 | end 154 | elseif not targetTile and pushTarget then 155 | if pushTarget and getDistanceBetween(tile:getPosition(),pushTarget:getPosition()) ~= 1 then 156 | resetData() 157 | return 158 | else 159 | tile:setText('DEST') 160 | targetTile = tile 161 | end 162 | end 163 | end) 164 | 165 | -- mark tile to throw anything from it 166 | onKeyPress(function(keys) 167 | if not config.enabled then return end 168 | if keys ~= config.pushMaxKey then return end 169 | local tile = getTileUnderCursor() 170 | if not tile then return end 171 | 172 | if (hold - now) < -2500 then 173 | if cleanTile and tile ~= cleanTile then 174 | resetData() 175 | elseif not cleanTile then 176 | cleanTile = tile 177 | tile:setText("CLEAR") 178 | end 179 | end 180 | hold = 0 181 | end) 182 | 183 | onCreaturePositionChange(function(creature, newPos, oldPos) 184 | if not config.enabled then return end 185 | if creature == player then 186 | resetData() 187 | end 188 | if not pushTarget or not targetTile then return end 189 | if creature == pushTarget and newPos == targetTile then 190 | resetData() 191 | end 192 | end) 193 | 194 | macro(50, function() 195 | if not config.enabled then return end 196 | 197 | local pushDelay = tonumber(config.pushDelay) 198 | local rune = tonumber(config.pushMaxRuneId) 199 | local customMwall = config.mwallBlockId 200 | 201 | if cleanTile then 202 | local tilePos = cleanTile:getPosition() 203 | local pPos = player:getPosition() 204 | if not isOk(tilePos, pPos) then 205 | resetData() 206 | return 207 | end 208 | 209 | if not cleanTile:hasCreature() then return end 210 | local tiles = getNearTiles(tilePos) 211 | local destTile 212 | local forbidden = {} 213 | -- unfortunately double loop 214 | for i, tile in pairs(tiles) do 215 | local minimapColor = g_map.getMinimapColor(tile:getPosition()) 216 | local stairs = (minimapColor >= 210 and minimapColor <= 213) 217 | if stairs then 218 | table.insert(forbidden, tile:getPosition()) 219 | end 220 | end 221 | for i, tile in pairs(tiles) do 222 | local minimapColor = g_map.getMinimapColor(tile:getPosition()) 223 | local stairs = (minimapColor >= 210 and minimapColor <= 213) 224 | if tile:isWalkable() and not isNotOk(fieldTable, tile) and not tile:hasCreature() and not stairs then 225 | local tooClose = false 226 | if #forbidden ~= 0 then 227 | for i=1,#forbidden do 228 | local pos = forbidden[i] 229 | if isOk(pos, tile:getPosition()) then 230 | tooClose = true 231 | break 232 | end 233 | end 234 | end 235 | if not tooClose then 236 | destTile = tile 237 | break 238 | end 239 | end 240 | end 241 | 242 | if not destTile then return end 243 | local parcel = cleanTile:getCreatures()[1] 244 | if parcel then 245 | test() 246 | g_game.move(parcel,destTile:getPosition()) 247 | delay(2000) 248 | end 249 | else 250 | if not pushTarget or not targetTile then return end 251 | local tilePos = targetTile:getPosition() 252 | local targetPos = pushTarget:getPosition() 253 | if not isOk(tilePos,targetPos) then return end 254 | 255 | local tileOfTarget = g_map.getTile(targetPos) 256 | 257 | if not targetTile:isWalkable() then 258 | local topThing = targetTile:getTopUseThing():getId() 259 | if topThing == 2129 or topThing == 2130 or topThing == customMwall then 260 | if targetTile:getTimer() < pushDelay+500 then 261 | vBot.isUsing = true 262 | schedule(pushDelay+700, function() 263 | vBot.isUsing = false 264 | end) 265 | end 266 | if targetTile:getTimer() > pushDelay then 267 | return 268 | end 269 | else 270 | return resetData() 271 | end 272 | end 273 | 274 | if not tileOfTarget:getTopUseThing():isNotMoveable() and targetTile:getTimer() < pushDelay+500 then 275 | return useWith(rune, pushTarget) 276 | end 277 | if isNotOk(fieldTable, targetTile) then 278 | if targetTile:canShoot() then 279 | return useWith(3148, targetTile:getTopUseThing()) 280 | else 281 | return 282 | end 283 | end 284 | g_game.move(pushTarget,tilePos) 285 | delay(2000) 286 | end 287 | end) -------------------------------------------------------------------------------- /vBot/pushmax.otui: -------------------------------------------------------------------------------- 1 | PushMaxWindow < MainWindow 2 | !text: tr('Pushmax Settings') 3 | size: 200 240 4 | @onEscape: self:hide() 5 | 6 | BotLabel 7 | id: delayText 8 | anchors.top: parent.top 9 | anchors.left: parent.left 10 | anchors.right: parent.right 11 | text-align: center 12 | 13 | HorizontalScrollBar 14 | id: delay 15 | anchors.left: delayText.left 16 | anchors.right: delayText.right 17 | anchors.top: delayText.bottom 18 | margin-top: 5 19 | minimum: 800 20 | maximum: 2000 21 | step: 10 22 | 23 | Label 24 | id: label2 25 | anchors.top: delay.bottom 26 | anchors.left: parent.horizontalCenter 27 | anchors.right: parent.right 28 | text-align: center 29 | text: Custom WallID 30 | margin-top: 5 31 | 32 | Label 33 | id: label3 34 | anchors.top: delay.bottom 35 | anchors.right: parent.horizontalCenter 36 | anchors.left: parent.left 37 | text-align: center 38 | text: VS AntiPush 39 | margin-top: 5 40 | 41 | BotItem 42 | id: runeId 43 | anchors.horizontalCenter: label3.horizontalCenter 44 | anchors.top: label3.bottom 45 | margin-top: 5 46 | 47 | BotItem 48 | id: mwallId 49 | anchors.horizontalCenter: label2.horizontalCenter 50 | anchors.top: label2.bottom 51 | margin-top: 5 52 | 53 | Label 54 | id: label1 55 | anchors.top: mwallId.bottom 56 | anchors.left: parent.left 57 | anchors.right: parent.right 58 | margin-top: 10 59 | text-align: center 60 | text: Hotkey for PUSHMAX 61 | 62 | TextEdit 63 | id: hotkey 64 | anchors.left: parent.left 65 | anchors.right: parent.right 66 | anchors.top: label1.bottom 67 | margin-top: 5 68 | text-align: center 69 | 70 | HorizontalSeparator 71 | id: separator 72 | anchors.right: parent.right 73 | anchors.left: parent.left 74 | anchors.bottom: closeButton.top 75 | margin-bottom: 8 76 | 77 | Button 78 | id: closeButton 79 | !text: tr('Close') 80 | font: cipsoftFont 81 | anchors.right: parent.right 82 | anchors.bottom: parent.bottom 83 | size: 45 21 84 | margin-top: 15 85 | margin-right: 5 -------------------------------------------------------------------------------- /vBot/quiver_label.lua: -------------------------------------------------------------------------------- 1 | local quiverSlot = modules.game_inventory.inventoryWindow:recursiveGetChildById('slot5') 2 | local label = quiverSlot.count 3 | 4 | label = label or g_ui.loadUIFromString([[ 5 | Label 6 | id: count 7 | color: #bfbfbf 8 | font: verdana-11px-rounded 9 | anchors.left: parent.left 10 | anchors.right: parent.right 11 | anchors.bottom: parent.bottom 12 | text-align: right 13 | margin-right: 3 14 | margin-left: 3 15 | text: 16 | ]], quiverSlot) 17 | 18 | 19 | function getQuiverAmount() 20 | -- old tibia 21 | if g_game.getClientVersion() < 1000 then return end 22 | 23 | 24 | local isQuiverEquipped = getRight() and getRight():isContainer() or false 25 | local quiver = isQuiverEquipped and getContainerByItem(getRight():getId()) 26 | local count = 0 27 | 28 | if quiver then 29 | for i, item in ipairs(quiver:getItems()) do 30 | count = count + item:getCount() 31 | end 32 | else 33 | return label:setText("") 34 | end 35 | 36 | return label:setText(count) 37 | end 38 | getQuiverAmount() 39 | 40 | onContainerOpen(function(container, previousContainer) 41 | getQuiverAmount() 42 | end) 43 | 44 | onContainerClose(function(container) 45 | getQuiverAmount() 46 | end) 47 | 48 | onAddItem(function(container, slot, item, oldItem) 49 | getQuiverAmount() 50 | end) 51 | 52 | onRemoveItem(function(container, slot, item) 53 | getQuiverAmount() 54 | end) 55 | 56 | onContainerUpdateItem(function(container, slot, item, oldItem) 57 | getQuiverAmount() 58 | end) -------------------------------------------------------------------------------- /vBot/quiver_manager.lua: -------------------------------------------------------------------------------- 1 | if voc() == 2 or voc() == 12 then 2 | local bows = { 3350, 31581, 27455, 8027, 20082, 36664, 7438, 28718, 36665, 14246, 19362, 35518, 34150, 29417, 9378, 16164, 22866, 12733, 8029, 20083, 20084, 8026, 8028, 34088} 3 | local xbows = { 30393, 3349, 27456, 20085, 16163, 5947, 8021, 14247, 22867, 8023, 22711, 19356, 20086, 20087, 34089} 4 | local arrows = { 16143, 763, 761, 7365, 3448, 762, 21470, 7364, 14251, 3447, 3449, 15793, 25757, 774, 35901 } 5 | local bolts = { 6528, 7363, 3450, 16141, 25758, 14252, 3446, 16142, 35902 } 6 | local hold = false 7 | 8 | onContainerOpen(function(container, previousContainer) 9 | hold = false 10 | end) 11 | 12 | onContainerClose(function(container) 13 | hold = false 14 | end) 15 | 16 | onAddItem(function(container, slot, item, oldItem) 17 | hold = false 18 | end) 19 | 20 | onRemoveItem(function(container, slot, item) 21 | hold = false 22 | end) 23 | 24 | onContainerUpdateItem(function(container, slot, item, oldItem) 25 | hold = false 26 | end) 27 | 28 | 29 | 30 | local function manageQuiver(isBowEquipped, quiverContainer) 31 | local ammo = isBowEquipped and arrows or bolts 32 | local dest = nil 33 | local containers = getContainers() 34 | for i, container in ipairs(containers) do 35 | if container ~= quiverContainer and not containerIsFull(container) then 36 | local cname = container:getName():lower() 37 | if not cname:find("loot") and (cname:find("backpack") or cname:find("bag") or cname:find("chess")) then 38 | dest = container 39 | end 40 | end 41 | end 42 | 43 | -- clearing 44 | if dest then 45 | for i, item in ipairs(quiverContainer:getItems()) do 46 | if not table.find(ammo, item:getId()) then 47 | local pos = dest:getSlotPosition(dest:getItemsCount()) 48 | return g_game.move(item, pos, item:getCount()) 49 | end 50 | end 51 | end 52 | 53 | if not containerIsFull(quiverContainer) then 54 | for i, container in ipairs(containers) do 55 | if container ~= quiverContainer then 56 | for j, item in ipairs(container:getItems()) do 57 | if table.find(ammo, item:getId()) then 58 | local pos = quiverContainer:getSlotPosition(quiverContainer:getItemsCount()) 59 | return g_game.move(item, pos, item:getCount()) 60 | end 61 | end 62 | end 63 | end 64 | end 65 | return true 66 | end 67 | 68 | UI.Separator() 69 | macro(100, "Quiver Manager", function() 70 | if hold then return end -- do nothing if nothing to do 71 | local hand = getLeft() and getLeft():getId() 72 | local quiverEquipped = getRight() and getRight():isContainer() 73 | 74 | if not hand then return end 75 | if not quiverEquipped then return end 76 | 77 | local quiverContainer = getContainerByItem(getRight():getId()) 78 | if not quiverContainer then return end 79 | 80 | local isBowEquipped = getLeft() and table.find(bows, hand) and true or false 81 | if not isBowEquipped then 82 | if not table.find(xbows, hand) then 83 | return -- neither bow and xbow is equipped 84 | end 85 | end 86 | 87 | if manageQuiver(isBowEquipped, quiverContainer) then -- if true then it didn't do anything 88 | hold = true 89 | end 90 | end) 91 | end -------------------------------------------------------------------------------- /vBot/siolist.otui: -------------------------------------------------------------------------------- 1 | VocationPanel < Panel 2 | padding: 3 3 | image-source: /images/ui/panel_flat 4 | image-border: 6 5 | size: 190 55 6 | 7 | Label 8 | anchors.top: parent.top 9 | anchors.left: parent.left 10 | anchors.right: parent.right 11 | text-align: center 12 | text: for BotServer, Heal only: 13 | 14 | BotSwitch 15 | id: ED 16 | anchors.bottom: parent.bottom 17 | anchors.left: parent.left 18 | anchors.right: parent.horizontalCenter 19 | text: Druids 20 | 21 | BotSwitch 22 | id: MS 23 | anchors.bottom: parent.bottom 24 | anchors.left: parent.horizontalCenter 25 | anchors.right: parent.right 26 | text: Sorcerers 27 | 28 | BotSwitch 29 | id: EK 30 | anchors.bottom: ED.top 31 | anchors.left: parent.left 32 | anchors.right: parent.horizontalCenter 33 | text: Knights 34 | 35 | BotSwitch 36 | id: RP 37 | anchors.bottom: ED.top 38 | anchors.left: parent.horizontalCenter 39 | anchors.right: parent.right 40 | text: Paladins 41 | 42 | 43 | 44 | SioListWindow < MainWindow 45 | !text: tr('Healer Options') 46 | size: 220 360 47 | @onEscape: self:hide() 48 | 49 | BotSwitch 50 | id: exuraSio 51 | anchors.top: parent.top 52 | anchors.left: parent.left 53 | anchors.right: parent.horizontalCenter 54 | text: Exura Sio 55 | margin-right: 2 56 | 57 | BotSwitch 58 | id: exuraGranSio 59 | anchors.top: parent.top 60 | anchors.left: prev.right 61 | anchors.right: parent.right 62 | text: Exura Gran Sio 63 | margin-left: 2 64 | 65 | BotSwitch 66 | id: exuraMasRes 67 | anchors.top: prev.bottom 68 | anchors.left: parent.left 69 | anchors.right: parent.right 70 | text: Exura Gran Mas Res 71 | margin-top: 3 72 | 73 | BotSwitch 74 | id: spell 75 | anchors.top: prev.bottom 76 | anchors.left: parent.left 77 | anchors.right: parent.right 78 | text: Custom Spell 79 | margin-top: 3 80 | text-align: center 81 | 82 | BotTextEdit 83 | id: spellName 84 | anchors.left: parent.left 85 | anchors.right: parent.right 86 | anchors.top: prev.bottom 87 | margin-top: 3 88 | 89 | HorizontalSeparator 90 | anchors.top: prev.bottom 91 | anchors.left: parent.left 92 | anchors.right: parent.right 93 | margin-top: 10 94 | 95 | BotItem 96 | id: itemId 97 | anchors.top: prev.bottom 98 | anchors.left: parent.left 99 | margin-top: 10 100 | 101 | BotSwitch 102 | id: item 103 | anchors.top: prev.top 104 | anchors.left: prev.right 105 | anchors.right: parent.right 106 | anchors.bottom: prev.verticalCenter 107 | text-align: center 108 | text: Item Healing 109 | margin-left: 2 110 | 111 | BotLabel 112 | id: distText 113 | anchors.top: itemId.verticalCenter 114 | anchors.left: itemId.right 115 | anchors.right: parent.right 116 | anchors.bottom: itemId.bottom 117 | text-align: center 118 | text: Max Distance 119 | 120 | HorizontalScrollBar 121 | id: Distance 122 | anchors.left: parent.left 123 | anchors.top: itemId.bottom 124 | anchors.right: parent.right 125 | margin-top: 3 126 | minimum: 1 127 | maximum: 10 128 | step: 1 129 | 130 | HorizontalSeparator 131 | anchors.top: prev.bottom 132 | anchors.left: parent.left 133 | anchors.right: parent.right 134 | margin-top: 8 135 | 136 | BotLabel 137 | id: manaInfo 138 | anchors.left: parent.left 139 | anchors.right: parent.right 140 | anchors.top: prev.bottom 141 | text-align: center 142 | margin-top: 5 143 | 144 | HorizontalScrollBar 145 | id: minMana 146 | anchors.left: spellName.left 147 | anchors.right: spellName.right 148 | anchors.top: manaInfo.bottom 149 | margin-top: 2 150 | minimum: 1 151 | maximum: 100 152 | step: 1 153 | 154 | BotLabel 155 | id: friendHp 156 | anchors.left: spellName.left 157 | anchors.right: spellName.right 158 | anchors.top: prev.bottom 159 | text-align: center 160 | margin-top: 5 161 | 162 | HorizontalScrollBar 163 | id: minFriendHp 164 | anchors.left: spellName.left 165 | anchors.right: spellName.right 166 | anchors.top: friendHp.bottom 167 | margin-top: 2 168 | minimum: 1 169 | maximum: 100 170 | step: 1 171 | 172 | VocationPanel 173 | id: vocation 174 | anchors.top: prev.bottom 175 | margin-top: 6 176 | 177 | HorizontalSeparator 178 | id: separator 179 | anchors.right: parent.right 180 | anchors.left: parent.left 181 | anchors.bottom: closeButton.top 182 | margin-bottom: 8 183 | 184 | Button 185 | id: closeButton 186 | !text: tr('Close') 187 | font: cipsoftFont 188 | anchors.right: parent.right 189 | anchors.bottom: parent.bottom 190 | size: 45 21 191 | margin-top: 15 192 | margin-right: 5 -------------------------------------------------------------------------------- /vBot/spy_level.lua: -------------------------------------------------------------------------------- 1 | -- config 2 | 3 | local keyUp = "=" 4 | local keyDown = "-" 5 | setDefaultTab("Tools") 6 | 7 | -- script 8 | 9 | local lockedLevel = pos().z 10 | 11 | onPlayerPositionChange(function(newPos, oldPos) 12 | lockedLevel = pos().z 13 | modules.game_interface.getMapPanel():unlockVisibleFloor() 14 | end) 15 | 16 | onKeyPress(function(keys) 17 | if keys == keyDown then 18 | lockedLevel = lockedLevel + 1 19 | modules.game_interface.getMapPanel():lockVisibleFloor(lockedLevel) 20 | elseif keys == keyUp then 21 | lockedLevel = lockedLevel - 1 22 | modules.game_interface.getMapPanel():lockVisibleFloor(lockedLevel) 23 | end 24 | end) -------------------------------------------------------------------------------- /vBot/supplies.otui: -------------------------------------------------------------------------------- 1 | ProfileLabel < UIWidget 2 | background-color: alpha 3 | text-offset: 3 1 4 | focusable: true 5 | height: 16 6 | font: verdana-11px-rounded 7 | text-align: left 8 | 9 | $focus: 10 | background-color: #00000055 11 | 12 | Button 13 | id: remove 14 | !text: tr('X') 15 | anchors.right: parent.right 16 | anchors.verticalCenter: parent.verticalCenter 17 | width: 14 18 | height: 14 19 | margin-right: 3 20 | text-align: center 21 | text-offset: 0 1 22 | tooltip: Remove profile from the list. 23 | 24 | SupplySpinBox < SpinBox 25 | height: 20 26 | margin-left: 3 27 | width: 75 28 | minimum: 0 29 | maximum: 9999 30 | text-align: center 31 | focusable: true 32 | text: 0 33 | 34 | ItemPanel < Panel 35 | height: 38 36 | 37 | BotItem 38 | id: id 39 | anchors.left: parent.left 40 | anchors.bottom: parent.bottom 41 | 42 | SupplySpinBox 43 | id: min 44 | anchors.left: prev.right 45 | anchors.bottom: parent.bottom 46 | 47 | SupplySpinBox 48 | id: max 49 | anchors.left: prev.right 50 | anchors.bottom: parent.bottom 51 | 52 | SupplySpinBox 53 | id: avg 54 | anchors.left: prev.right 55 | anchors.bottom: parent.bottom 56 | width: 50 57 | 58 | UIWidget 59 | anchors.left: min.left 60 | anchors.bottom: min.top 61 | width: 75 62 | text-align: center 63 | font: verdana-11px-rounded 64 | text: Min 65 | tooltip: Amount of given supplies for bot to leave the spawn. 66 | 67 | UIWidget 68 | anchors.left: max.left 69 | anchors.bottom: max.top 70 | width: 75 71 | text-align: center 72 | font: verdana-11px-rounded 73 | text: Max 74 | tooltip: Amount of given supplies to purchase 75 | 76 | UIWidget 77 | anchors.left: avg.left 78 | anchors.bottom: avg.top 79 | width: 55 80 | text-align: center 81 | font: verdana-11px-rounded 82 | text: AVG 83 | !tooltip: ("This is average consumption of supplies by round to help calculate the amount to purchase\n (info provided by CaveBot Stats)") 84 | 85 | SuppliesWindow < MainWindow 86 | !text: tr('Supplies') 87 | size: 430 330 88 | @onEscape: self:hide() 89 | 90 | VerticalSeparator 91 | id: sep 92 | anchors.top: parent.top 93 | anchors.right: parent.right 94 | margin-right: 140 95 | anchors.bottom: bottomSep.top 96 | margin-bottom: 5 97 | margin-left: 10 98 | visible: false 99 | 100 | Label 101 | anchors.left: sep.right 102 | anchors.right: parent.right 103 | anchors.top: parent.top 104 | margin-left: 10 105 | margin-top: 3 106 | text-align: center 107 | text: Additional Conditions: 108 | 109 | HorizontalSeparator 110 | anchors.top: prev.bottom 111 | anchors.left: prev.left 112 | anchors.right: prev.right 113 | margin-top: 3 114 | 115 | BotSwitch 116 | id: SoftBoots 117 | anchors.top: prev.bottom 118 | anchors.left: sep.right 119 | anchors.right: parent.right 120 | margin-top: 5 121 | margin-left: 10 122 | text: No Soft 123 | tooltip: Go refill if there's no more active soft boots. 124 | 125 | BotSwitch 126 | id: capSwitch 127 | height: 20 128 | anchors.left: SoftBoots.left 129 | anchors.right: parent.right 130 | anchors.top: prev.bottom 131 | margin-top: 5 132 | margin-right: 50 133 | text-align: center 134 | text: Cap Below: 135 | tooltip: Go refill if capacity is below set value. 136 | 137 | BotTextEdit 138 | id: capValue 139 | size: 40 20 140 | anchors.left: prev.right 141 | anchors.right: parent.right 142 | anchors.top: prev.top 143 | margin-left: 5 144 | 145 | BotSwitch 146 | id: staminaSwitch 147 | height: 20 148 | anchors.left: SoftBoots.left 149 | anchors.right: parent.right 150 | anchors.top: prev.bottom 151 | margin-top: 5 152 | margin-right: 50 153 | text-align: center 154 | text: Stamina: 155 | tooltip: Go refill if stamina is below set value. (in minutes) 156 | 157 | BotTextEdit 158 | id: staminaValue 159 | size: 40 20 160 | anchors.left: prev.right 161 | anchors.right: parent.right 162 | anchors.top: prev.top 163 | margin-left: 5 164 | 165 | BotSwitch 166 | id: imbues 167 | anchors.top: prev.bottom 168 | anchors.left: sep.right 169 | anchors.right: parent.right 170 | margin-top: 5 171 | margin-left: 10 172 | text: No Imbues 173 | tooltip: Go refill when mana leech imbue has worn off. 174 | 175 | TextList 176 | id: profiles 177 | anchors.top: prev.bottom 178 | margin-top: 5 179 | anchors.left: prev.left 180 | anchors.right: prev.right 181 | anchors.bottom: bottomSep.top 182 | margin-bottom: 25 183 | 184 | BotButton 185 | id: newProfile 186 | anchors.left: prev.left 187 | anchors.top: prev.bottom 188 | size: 35 15 189 | text: New 190 | font: cipsoftFont 191 | tooltip: Create new supplies profile. 192 | 193 | VerticalScrollBar 194 | id: itemsScrollBar 195 | anchors.top: items.top 196 | anchors.bottom: items.bottom 197 | anchors.right: items.right 198 | step: 14 199 | pixels-scroll: true 200 | 201 | ScrollablePanel 202 | id: items 203 | anchors.top: parent.top 204 | anchors.left: parent.left 205 | anchors.right: sep.left 206 | anchors.bottom: bottomSep.top 207 | margin-bottom: 8 208 | vertical-scrollbar: itemsScrollBar 209 | layout: verticalBox 210 | 211 | HorizontalSeparator 212 | id: bottomSep 213 | anchors.left: parent.left 214 | anchors.right: parent.right 215 | anchors.bottom: closeButton.top 216 | margin-bottom: 8 217 | 218 | Button 219 | id: closeButton 220 | !text: tr('Close') 221 | font: cipsoftFont 222 | anchors.right: parent.right 223 | anchors.bottom: parent.bottom 224 | size: 45 21 225 | margin-top: 15 226 | tooltip: Close supplies window and save settings. 227 | @onClick: self:getParent():hide() 228 | 229 | Button 230 | id: increment 231 | anchors.verticalCenter: prev.verticalCenter 232 | anchors.right: items.right 233 | text: + 234 | width: 50 235 | tooltip: increase all max supplies amount by average 236 | 237 | Button 238 | id: decrement 239 | anchors.verticalCenter: prev.verticalCenter 240 | anchors.right: prev.left 241 | margin-right: 3 242 | text: - 243 | width: 50 244 | tooltip: decrease all max supplies amount by average -------------------------------------------------------------------------------- /vBot/tools.lua: -------------------------------------------------------------------------------- 1 | -- tools tab 2 | setDefaultTab("Tools") 3 | 4 | if type(storage.moneyItems) ~= "table" then 5 | storage.moneyItems = {3031, 3035} 6 | end 7 | macro(1000, "Exchange money", function() 8 | if not storage.moneyItems[1] then return end 9 | local containers = g_game.getContainers() 10 | for index, container in pairs(containers) do 11 | if not container.lootContainer then -- ignore monster containers 12 | for i, item in ipairs(container:getItems()) do 13 | if item:getCount() == 100 then 14 | for m, moneyId in ipairs(storage.moneyItems) do 15 | if item:getId() == moneyId.id then 16 | return g_game.use(item) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | end 23 | end) 24 | 25 | local moneyContainer = UI.Container(function(widget, items) 26 | storage.moneyItems = items 27 | end, true) 28 | moneyContainer:setHeight(35) 29 | moneyContainer:setItems(storage.moneyItems) 30 | 31 | UI.Separator() 32 | 33 | macro(60000, "Send message on trade", function() 34 | local trade = getChannelId("advertising") 35 | if not trade then 36 | trade = getChannelId("trade") 37 | end 38 | if trade and storage.autoTradeMessage:len() > 0 then 39 | sayChannel(trade, storage.autoTradeMessage) 40 | end 41 | end) 42 | UI.TextEdit(storage.autoTradeMessage or "I'm using OTClientV8!", function(widget, text) 43 | storage.autoTradeMessage = text 44 | end) 45 | 46 | UI.Separator() 47 | -------------------------------------------------------------------------------- /vBot/version.txt: -------------------------------------------------------------------------------- 1 | 4.8 -------------------------------------------------------------------------------- /vBot/xeno_menu.lua: -------------------------------------------------------------------------------- 1 | modules.game_interface.gameRootPanel.onMouseRelease = function(widget, mousePos, mouseButton) 2 | if mouseButton == 2 then 3 | local child = rootWidget:recursiveGetChildByPos(mousePos) 4 | if child == widget then 5 | local menu = g_ui.createWidget('PopupMenu') 6 | menu:setId("blzMenu") 7 | menu:setGameMenu(true) 8 | menu:addOption('AttackBot', AttackBot.show, "OTCv8") 9 | menu:addOption('HealBot', HealBot.show, "OTCv8") 10 | menu:addOption('Conditions', Conditions.show, "OTCv8") 11 | menu:addSeparator() 12 | menu:addOption('CaveBot', function() 13 | if CaveBot.isOn() then 14 | CaveBot.setOff() 15 | else 16 | CaveBot.setOn() 17 | end 18 | end, CaveBot.isOn() and "ON " or "OFF ") 19 | menu:addOption('TargetBot', function() 20 | if TargetBot.isOn() then 21 | TargetBot.setOff() 22 | else 23 | TargetBot.setOn() 24 | end 25 | end, TargetBot.isOn() and "ON " or "OFF ") 26 | menu:display(mousePos) 27 | return true 28 | end 29 | end 30 | end --------------------------------------------------------------------------------