CaveBot.Actions = {} -- it adds an action widget to list CaveBot.addAction = function(action, value, focus) action = action:lower() local raction = CaveBot.Actions[action] if not raction then return warn("Invalid cavebot action: " .. action) end if type(value) == 'number' then value = tostring(value) end local widget = UI.createWidget("CaveBotAction", CaveBot.actionList) widget:setText(action .. ":" .. value:split("\n")[1]) widget.action = action widget.value = value if raction.color then widget:setColor(raction.color) end widget.onDoubleClick = function(cwidget) -- edit on double click if CaveBot.Editor then schedule(20, function() -- schedule to have correct focus CaveBot.Editor.edit(cwidget.action, cwidget.value, function(action, value) CaveBot.editAction(cwidget, action, value) CaveBot.save() end) end) end end if focus then widget:focus() CaveBot.actionList:ensureChildVisible(widget) end return widget end -- it updates existing widget, you should call CaveBot.save() later CaveBot.editAction = function(widget, action, value) action = action:lower() local raction = CaveBot.Actions[action] if not raction then return warn("Invalid cavebot action: " .. action) end if not widget.action or not widget.value then return warn("Invalid cavebot action widget, has missing action or value") end widget:setText(action .. ":" .. value:split("\n")[1]) widget.action = action widget.value = value if raction.color then widget:setColor(raction.color) end return widget end --[[ registerAction: action - string, color - string, callback = function(value, retries, prev) value is a string value of action, retries is number which will grow by 1 if return is "retry" prev is a true when previuos action was executed succesfully, false otherwise it must return true if executed correctly, false otherwise it can also return string "retry", then the function will be called again in 20 ms ]]-- CaveBot.registerAction = function(action, color, callback) action = action:lower() if CaveBot.Actions[action] then return warn("Duplicated acction: " .. action) end CaveBot.Actions[action] = { color=color, callback=callback } end CaveBot.registerAction("label", "yellow", function(value, retries, prev) return true end) CaveBot.registerAction("gotolabel", "#FFFF55", function(value, retries, prev) return CaveBot.gotoLabel(value) end) CaveBot.registerAction("delay", "#AAAAAA", function(value, retries, prev) if retries == 0 then CaveBot.delay(tonumber(value)) return "retry" end return true end) CaveBot.registerAction("follow", "#FF8400", function(value, retries, prev) local c = getCreatureByName(value) if not c then print("CaveBot[follow]: can't find creature to follow") return false end local cpos = c:getPosition() local pos = pos() if getDistanceBetween(cpos, pos) < 2 then g_game.cancelFollow() return true else follow(c) delay(200) return "retry" end end) CaveBot.registerAction("function", "red", function(value, retries, prev) local prefix = "local retries = " .. retries .. "\nlocal prev = " .. tostring(prev) .. "\nlocal delay = CaveBot.delay\nlocal gotoLabel = CaveBot.gotoLabel\n" prefix = prefix .. "local macro = function() warn('Macros inside cavebot functions are not allowed') end\n" for extension, callbacks in pairs(CaveBot.Extensions) do prefix = prefix .. "local " .. extension .. " = CaveBot.Extensions." .. extension .. "\n" end local status, result = pcall(function() return assert(load(prefix .. value, "cavebot_function"))() end) if not status then warn("warn in cavebot function:\n" .. result) return false end return result end) CaveBot.registerAction("goto", "green", function(value, retries, prev) local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)") if not pos[1] then warn("Invalid cavebot goto action value. It should be position (x,y,z), is: " .. value) return false end if CaveBot.Config.get("mapClick") then if retries >= 5 then return false -- tried 5 times, can't get there end else if retries >= 100 then return false -- tried 100 times, can't get there end end local precision = tonumber(pos[1][5]) pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])} local playerPos = player:getPosition() if pos.z ~= playerPos.z then return false -- different floor end if math.abs(pos.x-playerPos.x) + math.abs(pos.y-playerPos.y) > 40 then return false -- too far way end local minimapColor = g_map.getMinimapColor(pos) local stairs = (minimapColor >= 210 and minimapColor <= 213) if stairs then if math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 0 then return true -- already at position end elseif math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= (precision or 1) then return true -- already at position end -- check if there's a path to that place, ignore creatures and fields local path = findPath(playerPos, pos, 40, { ignoreNonPathable = true, precision = 1, ignoreCreatures = true }) if not path then return false -- there's no way end -- check if there's a path to destination but consider Creatures (attack only if trapped) local path2 = findPath(playerPos, pos, 40, { ignoreNonPathable = true, precision = 1 }) if not path2 then local monsters = {} for i, spec in pairs(getSpectators()) do if spec:isMonster() and spec:getType() ~= 3 then if spec:canShoot() and findPath(playerPos, spec:getPosition(), 20, {ignoreNonPathable = true, precision = 1}) then table.insert(monsters, {mob = spec, dist = getDistanceBetween(pos, spec:getPosition())}) end end end table.sort(monsters, function(a,b) return a.dist < b.dist end) if monsters[1] then g_game.attack(monsters[1].mob) storage.blockMonster = monsters[1].mob autoWalk(storage.blockMonster, 10, {precision = 1}) storage.clearing = true CaveBot.setOff() g_game.setChaseMode(1) schedule(3000, function() CaveBot.setOn() end) -- just in case callback trigger fails return "retry" else return false -- there's no way end end -- try to find path, don't ignore creatures, don't ignore fields if not CaveBot.Config.get("ignoreFields") and CaveBot.walkTo(pos, 40) then return "retry" end -- try to find path, don't ignore creatures, ignore fields if CaveBot.walkTo(pos, 40, { ignoreNonPathable = true }) then return "retry" end if retries >= 3 then -- try to lower precision, find something close to final position local precison = retries - 1 if stairs then precison = 0 end if CaveBot.walkTo(pos, 50, { ignoreNonPathable = true, precision = precison }) then return "retry" end end if not CaveBot.Config.get("mapClick") and retries >= 5 then return false end if CaveBot.Config.get("skipBlocked") then return false end -- everything else failed, try to walk ignoring creatures, maybe will work CaveBot.walkTo(pos, 40, { ignoreNonPathable = true, precision = 1, ignoreCreatures = true }) return "retry" end) onCreatureDisappear(function(creature) if creature ~= storage.blockMonster then return end if storage.clearing then CaveBot.setOn() storage.blockMonster = nil storage.clearing = false end end) onCreaturePositionChange(function(creature, newPos, oldPos) if creature ~= storage.blockMonster and creature ~= player then return end if storage.clearing then if creature == storage.blockMonster and not findPath(player:getPosition(), newPos, 20, {ignoreNonPathable = true, precision = 1}) then CaveBot.setOn() storage.blockMonster = nil storage.clearing = false end if creature == player then if oldPos.z ~= newPos.z then CaveBot.setOn() storage.blockMonster = nil storage.clearing = false end end end end) CaveBot.registerAction("use", "#FFB272", function(value, retries, prev) local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)") if not pos[1] then local itemid = tonumber(value) if not itemid then warn("Invalid cavebot use action value. It should be (x,y,z) or item id, is: " .. value) return false end use(itemid) return true end pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])} local playerPos = player:getPosition() if pos.z ~= playerPos.z then return false -- different floor end if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then return false -- too far way end local tile = g_map.getTile(pos) if not tile then return false end local topThing = tile:getTopUseThing() if not topThing then return false end use(topThing) CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping")) return true end) CaveBot.registerAction("usewith", "#EEB292", function(value, retries, prev) local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)") if not pos[1] then if not itemid then warn("Invalid cavebot usewith action value. It should be (itemid,x,y,z) or item id, is: " .. value) return false end use(itemid) return true end local itemid = tonumber(pos[1][2]) pos = {x=tonumber(pos[1][3]), y=tonumber(pos[1][4]), z=tonumber(pos[1][5])} local playerPos = player:getPosition() if pos.z ~= playerPos.z then return false -- different floor end if math.max(math.abs(pos.x-playerPos.x), math.abs(pos.y-playerPos.y)) > 7 then return false -- too far way end local tile = g_map.getTile(pos) if not tile then return false end local topThing = tile:getTopUseThing() if not topThing then return false end usewith(itemid, topThing) CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping")) return true end) CaveBot.registerAction("say", "#FF55FF", function(value, retries, prev) say(value) return true end) CaveBot.registerAction("npcsay", "#FF55FF", function(value, retries, prev) NPC.say(value) return true end)