2022-06-06 22:04:52 +00:00

505 lines
15 KiB
Lua

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