mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-05-02 12:09:20 +02:00
505 lines
15 KiB
Lua
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) |