This commit is contained in:
OTCv8 2020-11-16 08:43:23 +01:00
parent a4f9250ddb
commit 2d1cbaae12
141 changed files with 14564 additions and 106 deletions

Binary file not shown.

BIN
data/sounds/Low_Health.ogg Normal file

Binary file not shown.

BIN
data/sounds/Low_Mana.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,7 +5,8 @@ PageButton < Button
ContainerWindow < MiniWindow ContainerWindow < MiniWindow
height: 150 height: 150
&save: true
&containerWindow: true
UIItem UIItem
id: containerItemWidget id: containerItemWidget

View File

@ -1,6 +1,6 @@
-- CONFIG -- CONFIG
APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata
APP_VERSION = 1342 -- client version for updater and login to identify outdated client APP_VERSION = 1343 -- client version for updater and login to identify outdated client
DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow
-- If you don't use updater or other service, set it to updater = "" -- If you don't use updater or other service, set it to updater = ""

View File

@ -15,9 +15,10 @@ local serverSelector
local clientVersionSelector local clientVersionSelector
local serverHostTextEdit local serverHostTextEdit
local rememberPasswordBox local rememberPasswordBox
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "870", "961", "1000", "1077", "1090", "1096", "1098", "1099", "1100", "1200", "1220"} local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "870", "910", "961", "1000", "1077", "1090", "1096", "1098", "1099", "1100", "1200", "1220", "1230", "1240", "1250", "1252"}
local checkedByUpdater = {} local checkedByUpdater = {}
local waitingForHttpResults = 0
-- private functions -- private functions
local function onProtocolError(protocol, message, errorCode) local function onProtocolError(protocol, message, errorCode)
@ -194,10 +195,20 @@ local function onTibia12HTTPResult(session, playdata)
onCharacterList(nil, characters, account, nil) onCharacterList(nil, characters, account, nil)
end end
local function onHTTPResult(data, err) local function onHTTPResult(data, err)
if waitingForHttpResults == 0 then
return
end
waitingForHttpResults = waitingForHttpResults - 1
if err and waitingForHttpResults > 0 then
return -- ignore, wait for other requests
end
if err then if err then
return EnterGame.onError(err) return EnterGame.onError(err)
end end
waitingForHttpResults = 0
if data['error'] and data['error']:len() > 0 then if data['error'] and data['error']:len() > 0 then
return EnterGame.onLoginError(data['error']) return EnterGame.onLoginError(data['error'])
elseif data['errorMessage'] and data['errorMessage']:len() > 0 then elseif data['errorMessage'] and data['errorMessage']:len() > 0 then
@ -396,7 +407,11 @@ function EnterGame.onServerChange()
customServerSelectorPanel:setOn(false) customServerSelectorPanel:setOn(false)
end end
if Servers and Servers[server] ~= nil then if Servers and Servers[server] ~= nil then
serverHostTextEdit:setText(Servers[server]) if type(Servers[server]) == "table" then
serverHostTextEdit:setText(Servers[server][1])
else
serverHostTextEdit:setText(Servers[server])
end
end end
end end
@ -541,7 +556,19 @@ function EnterGame.doLoginHttp()
stayloggedin = true stayloggedin = true
} }
HTTP.postJSON(G.host, data, onHTTPResult) local server = serverSelector:getText()
if Servers and Servers[server] ~= nil then
if type(Servers[server]) == "table" then
local urls = Servers[server]
waitingForHttpResults = #urls
for _, url in ipairs(urls) do
HTTP.postJSON(url, data, onHTTPResult)
end
else
waitingForHttpResults = 1
HTTP.postJSON(G.host, data, onHTTPResult)
end
end
EnterGame.hide() EnterGame.hide()
end end

View File

@ -11,6 +11,8 @@ local adaptiveRender = nil
local slowMain = nil local slowMain = nil
local slowRender = nil local slowRender = nil
local widgetsInfo = nil local widgetsInfo = nil
local packets
local slowPackets
local updateEvent = nil local updateEvent = nil
local monitorEvent = nil local monitorEvent = nil
@ -36,9 +38,11 @@ function init()
dispatcherStats = statsWindow:recursiveGetChildById('dispatcherStats') dispatcherStats = statsWindow:recursiveGetChildById('dispatcherStats')
render = statsWindow:recursiveGetChildById('render') render = statsWindow:recursiveGetChildById('render')
atlas = statsWindow:recursiveGetChildById('atlas') atlas = statsWindow:recursiveGetChildById('atlas')
packets = statsWindow:recursiveGetChildById('packets')
adaptiveRender = statsWindow:recursiveGetChildById('adaptiveRender') adaptiveRender = statsWindow:recursiveGetChildById('adaptiveRender')
slowMain = statsWindow:recursiveGetChildById('slowMain') slowMain = statsWindow:recursiveGetChildById('slowMain')
slowRender = statsWindow:recursiveGetChildById('slowRender') slowRender = statsWindow:recursiveGetChildById('slowRender')
slowPackets = statsWindow:recursiveGetChildById('slowPackets')
widgetsInfo = statsWindow:recursiveGetChildById('widgetsInfo') widgetsInfo = statsWindow:recursiveGetChildById('widgetsInfo')
lastSend = os.time() lastSend = os.time()
@ -177,7 +181,7 @@ function update()
return return
end end
iter = (iter + 1) % 8 -- some functions are slow (~5ms), it will avoid lags iter = (iter + 1) % 9 -- some functions are slow (~5ms), it will avoid lags
if iter == 0 then if iter == 0 then
statsWindow.debugPanel.sleepTime:setText("GFPS: " .. g_app.getGraphicsFps() .. " PFPS: " .. g_app.getProcessingFps() .. " Packets: " .. g_game.getRecivedPacketsCount() .. " , " .. (g_game.getRecivedPacketsSize() / 1024) .. " KB") statsWindow.debugPanel.sleepTime:setText("GFPS: " .. g_app.getGraphicsFps() .. " PFPS: " .. g_app.getProcessingFps() .. " Packets: " .. g_game.getRecivedPacketsCount() .. " , " .. (g_game.getRecivedPacketsSize() / 1024) .. " KB")
statsWindow.debugPanel.luaRamUsage:setText("Ram usage by lua: " .. gcinfo() .. " kb") statsWindow.debugPanel.luaRamUsage:setText("Ram usage by lua: " .. gcinfo() .. " kb")
@ -200,6 +204,9 @@ function update()
--disabled because takes a lot of cpu --disabled because takes a lot of cpu
--widgetsInfo:setText(g_stats.getWidgetsInfo(10, true)) --widgetsInfo:setText(g_stats.getWidgetsInfo(10, true))
elseif iter == 7 then elseif iter == 7 then
packets:setText(g_stats.get(6, 10, true))
slowPackets:setText(g_stats.getSlow(6, 10, 10, true))
elseif iter == 8 then
if g_proxy then if g_proxy then
local text = "" local text = ""
local proxiesDebug = g_proxy.getProxiesDebugInfo() local proxiesDebug = g_proxy.getProxiesDebugInfo()

View File

@ -104,6 +104,13 @@ MainWindow
id: widgetsInfo id: widgetsInfo
text: Disabled, edit stats.lua to enable text: Disabled, edit stats.lua to enable
DebugLabel
!text: tr('Packets')
DebugText
id: packets
text: -
DebugLabel DebugLabel
!text: tr('Slow main functions') !text: tr('Slow main functions')
@ -117,6 +124,13 @@ MainWindow
DebugText DebugText
id: slowRender id: slowRender
text: - text: -
DebugLabel
!text: tr('Slow packets')
DebugText
id: slowPackets
text: -
VerticalScrollBar VerticalScrollBar
id: debugScroll id: debugScroll

View File

@ -165,8 +165,7 @@ function UIMiniWindow:setup()
self:eraseSettings({height = true}) self:eraseSettings({height = true})
end end
end end
if selfSettings.closed and not self.forceOpen and not self.containerWindow then
if selfSettings.closed and not self.forceOpen then
self:close(true) self:close(true)
end end
@ -174,7 +173,7 @@ function UIMiniWindow:setup()
self:lock(true) self:lock(true)
end end
else else
if not self.forceOpen and self.autoOpen ~= nil and (self.autoOpen == 0 or self.autoOpen == false) then if not self.forceOpen and self.autoOpen ~= nil and (self.autoOpen == 0 or self.autoOpen == false) and not self.containerWindow then
self:close(true) self:close(true)
end end
end end
@ -185,7 +184,7 @@ function UIMiniWindow:setup()
self.miniLoaded = true self.miniLoaded = true
if self.save then if self.save then
if oldParent and oldParent:getClassName() == 'UIMiniWindowContainer' then if oldParent and oldParent:getClassName() == 'UIMiniWindowContainer' and not self.containerWindow then
addEvent(function() oldParent:order() end) addEvent(function() oldParent:order() end)
end end
if newParent and newParent:getClassName() == 'UIMiniWindowContainer' and newParent ~= oldParent then if newParent and newParent:getClassName() == 'UIMiniWindowContainer' and newParent ~= oldParent then
@ -350,6 +349,20 @@ function UIMiniWindow:eraseSettings(data)
g_settings.setNode('MiniWindows', settings) g_settings.setNode('MiniWindows', settings)
end end
function UIMiniWindow:clearSettings()
if not self.save then return end
local settings = g_settings.getNode('MiniWindows')
if not settings then
settings = {}
end
local id = self:getId()
settings[id] = {}
g_settings.setNode('MiniWindows', settings)
end
function UIMiniWindow:saveParent(parent) function UIMiniWindow:saveParent(parent)
local parent = self:getParent() local parent = self:getParent()
if parent then if parent then

View File

@ -168,7 +168,15 @@ function UIMiniWindowContainer:scheduleInsert(widget, index)
local placed = false local placed = false
for nIndex,nWidget in pairs(self.scheduledWidgets) do for nIndex,nWidget in pairs(self.scheduledWidgets) do
if nIndex - 1 <= self:getChildCount() then if nIndex - 1 <= self:getChildCount() then
self:insertChild(nIndex, nWidget) local oldParent = nWidget:getParent()
if oldParent ~= self then
if oldParent then
oldParent:removeChild(nWidget)
end
self:insertChild(nIndex, nWidget)
else
self:moveChildToIndex(nWidget, nIndex)
end
self.scheduledWidgets[nIndex] = nil self.scheduledWidgets[nIndex] = nil
placed = true placed = true
break break
@ -176,7 +184,6 @@ function UIMiniWindowContainer:scheduleInsert(widget, index)
end end
if not placed then break end if not placed then break end
end end
end end
end end
end end

View File

@ -285,30 +285,30 @@ function createDefaultConfigs()
if not g_resources.directoryExists("/bot/" .. config_name) then if not g_resources.directoryExists("/bot/" .. config_name) then
return onError("Can't create /bot/" .. config_name .. " directory in " .. g_resources.getWriteDir()) return onError("Can't create /bot/" .. config_name .. " directory in " .. g_resources.getWriteDir())
end end
end
local defaultConfigFiles = g_resources.listDirectoryFiles("default_configs/" .. config_name, true, false) local defaultConfigFiles = g_resources.listDirectoryFiles("default_configs/" .. config_name, true, false)
for i, file in ipairs(defaultConfigFiles) do for i, file in ipairs(defaultConfigFiles) do
local baseName = file:split("/") local baseName = file:split("/")
baseName = baseName[#baseName] baseName = baseName[#baseName]
if g_resources.directoryExists(file) then if g_resources.directoryExists(file) then
g_resources.makeDir("/bot/" .. config_name .. "/" .. baseName) g_resources.makeDir("/bot/" .. config_name .. "/" .. baseName)
if not g_resources.directoryExists("/bot/" .. config_name .. "/" .. baseName) then if not g_resources.directoryExists("/bot/" .. config_name .. "/" .. baseName) then
return onError("Can't create /bot/" .. config_name .. "/" .. baseName .. " directory in " .. g_resources.getWriteDir()) return onError("Can't create /bot/" .. config_name .. "/" .. baseName .. " directory in " .. g_resources.getWriteDir())
end end
local defaultConfigFiles2 = g_resources.listDirectoryFiles("default_configs/" .. config_name .. "/" .. baseName, true, false) local defaultConfigFiles2 = g_resources.listDirectoryFiles("default_configs/" .. config_name .. "/" .. baseName, true, false)
for i, file in ipairs(defaultConfigFiles2) do for i, file in ipairs(defaultConfigFiles2) do
local baseName2 = file:split("/") local baseName2 = file:split("/")
baseName2 = baseName2[#baseName2] baseName2 = baseName2[#baseName2]
local contents = g_resources.fileExists(file) and g_resources.readFileContents(file) or ""
if contents:len() > 0 then
g_resources.writeFileContents("/bot/" .. config_name .. "/" .. baseName .. "/" .. baseName2, contents)
end
end
else
local contents = g_resources.fileExists(file) and g_resources.readFileContents(file) or "" local contents = g_resources.fileExists(file) and g_resources.readFileContents(file) or ""
if contents:len() > 0 then if contents:len() > 0 then
g_resources.writeFileContents("/bot/" .. config_name .. "/" .. baseName .. "/" .. baseName2, contents) g_resources.writeFileContents("/bot/" .. config_name .. "/" .. baseName, contents)
end end
end
else
local contents = g_resources.fileExists(file) and g_resources.readFileContents(file) or ""
if contents:len() > 0 then
g_resources.writeFileContents("/bot/" .. config_name .. "/" .. baseName, contents)
end end
end end
end end
@ -321,8 +321,8 @@ function uploadConfig()
if not archive then if not archive then
return displayErrorBox(tr("Config upload failed"), tr("Config %s is invalid (can't be compressed)", config)) return displayErrorBox(tr("Config upload failed"), tr("Config %s is invalid (can't be compressed)", config))
end end
if archive:len() > 64 * 1024 then if archive:len() > 1024 * 1024 then
return displayErrorBox(tr("Config upload failed"), tr("Config %s is too big, maximum size is 64KB. Now it has %s KB.", config, math.floor(archive / 1024))) return displayErrorBox(tr("Config upload failed"), tr("Config %s is too big, maximum size is 1024KB. Now it has %s KB.", config, math.floor(archive:len() / 1024)))
end end
local infoBox = displayInfoBox(tr("Uploading config"), tr("Uploading config %s. Please wait.", config)) local infoBox = displayInfoBox(tr("Uploading config"), tr("Uploading config %s. Please wait.", config))
@ -468,6 +468,7 @@ function initCallbacks()
connect(g_game, { connect(g_game, {
onTalk = botOnTalk, onTalk = botOnTalk,
onTextMessage = botOnTextMessage, onTextMessage = botOnTextMessage,
onLoginAdvice = botOnLoginAdvice,
onUse = botOnUse, onUse = botOnUse,
onUseWith = botOnUseWith, onUseWith = botOnUseWith,
onChannelList = botChannelList, onChannelList = botChannelList,
@ -486,12 +487,15 @@ function initCallbacks()
onDisappear = botCreatureDisappear, onDisappear = botCreatureDisappear,
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange, onHealthPercentChange = botCraetureHealthPercentChange,
onTurn = botCreatureTurn onTurn = botCreatureTurn,
}) onWalk = botCreatureWalk,
})
connect(LocalPlayer, { connect(LocalPlayer, {
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange onHealthPercentChange = botCraetureHealthPercentChange,
onTurn = botCreatureTurn,
onWalk = botCreatureWalk,
}) })
connect(Container, { connect(Container, {
@ -517,6 +521,7 @@ function terminateCallbacks()
disconnect(g_game, { disconnect(g_game, {
onTalk = botOnTalk, onTalk = botOnTalk,
onTextMessage = botOnTextMessage, onTextMessage = botOnTextMessage,
onLoginAdvice = botOnLoginAdvice,
onUse = botOnUse, onUse = botOnUse,
onUseWith = botOnUseWith, onUseWith = botOnUseWith,
onChannelList = botChannelList, onChannelList = botChannelList,
@ -535,12 +540,15 @@ function terminateCallbacks()
onDisappear = botCreatureDisappear, onDisappear = botCreatureDisappear,
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange, onHealthPercentChange = botCraetureHealthPercentChange,
onTurn = botCreatureTurn onTurn = botCreatureTurn,
onWalk = botCreatureWalk,
}) })
disconnect(LocalPlayer, { disconnect(LocalPlayer, {
onPositionChange = botCreaturePositionChange, onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange onHealthPercentChange = botCraetureHealthPercentChange,
onTurn = botCreatureTurn,
onWalk = botCreatureWalk,
}) })
disconnect(Container, { disconnect(Container, {
@ -591,6 +599,11 @@ function botOnTextMessage(mode, text)
safeBotCall(function() botExecutor.callbacks.onTextMessage(mode, text) end) safeBotCall(function() botExecutor.callbacks.onTextMessage(mode, text) end)
end end
function botOnLoginAdvice(message)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onLoginAdvice(message) end)
end
function botAddThing(tile, thing) function botAddThing(tile, thing)
if botExecutor == nil then return false end if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onAddThing(tile, thing) end) safeBotCall(function() botExecutor.callbacks.onAddThing(tile, thing) end)
@ -684,4 +697,9 @@ end
function botCreatureTurn(creature, direction) function botCreatureTurn(creature, direction)
if botExecutor == nil then return false end if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onTurn(creature, direction) end) safeBotCall(function() botExecutor.callbacks.onTurn(creature, direction) end)
end
function botCreatureWalk(creature, oldPos, newPos)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onWalk(creature, oldPos, newPos) end)
end end

View File

@ -8,12 +8,15 @@ setDefaultTab(cavebotTab)
CaveBot = {} -- global namespace CaveBot = {} -- global namespace
CaveBot.Extensions = {} CaveBot.Extensions = {}
importStyle("/cavebot/cavebot.otui") importStyle("/cavebot/cavebot.otui")
importStyle("/cavebot/config.otui")
importStyle("/cavebot/editor.otui") importStyle("/cavebot/editor.otui")
importStyle("/cavebot/supply.otui") importStyle("/cavebot/supply.otui")
dofile("/cavebot/actions.lua") dofile("/cavebot/actions.lua")
dofile("/cavebot/config.lua")
dofile("/cavebot/editor.lua") dofile("/cavebot/editor.lua")
dofile("/cavebot/example_functions.lua") dofile("/cavebot/example_functions.lua")
dofile("/cavebot/recorder.lua") dofile("/cavebot/recorder.lua")
dofile("/cavebot/walking.lua")
-- in this section you can add extensions, check extension_template.lua -- in this section you can add extensions, check extension_template.lua
--dofile("/cavebot/extension_template.lua") --dofile("/cavebot/extension_template.lua")
dofile("/cavebot/depositer.lua") dofile("/cavebot/depositer.lua")

View File

@ -107,16 +107,23 @@ CaveBot.registerAction("function", "red", function(value, retries, prev)
end) end)
CaveBot.registerAction("goto", "green", function(value, retries, prev) CaveBot.registerAction("goto", "green", function(value, retries, prev)
local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)") local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)")
if not pos[1] then if not pos[1] then
error("Invalid cavebot goto action value. It should be position (x,y,z), is: " .. value) error("Invalid cavebot goto action value. It should be position (x,y,z), is: " .. value)
return false return false
end end
if retries >= 5 then if CaveBot.Config.get("mapClick") then
return false -- tried 5 times, can't get there 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 end
local precision = tonumber(pos[1][5])
pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])} pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
local playerPos = player:getPosition() local playerPos = player:getPosition()
if pos.z ~= playerPos.z then if pos.z ~= playerPos.z then
@ -134,7 +141,7 @@ CaveBot.registerAction("goto", "green", function(value, retries, prev)
if math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 0 then if math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 0 then
return true -- already at position return true -- already at position
end end
elseif math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= 1 then elseif math.abs(pos.x-playerPos.x) == 0 and math.abs(pos.y-playerPos.y) <= (precision or 1) then
return true -- already at position return true -- already at position
end end
-- check if there's a path to that place, ignore creatures and fields -- check if there's a path to that place, ignore creatures and fields
@ -142,17 +149,14 @@ CaveBot.registerAction("goto", "green", function(value, retries, prev)
if not path then if not path then
return false -- there's no way return false -- there's no way
end end
-- walk will be executed, but it will take some time to get response from server, wait 300ms after autowalk
CaveBot.delay(300)
-- try to find path, don't ignore creatures, don't ignore fields -- try to find path, don't ignore creatures, don't ignore fields
if autoWalk(pos, 40) then if not CaveBot.Config.get("ignoreFields") and CaveBot.walkTo(pos, 40) then
return "retry" return "retry"
end end
-- try to find path, don't ignore creatures, ignore fields -- try to find path, don't ignore creatures, ignore fields
if autoWalk(pos, 40, { ignoreNonPathable = true }) then if CaveBot.walkTo(pos, 40, { ignoreNonPathable = true }) then
return "retry" return "retry"
end end
@ -162,12 +166,21 @@ CaveBot.registerAction("goto", "green", function(value, retries, prev)
if stairs then if stairs then
precison = 0 precison = 0
end end
if autoWalk(pos, 50, { ignoreNonPathable = true, precision = precison }) then if CaveBot.walkTo(pos, 50, { ignoreNonPathable = true, precision = precison }) then
return "retry" return "retry"
end end
end end
autoWalk(path) -- everything else failed, try to walk ignoring creatures, maybe will work 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" return "retry"
end) end)
@ -204,7 +217,7 @@ CaveBot.registerAction("use", "#FFB272", function(value, retries, prev)
end end
use(topThing) use(topThing)
CaveBot.delay(400) CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping"))
return true return true
end) end)
@ -241,11 +254,11 @@ CaveBot.registerAction("usewith", "#EEB292", function(value, retries, prev)
end end
usewith(itemid, topThing) usewith(itemid, topThing)
CaveBot.delay(400) CaveBot.delay(CaveBot.Config.get("useDelay") + CaveBot.Config.get("ping"))
return true return true
end) end)
CaveBot.registerAction("say", "#FF55FF", function(value, retries, prev) CaveBot.registerAction("say", "#FF55FF", function(value, retries, prev)
say(value) say(value)
return true return true
end) end)

View File

@ -11,6 +11,9 @@ CaveBot.actionList = ui.list
if CaveBot.Editor then if CaveBot.Editor then
CaveBot.Editor.setup() CaveBot.Editor.setup()
end end
if CaveBot.Config then
CaveBot.Config.setup()
end
for extension, callbacks in pairs(CaveBot.Extensions) do for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.setup then if callbacks.setup then
callbacks.setup() callbacks.setup()
@ -22,9 +25,14 @@ local actionRetries = 0
local prevActionResult = true local prevActionResult = true
cavebotMacro = macro(20, function() cavebotMacro = macro(20, function()
if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then
CaveBot.resetWalking()
return -- target bot or looting is working, wait return -- target bot or looting is working, wait
end end
if CaveBot.doWalking() then
return -- executing walking
end
local actions = ui.list:getChildCount() local actions = ui.list:getChildCount()
if actions == 0 then return end if actions == 0 then return end
local currentAction = ui.list:getFocusedChild() local currentAction = ui.list:getFocusedChild()
@ -36,6 +44,7 @@ cavebotMacro = macro(20, function()
local retry = false local retry = false
if action then if action then
local status, result = pcall(function() local status, result = pcall(function()
CaveBot.resetWalking()
return action.callback(value, actionRetries, prevActionResult) return action.callback(value, actionRetries, prevActionResult)
end) end)
if status then if status then
@ -72,11 +81,6 @@ cavebotMacro = macro(20, function()
ui.list:focusChild(ui.list:getChildByIndex(nextAction)) ui.list:focusChild(ui.list:getChildByIndex(nextAction))
end) end)
onPlayerPositionChange(function()
local delayAfterPositionChange = math.max(player:getStepDuration() + 100, 200)
cavebotMacro.delay = math.max(cavebotMacro.delay or 0, now + delayAfterPositionChange)
end)
-- config, its callback is called immediately, data can be nil -- config, its callback is called immediately, data can be nil
local lastConfig = "" local lastConfig = ""
config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, enabled, data) config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, enabled, data)
@ -90,9 +94,19 @@ config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, ena
ui.list:destroyChildren() ui.list:destroyChildren()
if not data then return cavebotMacro.setOff() end if not data then return cavebotMacro.setOff() end
local cavebotConfig = nil
for k,v in ipairs(data) do for k,v in ipairs(data) do
if type(v) == "table" and #v == 2 then if type(v) == "table" and #v == 2 then
if v[1] == "extensions" then if v[1] == "config" then
local status, result = pcall(function()
return json.decode(v[2])
end)
if not status then
error("Error while parsing CaveBot extensions from config:\n" .. result)
else
cavebotConfig = result
end
elseif v[1] == "extensions" then
local status, result = pcall(function() local status, result = pcall(function()
return json.decode(v[2]) return json.decode(v[2])
end) end)
@ -110,8 +124,11 @@ config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, ena
end end
end end
end end
CaveBot.Config.onConfigChange(name, enabled, cavebotConfig)
actionRetries = 0 actionRetries = 0
CaveBot.resetWalking()
prevActionResult = true prevActionResult = true
cavebotMacro.setOn(enabled) cavebotMacro.setOn(enabled)
cavebotMacro.delay = nil cavebotMacro.delay = nil
@ -134,6 +151,16 @@ ui.showEditor.onClick = function()
end end
end end
ui.showConfig.onClick = function()
if not CaveBot.Config then return end
if ui.showConfig:isOn() then
CaveBot.Config.hide()
ui.showConfig:setOn(false)
else
CaveBot.Config.show()
ui.showConfig:setOn(true)
end
end
-- public function, you can use them in your scripts -- public function, you can use them in your scripts
CaveBot.isOn = function() CaveBot.isOn = function()
@ -159,7 +186,7 @@ CaveBot.setOff = function(val)
end end
CaveBot.delay = function(value) CaveBot.delay = function(value)
cavebotMacro.delay = now + value cavebotMacro.delay = math.max(cavebotMacro.delay or 0, now + value)
end end
CaveBot.gotoLabel = function(label) CaveBot.gotoLabel = function(label)
@ -178,6 +205,11 @@ CaveBot.save = function()
for index, child in ipairs(ui.list:getChildren()) do for index, child in ipairs(ui.list:getChildren()) do
table.insert(data, {child.action, child.value}) table.insert(data, {child.action, child.value})
end end
if CaveBot.Config then
table.insert(data, {"config", json.encode(CaveBot.Config.save())})
end
local extension_data = {} local extension_data = {}
for extension, callbacks in pairs(CaveBot.Extensions) do for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.onSave then if callbacks.onSave then

View File

@ -46,3 +46,13 @@ CaveBotPanel < Panel
$!on: $!on:
text: Show waypoints editor text: Show waypoints editor
BotSwitch
id: showConfig
margin-top: 2
$on:
text: Hide config
$!on:
text: Show config

View File

@ -0,0 +1,94 @@
-- config for bot
CaveBot.Config = {}
CaveBot.Config.values = {}
CaveBot.Config.default_values = {}
CaveBot.Config.value_setters = {}
CaveBot.Config.setup = function()
CaveBot.Config.ui = UI.createWidget("CaveBotConfigPanel")
local ui = CaveBot.Config.ui
local add = CaveBot.Config.add
add("ping", "Server ping", 100)
add("walkDelay", "Walk delay", 10)
add("mapClick", "Use map click", false)
add("mapClickDelay", "Map click delay", 100)
add("ignoreFields", "Ignore fields", false)
add("skipBlocked", "Skip blocked path", false)
add("useDelay", "Delay after use", 400)
end
CaveBot.Config.show = function()
CaveBot.Config.ui:show()
end
CaveBot.Config.hide = function()
CaveBot.Config.ui:hide()
end
CaveBot.Config.onConfigChange = function(configName, isEnabled, configData)
for k, v in pairs(CaveBot.Config.default_values) do
CaveBot.Config.value_setters[k](v)
end
if not configData then return end
for k, v in pairs(configData) do
if CaveBot.Config.value_setters[k] then
CaveBot.Config.value_setters[k](v)
end
end
end
CaveBot.Config.save = function()
return CaveBot.Config.values
end
CaveBot.Config.add = function(id, title, defaultValue)
if CaveBot.Config.values[id] then
return error("Duplicated config key: " .. id)
end
local panel
local setter -- sets value
if type(defaultValue) == "number" then
panel = UI.createWidget("CaveBotConfigNumberValuePanel", CaveBot.Config.ui)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setText(value, true)
end
setter(defaultValue)
panel.value.onTextChange = function(widget, newValue)
newValue = tonumber(newValue)
if newValue then
CaveBot.Config.values[id] = newValue
CaveBot.save()
end
end
elseif type(defaultValue) == "boolean" then
panel = UI.createWidget("CaveBotConfigBooleanValuePanel", CaveBot.Config.ui)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setOn(value, true)
end
setter(defaultValue)
panel.value.onClick = function(widget)
widget:setOn(not widget:isOn())
CaveBot.Config.values[id] = widget:isOn()
CaveBot.save()
end
else
return error("Invalid default value of config for key " .. id .. ", should be number or boolean")
end
panel.title:setText(tr(title) .. ":")
CaveBot.Config.value_setters[id] = setter
CaveBot.Config.values[id] = defaultValue
CaveBot.Config.default_values[id] = defaultValue
end
CaveBot.Config.get = function(id)
if CaveBot.Config.values[id] == nil then
return error("Invalid CaveBot.Config.get, id: " .. id)
end
return CaveBot.Config.values[id]
end

View File

@ -0,0 +1,57 @@
CaveBotConfigPanel < Panel
id: cavebotEditor
visible: false
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 5
Label
text-align: center
text: CaveBot Config
margin-top: 5
CaveBotConfigNumberValuePanel < Panel
height: 20
margin-top: 5
BotTextEdit
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
CaveBotConfigBooleanValuePanel < Panel
height: 20
margin-top: 5
BotSwitch
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
$on:
text: On
$!on:
text: Off
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5

View File

@ -104,7 +104,7 @@ CaveBot.Editor.setup = function()
title="Go to position", title="Go to position",
description="Go to position (x,y,z)", description="Go to position (x,y,z)",
multiline=false, multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$" validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)$"
}) })
registerAction("use", { registerAction("use", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end, value=function() return posx() .. "," .. posy() .. "," .. posz() end,

View File

@ -63,3 +63,28 @@ delay(100 + retries * 100)
return "retry" return "retry"
]]) ]])
addExampleFunction("Disable TargetBot", [[
TargetBot.setOff()
return true
]])
addExampleFunction("Enable TargetBot", [[
TargetBot.setOn()
return true
]])
addExampleFunction("Enable TargetBot luring", [[
TargetBot.enableLuring()
return true
]])
addExampleFunction("Disable TargetBot luring", [[
TargetBot.disableLuring()
return true
]])
addExampleFunction("Logout", [[
g_game.safeLogout()
delay(1000)
return "retry"
]])

View File

@ -0,0 +1,93 @@
-- walking
local expectedDirs = {}
local isWalking = {}
local walkPath = {}
local walkPathIter = 0
CaveBot.resetWalking = function()
expectedDirs = {}
walkPath = {}
isWalking = false
end
CaveBot.doWalking = function()
if CaveBot.Config.get("mapClick") then
return false
end
if #expectedDirs == 0 then
return false
end
if #expectedDirs >= 3 then
CaveBot.resetWalking()
end
local dir = walkPath[walkPathIter]
if dir then
g_game.walk(dir, false)
table.insert(expectedDirs, dir)
walkPathIter = walkPathIter + 1
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end
return false
end
-- called when player position has been changed (step has been confirmed by server)
onPlayerPositionChange(function(newPos, oldPos)
if not oldPos or not newPos then return end
local dirs = {{NorthWest, North, NorthEast}, {West, 8, East}, {SouthWest, South, SouthEast}}
local dir = dirs[newPos.y - oldPos.y + 2]
if dir then
dir = dir[newPos.x - oldPos.x + 2]
end
if not dir then
dir = 8 -- 8 is invalid dir, it's fine
end
if not isWalking or not expectedDirs[1] then
-- some other walk action is taking place (for example use on ladder), wait
walkPath = {}
CaveBot.delay(CaveBot.Config.get("ping") + player:getStepDuration(false, dir) + 150)
return
end
if expectedDirs[1] ~= dir then
if CaveBot.Config.get("mapClick") then
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
else
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
return
end
table.remove(expectedDirs, 1)
if CaveBot.Config.get("mapClick") and #expectedDirs > 0 then
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
end)
CaveBot.walkTo = function(dest, maxDist, params)
local path = getPath(player:getPosition(), dest, maxDist, params)
if not path or not path[1] then
return false
end
local dir = path[1]
if CaveBot.Config.get("mapClick") then
local ret = autoWalk(path)
if ret then
isWalking = true
expectedDirs = path
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + math.max(CaveBot.Config.get("ping") + player:getStepDuration(false, dir), player:getStepDuration(false, dir) * 2))
end
return ret
end
g_game.walk(dir, false)
isWalking = true
walkPath = path
walkPathIter = 2
expectedDirs = { dir }
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end

View File

@ -1,5 +1,5 @@
-- main tab -- main tab
VERSION = "1.2" VERSION = "1.3"
UI.Label("Config version: " .. VERSION) UI.Label("Config version: " .. VERSION)

View File

@ -20,7 +20,13 @@ TargetBot.Creature.addConfig = function(config, focus)
TargetBot.Creature.resetConfigsCache() TargetBot.Creature.resetConfigsCache()
if not config.regex then if not config.regex then
config.regex = "^" .. config.name:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$" config.regex = ""
for part in string.gmatch(config.name, "[^,]+") do
if config.regex:len() > 0 then
config.regex = config.regex .. "|"
end
config.regex = config.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
end
end end
local widget = UI.createWidget("TargetBotEntry", TargetBot.targetList) local widget = UI.createWidget("TargetBotEntry", TargetBot.targetList)

View File

@ -67,8 +67,18 @@ TargetBot.Creature.walk = function(creature, config, targets)
local cpos = creature:getPosition() local cpos = creature:getPosition()
local pos = player:getPosition() local pos = player:getPosition()
local isTrapped = true
local pos = player:getPosition()
local dirs = {{-1,1}, {0,1}, {1,1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}
for i=1,#dirs do
local tile = g_map.getTile({x=pos.x-dirs[i][1],y=pos.y-dirs[i][2],z=pos.z})
if tile and tile:isWalkable(false) then
isTrapped = false
end
end
-- luring -- luring
if (config.lure or config.lureCavebot) and not (config.chase and creature:getHealthPercent() < 30) then if TargetBot.canLure() and (config.lure or config.lureCavebot) and not (config.chase and creature:getHealthPercent() < 30) and not isTrapped then
local monsters = 0 local monsters = 0
if targets < config.lureCount then if targets < config.lureCount then
if config.lureCavebot then if config.lureCavebot then

View File

@ -8,7 +8,7 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC
table.insert(values, {"name", function() return editor.name:getText() end}) table.insert(values, {"name", function() return editor.name:getText() end})
local addScrollBar = function(id, title, min, max, defaultValue) local addScrollBar = function(id, title, min, max, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorScrollBar', editor.left) local widget = UI.createWidget('TargetBotCreatureEditorScrollBar', editor.content.left)
widget.scroll.onValueChange = function(scroll, value) widget.scroll.onValueChange = function(scroll, value)
widget.text:setText(title .. ": " .. value) widget.text:setText(title .. ": " .. value)
end end
@ -24,14 +24,14 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC
end end
local addTextEdit = function(id, title, defaultValue) local addTextEdit = function(id, title, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorTextEdit', editor.right) local widget = UI.createWidget('TargetBotCreatureEditorTextEdit', editor.content.right)
widget.text:setText(title) widget.text:setText(title)
widget.textEdit:setText(config[id] or defaultValue or "") widget.textEdit:setText(config[id] or defaultValue or "")
table.insert(values, {id, function() return widget.textEdit:getText() end}) table.insert(values, {id, function() return widget.textEdit:getText() end})
end end
local addCheckBox = function(id, title, defaultValue) local addCheckBox = function(id, title, defaultValue)
local widget = UI.createWidget('TargetBotCreatureEditorCheckBox', editor.right) local widget = UI.createWidget('TargetBotCreatureEditorCheckBox', editor.content.right)
widget.onClick = function() widget.onClick = function()
widget:setOn(not widget:isOn()) widget:setOn(not widget:isOn())
end end
@ -45,7 +45,7 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC
end end
local addItem = function(id, title, defaultItem) local addItem = function(id, title, defaultItem)
local widget = UI.createWidget('TargetBotCreatureEditorItem', editor.right) local widget = UI.createWidget('TargetBotCreatureEditorItem', editor.content.right)
widget.text:setText(title) widget.text:setText(title)
widget.item:setItemId(config[id] or defaultItem) widget.item:setItemId(config[id] or defaultItem)
table.insert(values, {id, function() return widget.item:getItemId() end}) table.insert(values, {id, function() return widget.item:getItemId() end})
@ -62,7 +62,14 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC
newConfig[value[1]] = value[2]() newConfig[value[1]] = value[2]()
end end
if newConfig.name:len() < 1 then return end if newConfig.name:len() < 1 then return end
newConfig.regex = "^" .. newConfig.name:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
newConfig.regex = ""
for part in string.gmatch(newConfig.name, "[^,]+") do
if newConfig.regex:len() > 0 then
newConfig.regex = newConfig.regex .. "|"
end
newConfig.regex = newConfig.regex .. "^" .. part:trim():lower():gsub("%*", ".*"):gsub("%?", ".?") .. "$"
end
editor:destroy() editor:destroy()
callback(newConfig) callback(newConfig)
@ -88,6 +95,7 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC
addCheckBox("chase", "Chase", true) addCheckBox("chase", "Chase", true)
addCheckBox("keepDistance", "Keep Distance", false) addCheckBox("keepDistance", "Keep Distance", false)
addCheckBox("dontLoot", "Don't loot", false)
addCheckBox("lure", "Lure", false) addCheckBox("lure", "Lure", false)
addCheckBox("lureCavebot", "Lure using cavebot", false) addCheckBox("lureCavebot", "Lure using cavebot", false)
addCheckBox("avoidAttacks", "Avoid wave attacks", false) addCheckBox("avoidAttacks", "Avoid wave attacks", false)

View File

@ -64,14 +64,24 @@ TargetBotCreatureEditorCheckBox < BotSwitch
TargetBotCreatureEditorWindow < MainWindow TargetBotCreatureEditorWindow < MainWindow
text: TargetBot creature editor text: TargetBot creature editor
width: 500 width: 500
height: 600 height: 630
$mobile:
height: 300
Label Label
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
text-align: center text-align: center
text: You can use * (any characters) and ? (any character) in target name !text: tr('You can use * (any characters) and ? (any character) in target name')
Label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
text-align: center
!text: tr('You can also enter multiple targets, separate them by ,')
TextEdit TextEdit
id: name id: name
@ -86,29 +96,49 @@ TargetBotCreatureEditorWindow < MainWindow
anchors.left: parent.left anchors.left: parent.left
text: Target name: text: Target name:
Panel VerticalScrollBar
id: left id: contentScroll
anchors.top: name.bottom
anchors.right: parent.right
anchors.bottom: help.top
step: 28
pixels-scroll: true
margin-right: -10
margin-top: 5
margin-bottom: 5
ScrollablePanel
id: content
anchors.top: name.bottom anchors.top: name.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Panel
id: right
anchors.top: name.bottom
anchors.left: parent.horizontalCenter
anchors.right: parent.right anchors.right: parent.right
margin-top: 5 anchors.bottom: help.top
margin-left: 10 vertical-scrollbar: contentScroll
margin-right: 10 margin-bottom: 10
layout:
type: verticalBox Panel
fit-children: true id: left
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Panel
id: right
anchors.top: parent.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-top: 5
margin-left: 10
margin-right: 10
layout:
type: verticalBox
fit-children: true
Button Button
id: help id: help

View File

@ -277,6 +277,10 @@ end)
onCreatureDisappear(function(creature) onCreatureDisappear(function(creature)
if not TargetBot.isOn() then return end if not TargetBot.isOn() then return end
if not creature:isMonster() then return end if not creature:isMonster() then return end
local config = TargetBot.Creature.calculateParams(creature, {}) -- return {craeture, config, danger, priority}
if not config.config or config.config.dontLoot then
return
end
local pos = player:getPosition() local pos = player:getPosition()
local mpos = creature:getPosition() local mpos = creature:getPosition()
local name = creature:getName() local name = creature:getName()

View File

@ -2,6 +2,7 @@ local targetbotMacro = nil
local config = nil local config = nil
local lastAction = 0 local lastAction = 0
local cavebotAllowance = 0 local cavebotAllowance = 0
local lureEnabled = true
-- ui -- ui
local configWidget = UI.Config() local configWidget = UI.Config()
@ -77,6 +78,9 @@ targetbotMacro = macro(100, function()
TargetBot.setStatus("Luring using CaveBot") TargetBot.setStatus("Luring using CaveBot")
else else
TargetBot.setStatus("Attacking") TargetBot.setStatus("Attacking")
if not lureEnabled then
TargetBot.setStatus("Attacking (luring off)")
end
end end
TargetBot.walk() TargetBot.walk()
lastAction = now lastAction = now
@ -117,6 +121,7 @@ config = Config.setup("targetbot_configs", configWidget, "json", function(name,
targetbotMacro.setOn(enabled) targetbotMacro.setOn(enabled)
targetbotMacro.delay = nil targetbotMacro.delay = nil
lureEnabled = true
end) end)
-- setup ui -- setup ui
@ -198,6 +203,15 @@ TargetBot.allowCaveBot = function(time)
cavebotAllowance = now + time cavebotAllowance = now + time
end end
TargetBot.disableLuring = function()
lureEnabled = false
end
TargetBot.enableLuring = function()
lureEnabled = true
end
-- attacks -- attacks
local lastSpell = 0 local lastSpell = 0
local lastAttackSpell = 0 local lastAttackSpell = 0
@ -265,3 +279,7 @@ TargetBot.useAttackItem = function(item, subType, target, delay)
lastRuneAttack = now lastRuneAttack = now
end end
end end
TargetBot.canLure = function()
return lureEnabled
end

View File

@ -0,0 +1,235 @@
setDefaultTab("HP")
local conditionPanelName = "ConditionPanel"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Conditions')
Button
id: conditionList
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(conditionPanelName)
if not storage[conditionPanelName] then
storage[conditionPanelName] = {
enabled = false,
curePosion = false,
poisonCost = 20,
cureCurse = false,
curseCost = 80,
cureBleed = false,
bleedCost = 45,
cureBurn = false,
burnCost = 30,
cureElectrify = false,
electrifyCost = 22,
cureParalyse = false,
paralyseCost = 40,
paralyseSpell = "utani hur",
holdHaste = false,
hasteCost = 40,
hasteSpell = "utani hur",
holdUtamo = false,
utamoCost = 40,
holdUtana = false,
utanaCost = 440,
holdUtura = false,
uturaType = "",
uturaCost = 100,
ignoreInPz = true
}
end
ui.title:setOn(storage[conditionPanelName].enabled)
ui.title.onClick = function(widget)
storage[conditionPanelName].enabled = not storage[conditionPanelName].enabled
widget:setOn(storage[conditionPanelName].enabled)
end
ui.conditionList.onClick = function(widget)
conditionsWindow:show()
conditionsWindow:raise()
conditionsWindow:focus()
end
local rootWidget = g_ui.getRootWidget()
if rootWidget then
conditionsWindow = g_ui.createWidget('ConditionsWindow', rootWidget)
conditionsWindow:hide()
-- text edits
conditionsWindow.Cure.PoisonCost:setText(storage[conditionPanelName].poisonCost)
conditionsWindow.Cure.PoisonCost.onTextChange = function(widget, text)
storage[conditionPanelName].poisonCost = tonumber(text)
end
conditionsWindow.Cure.CurseCost:setText(storage[conditionPanelName].curseCost)
conditionsWindow.Cure.CurseCost.onTextChange = function(widget, text)
storage[conditionPanelName].curseCost = tonumber(text)
end
conditionsWindow.Cure.BleedCost:setText(storage[conditionPanelName].bleedCost)
conditionsWindow.Cure.BleedCost.onTextChange = function(widget, text)
storage[conditionPanelName].bleedCost = tonumber(text)
end
conditionsWindow.Cure.BurnCost:setText(storage[conditionPanelName].burnCost)
conditionsWindow.Cure.BurnCost.onTextChange = function(widget, text)
storage[conditionPanelName].burnCost = tonumber(text)
end
conditionsWindow.Cure.ElectrifyCost:setText(storage[conditionPanelName].electrifyCost)
conditionsWindow.Cure.ElectrifyCost.onTextChange = function(widget, text)
storage[conditionPanelName].electrifyCost = tonumber(text)
end
conditionsWindow.Cure.ParalyseCost:setText(storage[conditionPanelName].paralyseCost)
conditionsWindow.Cure.ParalyseCost.onTextChange = function(widget, text)
storage[conditionPanelName].paralyseCost = tonumber(text)
end
conditionsWindow.Cure.ParalyseSpell:setText(storage[conditionPanelName].paralyseSpell)
conditionsWindow.Cure.ParalyseSpell.onTextChange = function(widget, text)
storage[conditionPanelName].paralyseSpell = text
end
conditionsWindow.Hold.HasteSpell:setText(storage[conditionPanelName].hasteSpell)
conditionsWindow.Hold.HasteSpell.onTextChange = function(widget, text)
storage[conditionPanelName].hasteSpell = text
end
conditionsWindow.Hold.HasteCost:setText(storage[conditionPanelName].hasteCost)
conditionsWindow.Hold.HasteCost.onTextChange = function(widget, text)
storage[conditionPanelName].hasteCost = tonumber(text)
end
conditionsWindow.Hold.UtamoCost:setText(storage[conditionPanelName].utamoCost)
conditionsWindow.Hold.UtamoCost.onTextChange = function(widget, text)
storage[conditionPanelName].utamoCost = tonumber(text)
end
conditionsWindow.Hold.UtanaCost:setText(storage[conditionPanelName].utanaCost)
conditionsWindow.Hold.UtanaCost.onTextChange = function(widget, text)
storage[conditionPanelName].utanaCost = tonumber(text)
end
conditionsWindow.Hold.UturaCost:setText(storage[conditionPanelName].uturaCost)
conditionsWindow.Hold.UturaCost.onTextChange = function(widget, text)
storage[conditionPanelName].uturaCost = tonumber(text)
end
-- combo box
conditionsWindow.Hold.UturaType:setOption(storage[conditionPanelName].uturaType)
conditionsWindow.Hold.UturaType.onOptionChange = function(widget)
storage[conditionPanelName].uturaType = widget:getCurrentOption().text
end
-- checkboxes
conditionsWindow.Cure.CurePoison:setChecked(storage[conditionPanelName].curePoison)
conditionsWindow.Cure.CurePoison.onClick = function(widget)
storage[conditionPanelName].curePoison = not storage[conditionPanelName].curePoison
widget:setChecked(storage[conditionPanelName].curePoison)
end
conditionsWindow.Cure.CureCurse:setChecked(storage[conditionPanelName].cureCurse)
conditionsWindow.Cure.CureCurse.onClick = function(widget)
storage[conditionPanelName].cureCurse = not storage[conditionPanelName].cureCurse
widget:setChecked(storage[conditionPanelName].cureCurse)
end
conditionsWindow.Cure.CureBleed:setChecked(storage[conditionPanelName].cureBleed)
conditionsWindow.Cure.CureBleed.onClick = function(widget)
storage[conditionPanelName].cureBleed = not storage[conditionPanelName].cureBleed
widget:setChecked(storage[conditionPanelName].cureBleed)
end
conditionsWindow.Cure.CureBurn:setChecked(storage[conditionPanelName].cureBurn)
conditionsWindow.Cure.CureBurn.onClick = function(widget)
storage[conditionPanelName].cureBurn = not storage[conditionPanelName].cureBurn
widget:setChecked(storage[conditionPanelName].cureBurn)
end
conditionsWindow.Cure.CureElectrify:setChecked(storage[conditionPanelName].cureElectrify)
conditionsWindow.Cure.CureElectrify.onClick = function(widget)
storage[conditionPanelName].cureElectrify = not storage[conditionPanelName].cureElectrify
widget:setChecked(storage[conditionPanelName].cureElectrify)
end
conditionsWindow.Cure.CureParalyse:setChecked(storage[conditionPanelName].cureParalyse)
conditionsWindow.Cure.CureParalyse.onClick = function(widget)
storage[conditionPanelName].cureParalyse = not storage[conditionPanelName].cureParalyse
widget:setChecked(storage[conditionPanelName].cureParalyse)
end
conditionsWindow.Hold.HoldHaste:setChecked(storage[conditionPanelName].holdHaste)
conditionsWindow.Hold.HoldHaste.onClick = function(widget)
storage[conditionPanelName].holdHaste = not storage[conditionPanelName].holdHaste
widget:setChecked(storage[conditionPanelName].holdHaste)
end
conditionsWindow.Hold.HoldUtamo:setChecked(storage[conditionPanelName].holdUtamo)
conditionsWindow.Hold.HoldUtamo.onClick = function(widget)
storage[conditionPanelName].holdUtamo = not storage[conditionPanelName].holdUtamo
widget:setChecked(storage[conditionPanelName].holdUtamo)
end
conditionsWindow.Hold.HoldUtana:setChecked(storage[conditionPanelName].holdUtana)
conditionsWindow.Hold.HoldUtana.onClick = function(widget)
storage[conditionPanelName].holdUtana = not storage[conditionPanelName].holdUtana
widget:setChecked(storage[conditionPanelName].holdUtana)
end
conditionsWindow.Hold.HoldUtura:setChecked(storage[conditionPanelName].holdUtura)
conditionsWindow.Hold.HoldUtura.onClick = function(widget)
storage[conditionPanelName].holdUtura = not storage[conditionPanelName].holdUtura
widget:setChecked(storage[conditionPanelName].holdUtura)
end
conditionsWindow.Hold.IgnoreInPz:setChecked(storage[conditionPanelName].ignoreInPz)
conditionsWindow.Hold.IgnoreInPz.onClick = function(widget)
storage[conditionPanelName].ignoreInPz = not storage[conditionPanelName].ignoreInPz
widget:setChecked(storage[conditionPanelName].ignoreInPz)
end
-- buttons
conditionsWindow.closeButton.onClick = function(widget)
conditionsWindow:hide()
end
end
local utanaCast = nil
macro(500, function()
if not storage[conditionPanelName].enabled or modules.game_cooldown.isGroupCooldownIconActive(2) then return end
if storage[conditionPanelName].curePoison and mana() >= storage[conditionPanelName].poisonCost and isPoisioned() then say("exana pox")
elseif storage[conditionPanelName].cureCurse and mana() >= storage[conditionPanelName].curseCost and isCursed() then say("exana mort")
elseif storage[conditionPanelName].cureBleed and mana() >= storage[conditionPanelName].bleedCost and isBleeding() then say("exana kor")
elseif storage[conditionPanelName].cureBurn and mana() >= storage[conditionPanelName].burnCost and isBurning() then say("exana flam")
elseif storage[conditionPanelName].cureElectrify and mana() >= storage[conditionPanelName].electrifyCost and isEnergized() then say("exana vis")
elseif (not storage[conditionPanelName].ignoreInPz or not isInPz()) and storage[conditionPanelName].holdUtura and mana() >= storage[conditionPanelName].uturaCost and not hasPartyBuff() then say(storage[conditionPanelName].uturaType)
elseif (not storage[conditionPanelName].ignoreInPz or not isInPz()) and storage[conditionPanelName].holdUtana and mana() >= storage[conditionPanelName].utanaCost and (not utanaCast or (now - utanaCast > 120000)) then say("utana vid") utanaCast = now
end
end)
macro(50, function()
if not storage[conditionPanelName].enabled then return end
if (not storage[conditionPanelName].ignoreInPz or not isInPz()) and storage[conditionPanelName].holdUtamo and mana() >= storage[conditionPanelName].utamoCost and not hasManaShield() then say("utamo vita")
elseif (not storage[conditionPanelName].ignoreInPz or not isInPz()) and storage[conditionPanelName].holdHaste and mana() >= storage[conditionPanelName].hasteCost and not hasHaste() and not getSpellCoolDown(storage[conditionPanelName].hasteSpell) then say(storage[conditionPanelName].hasteSpell)
elseif storage[conditionPanelName].cureParalyse and mana() >= storage[conditionPanelName].paralyseCost and isParalyzed() and not getSpellCoolDown(storage[conditionPanelName].paralyseSpell) then say(storage[conditionPanelName].paralyseSpell)
end
end)

View File

@ -0,0 +1,60 @@
-- tools tab
setDefaultTab("Tools")
-- allows to test/edit bot lua scripts ingame, you can have multiple scripts like this, just change storage.ingame_lua
UI.Button("Ingame hotkey editor", function(newText)
UI.MultilineEditorWindow(storage.ingame_hotkeys or "", {title="Hotkeys editor", description="You can add your custom hotkeys/singlehotkeys here"}, function(text)
storage.ingame_hotkeys = text
reload()
end)
end)
UI.Separator()
for _, scripts in pairs({storage.ingame_hotkeys}) do
if type(scripts) == "string" and scripts:len() > 3 then
local status, result = pcall(function()
assert(load(scripts, "ingame_editor"))()
end)
if not status then
error("Ingame edior error:\n" .. result)
end
end
end
UI.Separator()
local moneyIds = {3031, 3035} -- gold coin, platinium coin
macro(1000, "Exchange money", function()
local containers = g_game.getContainers()
for index, container in pairs(containers) do
if not container.lootContainer then -- ignore monster containers
for i, item in ipairs(container:getItems()) do
if item:getCount() == 100 then
for m, moneyId in ipairs(moneyIds) do
if item:getId() == moneyId then
return g_game.use(item)
end
end
end
end
end
end
end)
UI.Separator()
macro(60000, "Send message on trade", function()
local trade = getChannelId("advertising")
if not trade then
trade = getChannelId("trade")
end
if trade and storage.autoTradeMessage:len() > 0 then
sayChannel(trade, storage.autoTradeMessage)
end
end)
UI.TextEdit(storage.autoTradeMessage or "I'm using OTClientV8!", function(widget, text)
storage.autoTradeMessage = text
end)
UI.Separator()

View File

@ -0,0 +1,53 @@
-- Cavebot by otclient@otclient.ovh
-- visit http://bot.otclient.ovh/
local cavebotTab = "Cave"
local targetingTab = "Target"
setDefaultTab(cavebotTab)
CaveBot = {} -- global namespace
CaveBot.Extensions = {}
importStyle("/cavebot/cavebot.otui")
importStyle("/cavebot/config.otui")
importStyle("/cavebot/editor.otui")
importStyle("/cavebot/supply.otui")
dofile("/cavebot/actions.lua")
dofile("/cavebot/config.lua")
dofile("/cavebot/editor.lua")
dofile("/cavebot/example_functions.lua")
dofile("/cavebot/recorder.lua")
dofile("/cavebot/walking.lua")
-- in this section you can add extensions, check extension_template.lua
--dofile("/cavebot/extension_template.lua")
dofile("/cavebot/sell_all.lua")
dofile("/cavebot/depositor.lua")
dofile("/cavebot/buy_supplies.lua")
dofile("/cavebot/d_withdraw.lua")
dofile("/cavebot/depositer.lua")
dofile("/cavebot/supply.lua")
dofile("/cavebot/supply_check.lua")
dofile("/cavebot/travel.lua")
dofile("/cavebot/doors.lua")
dofile("/cavebot/pos_check.lua")
dofile("/cavebot/withdraw.lua")
dofile("/cavebot/inbox_withdraw.lua")
dofile("/cavebot/lure.lua")
dofile("/cavebot/bank.lua")
dofile("/cavebot/depositer.lua")
dofile("/cavebot/supply.lua")
-- main cavebot file, must be last
dofile("/cavebot/cavebot.lua")
setDefaultTab(targetingTab)
TargetBot = {} -- global namespace
importStyle("/targetbot/looting.otui")
importStyle("/targetbot/target.otui")
importStyle("/targetbot/creature_editor.otui")
dofile("/targetbot/creature.lua")
dofile("/targetbot/creature_attack.lua")
dofile("/targetbot/creature_editor.lua")
dofile("/targetbot/creature_priority.lua")
dofile("/targetbot/looting.lua")
dofile("/targetbot/walking.lua")
-- main targetbot file, must be last
dofile("/targetbot/target.lua")

View File

@ -0,0 +1,7 @@
-- main tab
UI.Label("Vithrax CFG v1.1 \n \n Scripting service: \n Vithrax#5814")
UI.Separator()

View File

@ -0,0 +1,874 @@
-- lib ver 1.4
-- Author: Vithrax
-- contains mostly basic function shortcuts and code shorteners
function isBuffed()
if (4*(player:getSkillLevel(2) - player:getSkillBaseLevel(2))) < player:getSkillLevel(2) then
return false
else
return true
end
end
function killsToRs()
return math.min(g_game.getUnjustifiedPoints().killsDayRemaining, g_game.getUnjustifiedPoints().killsWeekRemaining, g_game.getUnjustifiedPoints().killsMonthRemaining)
end
function canCast(spell)
if not spell then return end
if not getSpellData(spell) then return true end
if not getSpellCoolDown(spell) and mana() >= getSpellData(spell).manaCost and level() >= getSpellData(spell).level then
return true
else
return false
end
end
function getSpellData(spell)
if not spell then return false end
if Spells[spell] then
return Spells[spell]
else
return false
end
end
Spells = {
["adana ani"] = {level = 54, manaCost = 1400},
["adana mort"] = {level = 27, manaCost = 600},
["adana pox"] = {level = 15, manaCost = 200},
["adeta sio"] = {level = 16, manaCost = 200},
["adevo grav flam"] = {level = 15, manaCost = 240},
["adevo grav pox"] = {level = 14, manaCost = 200},
["adevo grav tera"] = {level = 32, manaCost = 750},
["adevo grav vis"] = {level = 18, manaCost = 320},
["adevo grav vita"] = {level = 27, manaCost = 600},
["adevo ina"] = {level = 27, manaCost = 600},
["adevo mas flam"] = {level = 27, manaCost = 600},
["adevo mas grav flam"] = {level = 33, manaCost = 780},
["adevo mas grav pox"] = {level = 29, manaCost = 640},
["adevo mas grav vis"] = {level = 41, manaCost = 1000},
["adevo mas hur"] = {level = 31, manaCost = 570},
["adevo mas pox"] = {level = 25, manaCost = 520},
["adevo mas vis"] = {level = 37, manaCost = 880},
["adevo res flam"] = {level = 27, manaCost = 420},
["adito grav"] = {level = 17, manaCost = 120},
["adito tera"] = {level = 21, manaCost = 200},
["adori dis min vis"] = {level = 1, manaCost = 5},
["adori flam"] = {level = 27, manaCost = 460},
["adori frigo"] = {level = 28, manaCost = 460},
["adori gran mort"] = {level = 45, manaCost = 985},
["adori mas flam"] = {level = 30, manaCost = 530},
["adori mas frigo"] = {level = 30, manaCost = 530},
["adori mas tera"] = {level = 28, manaCost = 430},
["adori mas vis"] = {level = 28, manaCost = 430},
["adori min vis"] = {level = 15, manaCost = 120},
["adori san"] = {level = 27, manaCost = 300},
["adori tera"] = {level = 24, manaCost = 350},
["adori vis"] = {level = 25, manaCost = 350},
["adura gran"] = {level = 15, manaCost = 120},
["adura vita"] = {level = 24, manaCost = 400},
["exana flam"] = {level = 30, manaCost = 30},
["exana ina"] = {level = 26, manaCost = 200},
["exana kor"] = {level = 45, manaCost = 30},
["exana mort"] = {level = 80, manaCost = 40},
["exana pox"] = {level = 10, manaCost = 30},
["exana vis"] = {level = 22, manaCost = 30},
["exani tera"] = {level = 9, manaCost = 20},
["exeta con"] = {level = 45, manaCost = 350},
["exeta res"] = {level = 20, manaCost = 40},
["exevo con"] = {level = 13, manaCost = 100},
["exevo con flam"] = {level = 25, manaCost = 290},
["exevo dis flam hur"] = {level = 1, manaCost = 5},
["exevo flam hur"] = {level = 18, manaCost = 25},
["exevo frigo hur"] = {level = 18, manaCost = 25},
["exevo gran con hur"] = {level = 150, manaCost = 1000},
["exevo gran con vis"] = {level = 150, manaCost = 1000},
["exevo gran frigo hur"] = {level = 40, manaCost = 170},
["exevo gran mas flam"] = {level = 60, manaCost = 1100},
["exevo gran mas frigo"] = {level = 60, manaCost = 1050},
["exevo gran mas tera"] = {level = 55, manaCost = 700},
["exevo gran mas vis"] = {level = 55, manaCost = 600},
["exevo gran mort"] = {level = 41, manaCost = 250},
["exevo gran vis lux"] = {level = 29, manaCost = 110},
["exevo infir con"] = {level = 1, manaCost = 10},
["exevo infir flam hur"] = {level = 1, manaCost = 8},
["exevo infir frigo hur"] = {level = 1, manaCost = 8},
["exevo mas san"] = {level = 50, manaCost = 160},
["exevo pan"] = {level = 14, manaCost = 120},
["exevo tera hur"] = {level = 38, manaCost = 210},
["exevo vis hur"] = {level = 38, manaCost = 170},
["exevo vis lux"] = {level = 23, manaCost = 40},
["exori"] = {level = 35, manaCost = 115},
["exori amp vis"] = {level = 55, manaCost = 60},
["exori con"] = {level = 23, manaCost = 25},
["exori flam"] = {level = 14, manaCost = 20},
["exori frigo"] = {level = 15, manaCost = 20},
["exori gran"] = {level = 90, manaCost = 340},
["exori gran con"] = {level = 90, manaCost = 55},
["exori gran flam"] = {level = 70, manaCost = 60},
["exori gran frigo"] = {level = 80, manaCost = 60},
["exori gran ico"] = {level = 110, manaCost = 300},
["exori gran tera"] = {level = 70, manaCost = 60},
["exori gran vis"] = {level = 80, manaCost = 60},
["exori hur"] = {level = 28, manaCost = 40},
["exori ico"] = {level = 16, manaCost = 30},
["exori infir tera"] = {level = 1, manaCost = 6},
["exori infir vis"] = {level = 1, manaCost = 6},
["exori mas"] = {level = 33, manaCost = 160},
["exori max flam"] = {level = 90, manaCost = 100},
["exori max frigo"] = {level = 100, manaCost = 100},
["exori max tera"] = {level = 90, manaCost = 100},
["exori max vis"] = {level = 100, manaCost = 100},
["exori min"] = {level = 70, manaCost = 200},
["exori min flam"] = {level = 8, manaCost = 6},
["exori moe ico"] = {level = 16, manaCost = 20},
["exori mort"] = {level = 16, manaCost = 20},
["exori san"] = {level = 40, manaCost = 20},
["exori tera"] = {level = 13, manaCost = 20},
["exori vis"] = {level = 12, manaCost = 20},
["exura"] = {level = 8, manaCost = 20},
["exura dis"] = {level = 1, manaCost = 5},
["exura gran"] = {level = 20, manaCost = 70},
["exura gran ico"] = {level = 80, manaCost = 200},
["exura gran mas res"] = {level = 36, manaCost = 150},
["exura gran san"] = {level = 60, manaCost = 210},
["exura ico"] = {level = 8, manaCost = 40},
["exura infir"] = {level = 1, manaCost = 6},
["exura infir ico"] = {level = 1, manaCost = 10},
["exura san"] = {level = 35, manaCost = 160},
["exura vita"] = {level = 30, manaCost = 160},
["utamo mas sio"] = {level = 32, manaCost = 0},
["utamo tempo"] = {level = 55, manaCost = 200},
["utamo tempo san"] = {level = 55, manaCost = 400},
["utamo vita"] = {level = 14, manaCost = 50},
["utana vid"] = {level = 35, manaCost = 440},
["utani gran hur"] = {level = 20, manaCost = 100},
["utani hur"] = {level = 14, manaCost = 60},
["utani tempo hur"] = {level = 25, manaCost = 100},
["utevo gran lux"] = {level = 13, manaCost = 60},
["utevo gran res dru"] = {level = 200, manaCost = 3000},
["utevo gran res eq"] = {level = 200, manaCost = 1000},
["utevo gran res sac"] = {level = 200, manaCost = 2000},
["utevo gran res ven"] = {level = 200, manaCost = 3000},
["utevo lux"] = {level = 8, manaCost = 20},
["utevo vis lux"] = {level = 26, manaCost = 140},
["utito mas sio"] = {level = 32, manaCost = 0},
["utito tempo"] = {level = 60, manaCost = 290},
["utito tempo san"] = {level = 60, manaCost = 450},
["utori flam"] = {level = 26, manaCost = 30},
["utori kor"] = {level = 40, manaCost = 30},
["utori mas sio"] = {level = 32, manaCost = 0},
["utori mort"] = {level = 75, manaCost = 30},
["utori pox"] = {level = 50, manaCost = 30},
["utori san"] = {level = 70, manaCost = 30},
["utori vis"] = {level = 34, manaCost = 30},
["utura"] = {level = 50, manaCost = 75},
["utura gran"] = {level = 100, manaCost = 165},
["utura mas sio"] = {level = 32, manaCost = 0}
}
function getSpellCoolDown(text)
if not text then return false end
if text:lower() == "exura" then
return modules.game_cooldown.isCooldownIconActive(1)
elseif text:lower() == "exura gran" then
return modules.game_cooldown.isCooldownIconActive(2)
elseif text:lower() == "exura vita" then
return modules.game_cooldown.isCooldownIconActive(3)
elseif text:lower() == "exura gran mas res" then
return modules.game_cooldown.isCooldownIconActive(82)
elseif string.find(text:lower(), "exura sio") then
return modules.game_cooldown.isCooldownIconActive(84)
elseif string.find(text:lower(), "exiva") then
return modules.game_cooldown.isCooldownIconActive(20)
elseif string.find(text:lower(), "exani hur") then
return modules.game_cooldown.isCooldownIconActive(81)
elseif string.find(text:lower(), "utevo res ina") then
return modules.game_cooldown.isCooldownIconActive(38)
elseif string.find(text:lower(), 'utevo res "' ) then
return modules.game_cooldown.isCooldownIconActive(9)
elseif text:lower() == "exana pox" then
return modules.game_cooldown.isCooldownIconActive(29)
elseif text:lower() == "utevo lux" then
return modules.game_cooldown.isCooldownIconActive(10)
elseif text:lower() == "exani tera" then
return modules.game_cooldown.isCooldownIconActive(76)
elseif text:lower() == "exori vis" then
return modules.game_cooldown.isCooldownIconActive(88)
elseif text:lower() == "utevo gran lux" then
return modules.game_cooldown.isCooldownIconActive(11)
elseif text:lower() == "utani hur" then
return modules.game_cooldown.isCooldownIconActive(6)
elseif text:lower() == "exori tera" then
return modules.game_cooldown.isCooldownIconActive(113)
elseif text:lower() == "exevo pan" then
return modules.game_cooldown.isCooldownIconActive(42)
elseif text:lower() == "utamo vita" then
return modules.game_cooldown.isCooldownIconActive(44)
elseif text:lower() == "exori flam" then
return modules.game_cooldown.isCooldownIconActive(89)
elseif text:lower() == "exori frigo" then
return modules.game_cooldown.isCooldownIconActive(112)
elseif text:lower() == "exori moe ico" then
return modules.game_cooldown.isCooldownIconActive(148)
elseif text:lower() == "exevo frigo hur" then
return modules.game_cooldown.isCooldownIconActive(121)
elseif text:lower() == "utani gran hur" then
return modules.game_cooldown.isCooldownIconActive(39)
elseif text:lower() == "exana vis" then
return modules.game_cooldown.isCooldownIconActive(146)
elseif text:lower() == "utevo vis lux" then
return modules.game_cooldown.isCooldownIconActive(75)
elseif text:lower() == "exana flam" then
return modules.game_cooldown.isCooldownIconActive(145)
elseif text:lower() == "utana vid" then
return modules.game_cooldown.isCooldownIconActive(45)
elseif text:lower() == "exevo tera hur" then
return modules.game_cooldown.isCooldownIconActive(120)
elseif text:lower() == "exevo gran frigo hur" then
return modules.game_cooldown.isCooldownIconActive(43)
elseif text:lower() == "exana kor" then
return modules.game_cooldown.isCooldownIconActive(144)
elseif text:lower() == "utori pox" then
return modules.game_cooldown.isCooldownIconActive(142)
elseif text:lower() == "exevo gran mas tera" then
return modules.game_cooldown.isCooldownIconActive(56)
elseif text:lower() == "exevo gran mas frigo" then
return modules.game_cooldown.isCooldownIconActive(118)
elseif text:lower() == "exevo gran mas tera" then
return modules.game_cooldown.isCooldownIconActive(56)
elseif text:lower() == "exori gran tera" then
return modules.game_cooldown.isCooldownIconActive(153)
elseif text:lower() == "exori max tera" then
return modules.game_cooldown.isCooldownIconActive(157)
elseif text:lower() == "exori gran frigo" then
return modules.game_cooldown.isCooldownIconActive(152)
elseif text:lower() == "exori max frigo" then
return modules.game_cooldown.isCooldownIconActive(156)
elseif text:lower() == "exori max tera" then
return modules.game_cooldown.isCooldownIconActive(157)
elseif text:lower() == "exori con" then
return modules.game_cooldown.isCooldownIconActive(111)
elseif text:lower() == "exura san" then
return modules.game_cooldown.isCooldownIconActive(125)
elseif text:lower() == "exevo mas san" then
return modules.game_cooldown.isCooldownIconActive(124)
elseif text:lower() == "utura" then
return modules.game_cooldown.isCooldownIconActive(159)
elseif text:lower() == "utura gran" then
return modules.game_cooldown.isCooldownIconActive(160)
elseif text:lower() == "utamo tempo san" then
return modules.game_cooldown.isCooldownIconActive(134)
elseif text:lower() == "utito tempo san" then
return modules.game_cooldown.isCooldownIconActive(135)
elseif text:lower() == "exura gran san" then
return modules.game_cooldown.isCooldownIconActive(36)
elseif text:lower() == "utori san" then
return modules.game_cooldown.isCooldownIconActive(143)
elseif text:lower() == "exana mort" then
return modules.game_cooldown.isCooldownIconActive(147)
elseif text:lower() == "exori gran con" then
return modules.game_cooldown.isCooldownIconActive(57)
elseif text:lower() == "exura ico" then
return modules.game_cooldown.isCooldownIconActive(123)
elseif text:lower() == "exeta res" then
return modules.game_cooldown.isCooldownIconActive(93)
elseif text:lower() == "utani tempo hur" then
return modules.game_cooldown.isCooldownIconActive(131)
elseif text:lower() == "utamo tempo" then
return modules.game_cooldown.isCooldownIconActive(132)
elseif text:lower() == "utito tempo" then
return modules.game_cooldown.isCooldownIconActive(133)
elseif text:lower() == "exura gran ico" then
return modules.game_cooldown.isCooldownIconActive(158)
elseif text:lower() == "exori hur" then
return modules.game_cooldown.isCooldownIconActive(107)
elseif text:lower() == "exori ico" then
return modules.game_cooldown.isCooldownIconActive(61)
elseif text:lower() == "exori" then
return modules.game_cooldown.isCooldownIconActive(80)
elseif text:lower() == "exori mas" then
return modules.game_cooldown.isCooldownIconActive(106)
elseif text:lower() == "exori gran" then
return modules.game_cooldown.isCooldownIconActive(105)
elseif text:lower() == "exori gran ico" then
return modules.game_cooldown.isCooldownIconActive(62)
elseif text:lower() == "exori min" then
return modules.game_cooldown.isCooldownIconActive(59)
elseif text:lower() == "exevo gran mas flam" then
return modules.game_cooldown.isCooldownIconActive(24)
elseif text:lower() == "exevo gran mas vis" then
return modules.game_cooldown.isCooldownIconActive(119)
elseif text:lower() == "exevo vis hur" then
return modules.game_cooldown.isCooldownIconActive(13)
elseif text:lower() == "exevo vis lux" then
return modules.game_cooldown.isCooldownIconActive(22)
elseif text:lower() == "exevo gran vis lux" then
return modules.game_cooldown.isCooldownIconActive(23)
elseif text:lower() == "exori amp vis" then
return modules.game_cooldown.isCooldownIconActive(149)
elseif text:lower() == "exori gran vis" then
return modules.game_cooldown.isCooldownIconActive(151)
elseif text:lower() == "exori gran flam" then
return modules.game_cooldown.isCooldownIconActive(150)
elseif text:lower() == "exori max vis" then
return modules.game_cooldown.isCooldownIconActive(155)
elseif text:lower() == "exori max flam" then
return modules.game_cooldown.isCooldownIconActive(154)
elseif text:lower() == "exevo gran flam hur" then
return modules.game_cooldown.isCooldownIconActive(150)
else
return false
end
end
storage.isUsing = false
onUse(function(pos, itemId, stackPos, subType)
if pos.x < 65000 then
storage.isUsing = true
end
schedule(1500, function() storage.isUsing = false end)
end)
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function isFriend(name)
if not name then return false end
if getCreatureByName(name, true):isPlayer() and not getCreatureByName(name, true):isLocalPlayer() and table.find(storage.playerList.friendList, name) or string.find(storage.serverMembers, name) or table.find(storage.playerList.friendList, name:lower()) or (storage.playerList.groupMembers and ((getCreatureByName(name, true):getShield() >= 3 and getCreatureByName(name, true):getShield() <= 10) or getCreatureByName(name, true):getEmblem() == 2)) then
return true
else
return false
end
end
function isEnemy(name)
if not name then return false end
if getCreatureByName(name, true):isPlayer() and not getCreatureByName(name, true):isLocalPlayer() and table.find(storage.playerList.enemyList, name) or table.find(storage.playerList.enemyList, name:lower()) or (storage.playerList.marks and not isFriend(name)) then
return true
else
return false
end
end
function isAttSpell(expr)
if string.starts(expr, "exori") or string.starts(expr, "exevo") then
return true
else
return false
end
end
function getPlayerByName(name)
if not name then
return false
end
local creature
for i, spec in pairs(getSpectators()) do
if spec:isPlayer() and spec:getName():lower() == name:lower() then
creature = spec
end
end
if creature then
return creature
end
end
function getActiveItemId(id)
if not id then
return false
end
if id == 3049 then
return 3086
elseif id == 3050 then
return 3087
elseif id == 3051 then
return 3088
elseif id == 3052 then
return 3089
elseif id == 3053 then
return 3090
elseif id == 3091 then
return 3094
elseif id == 3092 then
return 3095
elseif id == 3093 then
return 3096
elseif id == 3097 then
return 3099
elseif id == 3098 then
return 3100
elseif id == 16114 then
return 16264
elseif id == 23531 then
return 23532
elseif id == 23533 then
return 23534
elseif id == 23529 then
return 23530
else
return id
end
end
function getInactiveItemId(id)
if not id then
return false
end
if id == 3086 then
return 3049
elseif id == 3087 then
return 3050
elseif id == 3088 then
return 3051
elseif id == 3089 then
return 3052
elseif id == 3090 then
return 3053
elseif id == 3094 then
return 3091
elseif id == 3095 then
return 3092
elseif id == 3096 then
return 3093
elseif id == 3099 then
return 3097
elseif id == 3100 then
return 3098
elseif id == 16264 then
return 16114
elseif id == 23532 then
return 23531
elseif id == 23534 then
return 23533
elseif id == 23530 then
return 23529
else
return id
end
end
function getMonstersInRange(pos, range)
if not pos or not range then
return false
end
local monsters = 0
for i, spec in pairs(getSpectators()) do
if spec:isMonster() and spec:getType() ~= 3 and getDistanceBetween(pos, spec:getPosition()) < range then
monsters = monsters + 1
end
end
return monsters
end
function distanceFromPlayer(coords)
if not coords then
return false
end
return getDistanceBetween(pos(), coords)
end
function getMonsters(range, multifloor)
if not range then
range = 10
end
local mobs = 0;
for _, spec in pairs(getSpectators(multifloor)) do
mobs = spec:getType() ~= 3 and spec:isMonster() and distanceFromPlayer(spec:getPosition()) <= range and mobs + 1 or mobs;
end
return mobs;
end
function getPlayers(range, multifloor)
if not range then
range = 10
end
local specs = 0;
for _, spec in pairs(getSpectators(multifloor)) do
specs = not spec:isLocalPlayer() and spec:isPlayer() and distanceFromPlayer(spec:getPosition()) <= range and not ((spec:getShield() >= 3 and spec:getShield() <= 10) or spec:getEmblem() == 1) and specs + 1 or specs;
end
return specs;
end
function isSafe(range, multifloor, padding)
local onSame = 0
local onAnother = 0
if not multifloor and padding then
multifloor = false
padding = false
end
for _, spec in pairs(getSpectators(multifloor)) do
if spec:isPlayer() and not spec:isLocalPlayer() and not isFriend(spec:getName()) then
if spec:getPosition().z == posz() and distanceFromPlayer(spec:getPosition()) <= range then
onSame = onSame + 1
end
if multifloor and padding and spec:getPosition().z ~= posz() and distanceFromPlayer(spec:getPosition()) <= (range + padding) then
onAnother = onAnother + 1
end
end
end
if onSame + onAnother > 0 then
return false
else
return true
end
end
function getAllPlayers(range, multifloor)
if not range then
range = 10
end
local specs = 0;
for _, spec in pairs(g_map.getSpectators(multifloor)) do
specs = not spec:isLocalPlayer() and spec:isPlayer() and distanceFromPlayer(spec:getPosition()) <= range and specs + 1 or specs;
end
return specs;
end
function getNpcs(range, multifloor)
if not range then
range = 10
end
local npcs = 0;
for _, spec in pairs(g_map.getSpectators(multifloor)) do
npcs = spec:isNpc() and distanceFromPlayer(spec:getPosition()) <= range and npcs + 1 or npcs;
end
return npcs;
end
function itemAmount(id)
local totalItemCount = 0
for _, container in pairs(getContainers()) do
for _, item in ipairs(container:getItems()) do
totalItemCount = item:getId() == id and totalItemCount + item:getCount() or totalItemCount
end
end
if getHead() and getHead():getId() == id then
totalItemCount = totalItemCount + getHead():getCount()
end
if getNeck() and getNeck():getId() == id then
totalItemCount = totalItemCount + getNeck():getCount()
end
if getBack() and getBack():getId() == id then
totalItemCount = totalItemCount + getBack():getCount()
end
if getBody() and getBody():getId() == id then
totalItemCount = totalItemCount + getBody():getCount()
end
if getRight() and getRight():getId() == id then
totalItemCount = totalItemCount + getRight():getCount()
end
if getLeft() and getLeft():getId() == id then
totalItemCount = totalItemCount + getLeft():getCount()
end
if getLeg() and getLeg():getId() == id then
totalItemCount = totalItemCount + getLeg():getCount()
end
if getFeet() and getFeet():getId() == id then
totalItemCount = totalItemCount + getFeet():getCount()
end
if getFinger() and getFinger():getId() == id then
totalItemCount = totalItemCount + getFinger():getCount()
end
if getAmmo() and getAmmo():getId() == id then
totalItemCount = totalItemCount + getAmmo():getCount()
end
return totalItemCount
end
function cordsToPos(x, y, z)
if not x or not y or not z then
return false
end
local tilePos = pos()
tilePos.x = x
tilePos.y = y
tilePos.z = z
return tilePos
end
function reachGroundItem(id)
local targetTile
for _, tile in ipairs(g_map.getTiles(posz())) do
if tile:getTopUseThing():getId() == id then
targetTile = tile:getPosition()
end
end
if distanceFromPlayer(targetTile) > 1 then
if autoWalk(targetTile, 10, {ignoreNonPathable = true, precision=1}) then
delay(200)
end
else
return true
end
end
function useGroundItem(id)
if not id then
return false
end
local targetTile = nil
for _, tile in ipairs(g_map.getTiles(posz())) do
if tile:getTopUseThing():getId() == id then
targetTile = tile:getPosition()
end
end
if targetTile then
if distanceFromPlayer(targetTile) > 1 then
if autoWalk(targetTile, 20, {ignoreNonWalkable = true, ignoreNonPathable = true, precision=1}) then
delay(200)
end
else
g_game.use(g_map.getTile(targetTile):getTopUseThing())
return true
end
else
return "retry"
end
end
function target()
if not g_game.isAttacking() then
return
else
return g_game.getAttackingCreature()
end
end
function getTarget()
return target()
end
function targetPos(dist)
if not g_game.isAttacking() then
return
end
if dist then
return distanceFromPlayer(target():getPosition())
else
return target():getPosition()
end
end
-- for gunzodus
function reopenPurse()
schedule(100, function() g_game.open(findItem(23721)) return true end)
schedule(1400, function() g_game.open(findItem(23721)) return true end)
CaveBot.delay(1500)
return true
end
-- getSpectator patterns
function getCreaturesInArea(param1, param2, param3)
-- param1 - pos/creature
-- param2 - pattern
-- param3 - type of return
-- 1 - everyone, 2 - monsters, 3 - players
local specs = 0
local monsters = 0
local players = 0
for i, spec in pairs(getSpectators(param1, param2)) do
if spec ~= player then
specs = specs + 1
if spec:isMonster() then
monsters = monsters + 1
elseif spec:isPlayer() and not isFriend(spec:getName()) then
players = players +1
end
end
end
if param3 == 1 then
return specs
elseif param3 == 2 then
return monsters
else
return players
end
end
function getBestTileByPatern(pattern, specType, maxDist, safe)
if not pattern or not specType then return end
if not maxDist then maxDist = 4 end
if not safe then safe = false end
local fieldList = {}
local bestTile = nil
-- best area tile to use
for _, tile in pairs(g_map.getTiles(posz())) do
if tile:canShoot() and distanceFromPlayer(tile:getPosition()) <= maxDist and tile:isWalkable() and getCreaturesInArea(tile:getPosition(), pattern, specType) > 0 and (not safe or getCreaturesInArea(tile:getPosition(), pattern, 3) == 0) then
table.insert(fieldList, {pos = tile, count = getCreaturesInArea(tile:getPosition(), pattern, specType)})
end
end
table.sort(fieldList, function(a,b) return a.count > b.count end)
bestTile = fieldList[1]
if bestTile then
return bestTile
else
return false
end
end
LargeUeArea = [[
0000001000000
0000011100000
0000111110000
0001111111000
0011111111100
0111111111110
1111111111111
0111111111110
0011111111100
0001111111000
0000111110000
0000011100000
0000001000000
]]
NormalUeAreaMs = [[
00000100000
00011111000
00111111100
01111111110
01111111110
11111111111
01111111110
01111111110
00111111100
00001110000
00000100000
]]
NormalUeAreaEd = [[
00000100000
00001110000
00011111000
00111111100
01111111110
11111111111
01111111110
00111111100
00011111000
00001110000
00000100000
]]
smallUeArea = [[
0011100
0111110
1111111
1111111
1111111
0111110
0011100
]]
largeRuneArea = [[
0011100
0111110
1111111
1111111
1111111
0111110
0011100
]]
adjacentArea = [[
111
101
111
]]
longBeamArea = [[
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
WWWWWWW0EEEEEEE
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
]]
shortBeamArea = [[
00000100000
00000100000
00000100000
00000100000
00000100000
EEEEE0WWWWW
00000S00000
00000S00000
00000S00000
00000S00000
00000S00000
]]
newWaveArea = [[
000NNNNN000
000NNNNN000
0000NNN0000
WW00NNN00EE
WWWW0N0EEEE
WWWWW0EEEEE
WWWW0S0EEEE
WW00SSS00EE
0000SSS0000
000SSSSS000
000SSSSS000
]]
bigWaveArea = [[
0000NNN0000
0000NNN0000
0000NNN0000
00000N00000
WWW00N00EEE
WWWWW0EEEEE
WWW00S00EEE
00000S00000
0000SSS0000
0000SSS0000
0000SSS0000
]]
smallWaveArea = [[
00NNN00
00NNN00
WW0N0EE
WWW0EEE
WW0S0EE
00SSS00
00SSS00
]]
diamondArrowArea = [[
01110
11111
11111
11111
01110
]]

View File

@ -0,0 +1,147 @@
setDefaultTab("Main")
BotPanelName = "BOTserver"
local ui = setupUI([[
Panel
height: 18
Button
id: botServer
anchors.left: parent.left
anchors.right: parent.right
text-align: center
height: 18
!text: tr('BotServer')
]])
ui:setId(BotPanelName)
if not storage[BotPanelName] then
storage[BotPanelName] = {
manaInfo = true,
mwallInfo = true
}
end
if not storage.BotServerChannel then
storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999))
end
local channel = tostring(storage.BotServerChannel)
BotServer.init(name(), channel)
rootWidget = g_ui.getRootWidget()
if rootWidget then
botServerWindow = g_ui.createWidget('BotServerWindow', rootWidget)
botServerWindow:hide()
botServerWindow.Data.Channel:setText(storage.BotServerChannel)
botServerWindow.Data.Channel.onTextChange = function(widget, text)
storage.BotServerChannel = text
end
botServerWindow.Data.Random.onClick = function(widget)
storage.BotServerChannel = tostring(math.random(1000000000000,9999999999999))
botServerWindow.Data.Channel:setText(storage.BotServerChannel)
end
botServerWindow.Features.Feature1:setOn(storage[BotPanelName].manaInfo)
botServerWindow.Features.Feature1.onClick = function(widget)
storage[BotPanelName].manaInfo = not storage[BotPanelName].manaInfo
widget:setOn(storage[BotPanelName].manaInfo)
end
botServerWindow.Features.Feature2:setOn(storage[BotPanelName].mwallInfo)
botServerWindow.Features.Feature2.onClick = function(widget)
storage[BotPanelName].mwallInfo = not storage[BotPanelName].mwallInfo
widget:setOn(storage[BotPanelName].mwallInfo)
end
end
function updateStatusText()
if BotServer._websocket then
botServerWindow.Data.ServerStatus:setText("CONNECTED")
if serverCount then
botServerWindow.Data.Participants:setText(#serverCount)
end
else
botServerWindow.Data.ServerStatus:setText("DISCONNECTED")
botServerWindow.Data.Participants:setText("-")
end
end
macro(2000, function()
if BotServer._websocket then
BotServer.send("list")
end
updateStatusText()
end)
local regex = [["(.*?)"]]
BotServer.listen("list", function(name, data)
serverCount = regexMatch(json.encode(data), regex)
storage.serverMembers = json.encode(data)
end)
ui.botServer.onClick = function(widget)
botServerWindow:show()
botServerWindow:raise()
botServerWindow:focus()
end
botServerWindow.closeButton.onClick = function(widget)
botServerWindow:hide()
end
-- scripts
storage[BotPanelName].mwalls = {}
BotServer.listen("mwall", function(name, message)
if storage[BotPanelName].mwallInfo then
if not storage[BotPanelName].mwalls[message["pos"]] or storage[BotPanelName].mwalls[message["pos"]] < now then
storage[BotPanelName].mwalls[message["pos"]] = now + message["duration"] - 150 -- 150 is latency correction
end
end
end)
BotServer.listen("mana", function(name, message)
if storage[BotPanelName].manaInfo then
local creature = getPlayerByName(name)
if creature then
creature:setManaPercent(message["mana"])
end
end
end)
onAddThing(function(tile, thing)
if storage[BotPanelName].mwallInfo then
if thing:isItem() and thing:getId() == 2129 then
local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z
if not storage[BotPanelName].mwalls[pos] or storage[BotPanelName].mwalls[pos] < now then
storage[BotPanelName].mwalls[pos] = now + 20000
BotServer.send("mwall", {pos=pos, duration=20000})
end
tile:setTimer(storage[BotPanelName].mwalls[pos] - now)
end
end
end)
local lastMana = 0
macro(100, function()
if storage[BotPanelName].manaInfo then
if manapercent() ~= lastMana then
lastMana = manapercent()
BotServer.send("mana", {mana=lastMana})
end
end
end)
addSeparator()

View File

@ -0,0 +1,178 @@
alarmsPanelName = "alarms"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Alarms')
Button
id: alerts
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Edit
]])
ui:setId(alarmsPanelName)
if not storage[alarmsPanelName] then
storage[alarmsPanelName] = {
enabled = false,
playerAttack = false,
playerDetected = false,
playerDetectedLogout = false,
creatureDetected = false,
healthBelow = false,
healthValue = 40,
manaBelow = false,
manaValue = 50,
privateMessage = false
}
end
ui.title:setOn(storage[alarmsPanelName].enabled)
ui.title.onClick = function(widget)
storage[alarmsPanelName].enabled = not storage[alarmsPanelName].enabled
widget:setOn(storage[alarmsPanelName].enabled)
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
alarmsWindow = g_ui.createWidget('AlarmsWindow', rootWidget)
alarmsWindow:hide()
alarmsWindow.closeButton.onClick = function(widget)
alarmsWindow:hide()
end
alarmsWindow.playerAttack:setOn(storage[alarmsPanelName].playerAttack)
alarmsWindow.playerAttack.onClick = function(widget)
storage[alarmsPanelName].playerAttack = not storage[alarmsPanelName].playerAttack
widget:setOn(storage[alarmsPanelName].playerAttack)
end
alarmsWindow.playerDetected:setOn(storage[alarmsPanelName].playerDetected)
alarmsWindow.playerDetected.onClick = function(widget)
storage[alarmsPanelName].playerDetected = not storage[alarmsPanelName].playerDetected
widget:setOn(storage[alarmsPanelName].playerDetected)
end
alarmsWindow.playerDetectedLogout:setChecked(storage[alarmsPanelName].playerDetectedLogout)
alarmsWindow.playerDetectedLogout.onClick = function(widget)
storage[alarmsPanelName].playerDetectedLogout = not storage[alarmsPanelName].playerDetectedLogout
widget:setChecked(storage[alarmsPanelName].playerDetectedLogout)
end
alarmsWindow.creatureDetected:setOn(storage[alarmsPanelName].creatureDetected)
alarmsWindow.creatureDetected.onClick = function(widget)
storage[alarmsPanelName].creatureDetected = not storage[alarmsPanelName].creatureDetected
widget:setOn(storage[alarmsPanelName].creatureDetected)
end
alarmsWindow.healthBelow:setOn(storage[alarmsPanelName].healthBelow)
alarmsWindow.healthBelow.onClick = function(widget)
storage[alarmsPanelName].healthBelow = not storage[alarmsPanelName].healthBelow
widget:setOn(storage[alarmsPanelName].healthBelow)
end
alarmsWindow.healthValue.onValueChange = function(scroll, value)
storage[alarmsPanelName].healthValue = value
alarmsWindow.healthBelow:setText("Health < " .. storage[alarmsPanelName].healthValue .. "%")
end
alarmsWindow.healthValue:setValue(storage[alarmsPanelName].healthValue)
alarmsWindow.manaBelow:setOn(storage[alarmsPanelName].manaBelow)
alarmsWindow.manaBelow.onClick = function(widget)
storage[alarmsPanelName].manaBelow = not storage[alarmsPanelName].manaBelow
widget:setOn(storage[alarmsPanelName].manaBelow)
end
alarmsWindow.manaValue.onValueChange = function(scroll, value)
storage[alarmsPanelName].manaValue = value
alarmsWindow.manaBelow:setText("Mana < " .. storage[alarmsPanelName].manaValue .. "%")
end
alarmsWindow.manaValue:setValue(storage[alarmsPanelName].manaValue)
alarmsWindow.privateMessage:setOn(storage[alarmsPanelName].privateMessage)
alarmsWindow.privateMessage.onClick = function(widget)
storage[alarmsPanelName].privateMessage = not storage[alarmsPanelName].privateMessage
widget:setOn(storage[alarmsPanelName].privateMessage)
end
onTextMessage(function(mode, text)
if storage[alarmsPanelName].enabled and storage[alarmsPanelName].playerAttack and mode == 16 and string.match(text, "hitpoints due to an attack") and not string.match(text, "hitpoints due to an attack by a ") then
playSound("/sounds/Player_Attack.ogg")
end
end)
macro(100, function()
if not storage[alarmsPanelName].enabled then
return
end
if storage[alarmsPanelName].playerDetected then
for _, spec in ipairs(getSpectators()) do
if spec:isPlayer() and spec:getName() ~= name() then
specPos = spec:getPosition()
if math.max(math.abs(posx()-specPos.x), math.abs(posy()-specPos.y)) <= 8 then
playSound("/sounds/Player_Detected.ogg")
delay(1500)
if storage[alarmsPanelName].playerDetectedLogout then
modules.game_interface.tryLogout(false)
end
return
end
end
end
end
if storage[alarmsPanelName].creatureDetected then
for _, spec in ipairs(getSpectators()) do
if not spec:isPlayer()then
specPos = spec:getPosition()
if math.max(math.abs(posx()-specPos.x), math.abs(posy()-specPos.y)) <= 8 then
playSound("/sounds/Creature_Detected.ogg")
delay(1500)
return
end
end
end
end
if storage[alarmsPanelName].healthBelow then
if hppercent() <= storage[alarmsPanelName].healthValue then
playSound("/sounds/Low_Health.ogg")
delay(1500)
return
end
end
if storage[alarmsPanelName].manaBelow then
if manapercent() <= storage[alarmsPanelName].manaValue then
playSound("/sounds/Low_Mana.ogg")
delay(1500)
return
end
end
end)
onTalk(function(name, level, mode, text, channelId, pos)
if mode == 4 and storage[alarmsPanelName].enabled and storage[alarmsPanelName].privateMessage then
playSound("/sounds/Private_Message.ogg")
return
end
end)
end
ui.alerts.onClick = function(widget)
alarmsWindow:show()
alarmsWindow:raise()
alarmsWindow:focus()
end

View File

@ -0,0 +1,440 @@
ComboPanelName = "combobot"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('ComboBot')
Button
id: combos
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(ComboPanelName)
if not storage[ComboPanelName] then
storage[ComboPanelName] = {
enabled = false,
onSayEnabled = false,
onShootEnabled = false,
onCastEnabled = false,
followLeaderEnabled = false,
attackLeaderTargetEnabled = false,
attackSpellEnabled = false,
attackItemToggle = false,
sayLeader = "",
shootLeader = "",
castLeader = "",
sayPhrase = "",
spell = "",
serverLeader = "",
item = 3155,
attack = "",
follow = "",
commandsEnabled = true,
serverEnabled = false,
serverLeaderTarget = false,
serverTriggers = true
}
end
ui.title:setOn(storage[ComboPanelName].enabled)
ui.title.onClick = function(widget)
storage[ComboPanelName].enabled = not storage[ComboPanelName].enabled
widget:setOn(storage[ComboPanelName].enabled)
end
ui.combos.onClick = function(widget)
comboWindow:show()
comboWindow:raise()
comboWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
comboWindow = g_ui.createWidget('ComboWindow', rootWidget)
comboWindow:hide()
-- bot item
comboWindow.actions.attackItem:setItemId(storage[ComboPanelName].item)
comboWindow.actions.attackItem.onItemChange = function(widget)
storage[ComboPanelName].item = widget:getItemId()
end
-- switches
comboWindow.actions.commandsToggle:setOn(storage[ComboPanelName].commandsEnabled)
comboWindow.actions.commandsToggle.onClick = function(widget)
storage[ComboPanelName].commandsEnabled = not storage[ComboPanelName].commandsEnabled
widget:setOn(storage[ComboPanelName].commandsEnabled)
end
comboWindow.server.botServerToggle:setOn(storage[ComboPanelName].serverEnabled)
comboWindow.server.botServerToggle.onClick = function(widget)
storage[ComboPanelName].serverEnabled = not storage[ComboPanelName].serverEnabled
widget:setOn(storage[ComboPanelName].serverEnabled)
end
comboWindow.server.Triggers:setOn(storage[ComboPanelName].serverTriggers)
comboWindow.server.Triggers.onClick = function(widget)
storage[ComboPanelName].serverTriggers = not storage[ComboPanelName].serverTriggers
widget:setOn(storage[ComboPanelName].serverTriggers)
end
comboWindow.server.targetServerLeaderToggle:setOn(storage[ComboPanelName].serverLeaderTarget)
comboWindow.server.targetServerLeaderToggle.onClick = function(widget)
storage[ComboPanelName].serverLeaderTarget = not storage[ComboPanelName].serverLeaderTarget
widget:setOn(storage[ComboPanelName].serverLeaderTarget)
end
-- buttons
comboWindow.closeButton.onClick = function(widget)
comboWindow:hide()
end
-- combo boxes
comboWindow.actions.followLeader:setOption(storage[ComboPanelName].follow)
comboWindow.actions.followLeader.onOptionChange = function(widget)
storage[ComboPanelName].follow = widget:getCurrentOption().text
end
comboWindow.actions.attackLeaderTarget:setOption(storage[ComboPanelName].attack)
comboWindow.actions.attackLeaderTarget.onOptionChange = function(widget)
storage[ComboPanelName].attack = widget:getCurrentOption().text
end
-- checkboxes
comboWindow.trigger.onSayToggle:setChecked(storage[ComboPanelName].onSayEnabled)
comboWindow.trigger.onSayToggle.onClick = function(widget)
storage[ComboPanelName].onSayEnabled = not storage[ComboPanelName].onSayEnabled
widget:setChecked(storage[ComboPanelName].onSayEnabled)
end
comboWindow.trigger.onShootToggle:setChecked(storage[ComboPanelName].onShootEnabled)
comboWindow.trigger.onShootToggle.onClick = function(widget)
storage[ComboPanelName].onShootEnabled = not storage[ComboPanelName].onShootEnabled
widget:setChecked(storage[ComboPanelName].onShootEnabled)
end
comboWindow.trigger.onCastToggle:setChecked(storage[ComboPanelName].onCastEnabled)
comboWindow.trigger.onCastToggle.onClick = function(widget)
storage[ComboPanelName].onCastEnabled = not storage[ComboPanelName].onCastEnabled
widget:setChecked(storage[ComboPanelName].onCastEnabled)
end
comboWindow.actions.followLeaderToggle:setChecked(storage[ComboPanelName].followLeaderEnabled)
comboWindow.actions.followLeaderToggle.onClick = function(widget)
storage[ComboPanelName].followLeaderEnabled = not storage[ComboPanelName].followLeaderEnabled
widget:setChecked(storage[ComboPanelName].followLeaderEnabled)
end
comboWindow.actions.attackLeaderTargetToggle:setChecked(storage[ComboPanelName].attackLeaderTargetEnabled)
comboWindow.actions.attackLeaderTargetToggle.onClick = function(widget)
storage[ComboPanelName].attackLeaderTargetEnabled = not storage[ComboPanelName].attackLeaderTargetEnabled
widget:setChecked(storage[ComboPanelName].attackLeaderTargetEnabled)
end
comboWindow.actions.attackSpellToggle:setChecked(storage[ComboPanelName].attackSpellEnabled)
comboWindow.actions.attackSpellToggle.onClick = function(widget)
storage[ComboPanelName].attackSpellEnabled = not storage[ComboPanelName].attackSpellEnabled
widget:setChecked(storage[ComboPanelName].attackSpellEnabled)
end
comboWindow.actions.attackItemToggle:setChecked(storage[ComboPanelName].attackItemEnabled)
comboWindow.actions.attackItemToggle.onClick = function(widget)
storage[ComboPanelName].attackItemEnabled = not storage[ComboPanelName].attackItemEnabled
widget:setChecked(storage[ComboPanelName].attackItemEnabled)
end
-- text edits
comboWindow.trigger.onSayLeader:setText(storage[ComboPanelName].sayLeader)
comboWindow.trigger.onSayLeader.onTextChange = function(widget, text)
storage[ComboPanelName].sayLeader = text
end
comboWindow.trigger.onShootLeader:setText(storage[ComboPanelName].shootLeader)
comboWindow.trigger.onShootLeader.onTextChange = function(widget, text)
storage[ComboPanelName].shootLeader = text
end
comboWindow.trigger.onCastLeader:setText(storage[ComboPanelName].castLeader)
comboWindow.trigger.onCastLeader.onTextChange = function(widget, text)
storage[ComboPanelName].castLeader = text
end
comboWindow.trigger.onSayPhrase:setText(storage[ComboPanelName].sayPhrase)
comboWindow.trigger.onSayPhrase.onTextChange = function(widget, text)
storage[ComboPanelName].sayPhrase = text
end
comboWindow.actions.attackSpell:setText(storage[ComboPanelName].spell)
comboWindow.actions.attackSpell.onTextChange = function(widget, text)
storage[ComboPanelName].spell = text
end
comboWindow.server.botServerLeader:setText(storage[ComboPanelName].serverLeader)
comboWindow.server.botServerLeader.onTextChange = function(widget, text)
storage[ComboPanelName].serverLeader = text
end
end
-- bot server
-- [[ join party made by Frosty ]] --
local shouldCloseWindow = false
local firstInvitee = true
local isInComboTeam = false
macro(10, function()
if shouldCloseWindow and storage[ComboPanelName].serverEnabled and storage[ComboPanelName].enabled then
local channelsWindow = modules.game_console.channelsWindow
if channelsWindow then
local child = channelsWindow:getChildById("buttonCancel")
if child then
child:onClick()
shouldCloseWindow = false
isInComboTeam = true
end
end
end
end)
comboWindow.server.partyButton.onClick = function(widget)
if storage[ComboPanelName].serverEnabled and storage[ComboPanelName].enabled then
if storage[ComboPanelName].serverLeader:len() > 0 and storage.BotServerChannel:len() > 0 then
talkPrivate(storage[ComboPanelName].serverLeader, "request invite " .. storage.BotServerChannel)
else
error("Request failed. Lack of data.")
end
end
end
onTextMessage(function(mode, text)
if storage[ComboPanelName].serverEnabled and storage[ComboPanelName].enabled then
if mode == 20 then
if string.find(text, "invited you to") then
local regex = "[a-zA-Z]*"
local regexData = regexMatch(text, regex)
if regexData[1][1]:lower() == storage[ComboPanelName].serverLeader:lower() then
local leader = getCreatureByName(regexData[1][1])
if leader then
g_game.partyJoin(leader:getId())
g_game.requestChannels()
g_game.joinChannel(1)
shouldCloseWindow = true
end
end
end
end
end
end)
onTalk(function(name, level, mode, text, channelId, pos)
if storage[ComboPanelName].serverEnabled and storage[ComboPanelName].enabled then
if mode == 4 then
if string.find(text, "request invite") then
local access = string.match(text, "%d.*")
if access and access == storage.BotServerChannel then
local minion = getCreatureByName(name)
if minion then
g_game.partyInvite(minion:getId())
if firstInvitee then
g_game.requestChannels()
g_game.joinChannel(1)
shouldCloseWindow = true
firstInvitee = false
end
end
else
talkPrivate(name, "Incorrect access key!")
end
end
end
end
-- [[ End of Frosty's Code ]] --
if storage[ComboPanelName].enabled and storage[ComboPanelName].enabled then
if name:lower() == storage[ComboPanelName].sayLeader:lower() and string.find(text, storage[ComboPanelName].sayPhrase) and storage[ComboPanelName].onSayEnabled then
startCombo = true
end
if (storage[ComboPanelName].castLeader and name:lower() == storage[ComboPanelName].castLeader:lower()) and isAttSpell(text) and storage[ComboPanelName].onCastEnabled then
startCombo = true
end
end
if storage[ComboPanelName].enabled and storage[ComboPanelName].commandsEnabled and (storage[ComboPanelName].shootLeader and name:lower() == storage[ComboPanelName].shootLeader:lower()) or (storage[ComboPanelName].sayLeader and name:lower() == storage[ComboPanelName].sayLeader:lower()) or (storage[ComboPanelName].castLeader and name:lower() == storage[ComboPanelName].castLeader:lower()) then
if string.find(text, "ue") then
say(storage[ComboPanelName].spell)
elseif string.find(text, "sd") then
local params = string.split(text, ",")
if #params == 2 then
local target = params[2]:trim()
if getCreatureByName(target) then
useWith(3155, getCreatureByName(target))
end
end
elseif string.find(text, "att") then
local attParams = string.split(text, ",")
if #attParams == 2 then
local atTarget = attParams[2]:trim()
if getCreatureByName(atTarget) and storage[ComboPanelName].attack == "COMMAND TARGET" then
g_game.attack(getCreatureByName(atTarget))
end
end
end
end
if isAttSpell(text) and storage[ComboPanelName].enabled and storage[ComboPanelName].serverEnabled then
BotServer.send("trigger", "start")
end
end)
onMissle(function(missle)
if storage[ComboPanelName].enabled and storage[ComboPanelName].onShootEnabled then
if not storage[ComboPanelName].shootLeader or storage[ComboPanelName].shootLeader:len() == 0 then
return
end
local src = missle:getSource()
if src.z ~= posz() then
return
end
local from = g_map.getTile(src)
local to = g_map.getTile(missle:getDestination())
if not from or not to then
return
end
local fromCreatures = from:getCreatures()
local toCreatures = to:getCreatures()
if #fromCreatures ~= 1 or #toCreatures ~= 1 then
return
end
local c1 = fromCreatures[1]
local t1 = toCreatures[1]
leaderTarget = t1
if c1:getName():lower() == storage[ComboPanelName].shootLeader:lower() then
if storage[ComboPanelName].attackItemEnabled and storage[ComboPanelName].item and storage[ComboPanelName].item > 100 and findItem(storage[ComboPanelName].item) then
useWith(storage[ComboPanelName].item, t1)
end
if storage[ComboPanelName].attackSpellEnabled and storage[ComboPanelName].spell:len() > 1 then
say(storage[ComboPanelName].spell)
end
end
end
end)
macro(10, function()
if not storage[ComboPanelName].enabled or not storage[ComboPanelName].attackLeaderTargetEnabled then return end
if leaderTarget and storage[ComboPanelName].attack == "LEADER TARGET" then
if not getTarget() or (getTarget() and getTarget():getName() ~= leaderTarget:getName()) then
g_game.attack(leaderTarget)
end
end
if storage[ComboPanelName].enabled and storage[ComboPanelName].serverEnabled and storage[ComboPanelName].attack == "SERVER LEADER TARGET" and serverTarget then
if serverTarget and not getTarget() or (getTarget() and getTarget():getname() ~= serverTarget)
then
g_game.attack(serverTarget)
end
end
end)
local toFollow
local toFollowPos = {}
macro(100, function()
toFollow = nil
if not storage[ComboPanelName].enabled or not storage[ComboPanelName].followLeaderEnabled then return end
if leaderTarget and storage[ComboPanelName].follow == "LEADER TARGET" and leaderTarget:isPlayer() then
toFollow = leaderTarget:getName()
elseif storage[ComboPanelName].follow == "SERVER LEADER TARGET" and storage[ComboPanelName].serverLeader:len() ~= 0 then
toFollow = serverTarget
elseif storage[ComboPanelName].follow == "SERVER LEADER" and storage[ComboPanelName].serverLeader:len() ~= 0 then
toFollow = storage[ComboPanelName].serverLeader
elseif storage[ComboPanelName].follow == "LEADER" then
if storage[ComboPanelName].onSayEnabled and storage[ComboPanelName].sayLeader:len() ~= 0 then
toFollow = storage[ComboPanelName].sayLeader
elseif storage[ComboPanelName].onCastEnabled and storage[ComboPanelName].castLeader:len() ~= 0 then
toFollow = storage[ComboPanelName].castLeader
elseif storage[ComboPanelName].onShootEnabled and storage[ComboPanelName].shootLeader:len() ~= 0 then
toFollow = storage[ComboPanelName].shootLeader
end
end
if not toFollow then return end
local target = getCreatureByName(toFollow)
if target then
local tpos = target:getPosition()
toFollowPos[tpos.z] = tpos
end
if player:isWalking() then return end
local p = toFollowPos[posz()]
if not p then return end
if autoWalk(p, 20, {ignoreNonPathable=true, precision=1, ignoreStairs=false}) then
delay(100)
end
end)
onCreaturePositionChange(function(creature, oldPos, newPos)
if creature:getName() == toFollow and newPos then
toFollowPos[newPos.z] = newPos
end
end)
local timeout = now
macro(10, function()
if storage[ComboPanelName].enabled and startCombo then
if storage[ComboPanelName].attackItemEnabled and storage[ComboPanelName].item and storage[ComboPanelName].item > 100 and findItem(storage[ComboPanelName].item) then
useWith(storage[ComboPanelName].item, getTarget())
end
if storage[ComboPanelName].attackSpellEnabled and storage[ComboPanelName].spell:len() > 1 then
say(storage[ComboPanelName].spell)
end
startCombo = false
end
-- attack part / server
if BotServer._websocket and storage[ComboPanelName].enabled and storage[ComboPanelName].serverEnabled then
if target() and now - timeout > 500 then
targetPos = target():getName()
BotServer.send("target", targetPos)
timeout = now
end
end
end)
onUseWith(function(pos, itemId, target, subType)
if BotServer._websocket and itemId == 3155 then
BotServer.send("useWith", target:getPosition())
end
end)
if BotServer._websocket and storage[ComboPanelName].enabled and storage[ComboPanelName].serverEnabled then
BotServer.listen("trigger", function(name, message)
if message == "start" and name:lower() ~= player:getName():lower() and name:lower() == storage[ComboPanelName].serverLeader:lower() and storage[ComboPanelName].serverTriggers then
startCombo = true
end
end)
BotServer.listen("target", function(name, message)
if name:lower() ~= player:getName():lower() and name:lower() == storage[ComboPanelName].serverLeader:lower() then
if not target() or target():getName() == getCreatureByName(message) then
if storage[ComboPanelName].serverLeaderTarget then
serverTarget = getCreatureByName(message)
g_game.attack(getCreatureByName(message))
end
end
end
end)
BotServer.listen("useWith", function(name, message)
local tile = g_map.getTile(message)
if storage[ComboPanelName].serverTriggers and name:lower() ~= player:getName():lower() and name:lower() == storage[ComboPanelName].serverLeader:lower() and storage[ComboPanelName].attackItemEnabled and storage[ComboPanelName].item and findItem(storage[ComboPanelName].item) then
useWith(storage[ComboPanelName].item, tile:getTopUseThing())
end
end)
end

View File

@ -0,0 +1,183 @@
setDefaultTab("Main")
pushPanelName = "pushmax"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('PUSHMAX')
Button
id: push
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(pushPanelName)
if not storage[pushPanelName] then
storage[pushPanelName] = {
enabled = true,
pushDelay = 1060,
pushMaxRuneId = 3188,
mwallBlockId = 2128,
pushMaxKey = "PageUp"
}
end
ui.title:setOn(storage[pushPanelName].enabled)
ui.title.onClick = function(widget)
storage[pushPanelName].enabled = not storage[pushPanelName].enabled
widget:setOn(storage[pushPanelName].enabled)
end
ui.push.onClick = function(widget)
pushWindow:show()
pushWindow:raise()
pushWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
pushWindow = g_ui.createWidget('PushMaxWindow', rootWidget)
pushWindow:hide()
pushWindow.closeButton.onClick = function(widget)
pushWindow:hide()
end
local updateDelayText = function()
pushWindow.delayText:setText("Push Delay: ".. storage[pushPanelName].pushDelay)
end
updateDelayText()
pushWindow.delay.onValueChange = function(scroll, value)
storage[pushPanelName].pushDelay = value
updateDelayText()
end
pushWindow.delay:setValue(storage[pushPanelName].pushDelay)
pushWindow.runeId.onItemChange = function(widget)
storage[pushPanelName].pushMaxRuneId = widget:getItemId()
end
pushWindow.runeId:setItemId(storage[pushPanelName].pushMaxRuneId)
pushWindow.mwallId.onItemChange = function(widget)
storage[pushPanelName].mwallBlockId = widget:getItemId()
end
pushWindow.mwallId:setItemId(storage[pushPanelName].mwallBlockId)
pushWindow.hotkey.onTextChange = function(widget, text)
storage[pushPanelName].pushMaxKey = text
end
pushWindow.hotkey:setText(storage[pushPanelName].pushMaxKey)
end
function matchPosition(curX, curY, destX, destY)
return (curX == destX and curY == destY)
end
local target
local targetTile
local targetOldPos
macro(10, function()
if not storage[pushPanelName].enabled then return end
if getTarget() then
target = getTarget()
else
target = g_game.getFollowingCreature()
end
if target and targetTile then
if not matchPosition(target:getPosition().x, target:getPosition().y, targetTile:getPosition().x, targetTile:getPosition().y) then
local tile = g_map.getTile(target:getPosition())
targetOldPos = tile:getPosition()
if tile then
if targetTile:getTopThing():getId() == 2129 or targetTile:getTopThing():getId() == 2130 or targetTile:getTopThing():getId() == tonumber(storage[pushPanelName].mwallBlockId) then
if targetTile:getTimer() <= tonumber(storage[pushPanelName].pushDelay) then
useWith(tonumber(storage[pushPanelName].pushMaxRuneId), target) -- 3197 desintigrate rune / 3188 firebomb rune
delay(10)
g_game.move(target, targetTile:getPosition())
tile:setText("")
targetTile:setText("")
target = nil
targetTile = nil
end
else
if tile:getTopUseThing():isPickupable() or not tile:getTopUseThing():isNotMoveable() then
useWith(tonumber(storage[pushPanelName].pushMaxRuneId), target)
delay(10)
g_game.move(target, targetTile:getPosition())
delay(1250)
else
g_game.move(target, targetTile:getPosition())
delay(1250)
end
end
end
else
if targetOldPos then
local tile = g_map.getTile(targetOldPos)
if tile then
tile:setText("")
targetTile:setText("")
end
end
target = nil
targetTile = nil
end
end
end)
local resetTimer = now
onKeyDown(function(keys)
if not target or not storage[pushPanelName].enabled then return end
if keys == storage[pushPanelName].pushMaxKey and resetTimer == 0 then
local tile = getTileUnderCursor()
if tile and not tile:getCreatures()[1] then
targetTile = tile
tile:setText("DESTINATION")
end
end
resetTimer = now
end)
onKeyPress(function(keys)
if not target or not storage[pushPanelName].enabled then return end
if keys == storage[pushPanelName].pushMaxKey and (resetTimer - now) < -10 then
for _, tile in ipairs(g_map.getTiles(posz())) do
if getDistanceBetween(pos(), tile:getPosition()) < 3 then
if tile:getText() ~= "" then
tile:setText("")
end
end
end
target = nil
targetTile = nil
resetTimer = 0
else
resetTimer = 0
end
end)
onCreaturePositionChange(function(creature, newPos, oldPos)
if target and target:isPlayer() and storage[pushPanelName].enabled then
if creature:getName() == target:getName() then
targetTile = nil
for _, tile in ipairs(g_map.getTiles(posz())) do
if getDistanceBetween(pos(), tile:getPosition()) < 3 then
if tile:getText() ~= "" then
tile:setText("")
end
end
end
end
end
end)

View File

@ -0,0 +1,293 @@
setDefaultTab("HP")
healPanelName = "healbot"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('HealBot')
Button
id: combos
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]])
ui:setId(healPanelName)
if not storage[healPanelName] or not storage[healPanelName].spellTable or not storage[healPanelName].itemTable then
storage[healPanelName] = {
enabled = false,
spellTable = {},
itemTable = {}
}
end
ui.title:setOn(storage[healPanelName].enabled)
ui.title.onClick = function(widget)
storage[healPanelName].enabled = not storage[healPanelName].enabled
widget:setOn(storage[healPanelName].enabled)
end
ui.combos.onClick = function(widget)
healWindow:show()
healWindow:raise()
healWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
healWindow = g_ui.createWidget('HealWindow', rootWidget)
healWindow:hide()
healWindow.spells.addSpell.onClick = function(widget)
local spellFormula = healWindow.spells.spellFormula:getText():trim()
local manaCost = tonumber(healWindow.spells.manaCost:getText())
local spellTrigger = tonumber(healWindow.spells.spellValue:getText())
local spellSource = healWindow.spells.spellSource:getCurrentOption().text
local spellEquasion = healWindow.spells.spellCondition:getCurrentOption().text
local source
local equasion
if not manaCost then
warn("HealBot: incorrect mana cost value!")
healWindow.spells.spellFormula:setText('')
healWindow.spells.spellValue:setText('')
healWindow.spells.manaCost:setText('')
return
end
if not spellTrigger then
warn("HealBot: incorrect condition value!")
healWindow.spells.spellFormula:setText('')
healWindow.spells.spellValue:setText('')
healWindow.spells.manaCost:setText('')
return
end
if spellSource == "Current Mana" then
source = "MP"
elseif spellSource == "Current Health" then
source = "HP"
elseif spellSource == "Mana Percent" then
source = "MP%"
else
source = "HP%"
end
if spellEquasion == "Above" then
equasion = ">"
elseif spellEquasion == "Below" then
equasion = "<"
else
equasion = "="
end
if spellFormula:len() > 0 then
table.insert(storage[healPanelName].spellTable, {spell = spellFormula, sign = equasion, origin = source, cost = manaCost, value = spellTrigger})
local label = g_ui.createWidget("SpellEntry", healWindow.spells.spellList)
label.remove.onClick = function(widget)
table.removevalue(storage[healPanelName].spellTable, label:getText())
label:destroy()
end
label:setText("(MP>" .. manaCost .. ") " .. source .. equasion .. spellTrigger .. ":" .. spellFormula)
healWindow.spells.spellFormula:setText('')
healWindow.spells.spellValue:setText('')
healWindow.spells.manaCost:setText('')
end
end
healWindow.items.addItem.onClick = function(widget)
local id = healWindow.items.itemId:getItemId()
local trigger = tonumber(healWindow.items.itemValue:getText())
local src = healWindow.items.itemSource:getCurrentOption().text
local eq = healWindow.items.itemCondition:getCurrentOption().text
local source
local equasion
if not trigger then
warn("HealBot: incorrect trigger value!")
healWindow.items.id:setItemId(0)
healWindow.items.trigger:setText('')
return
end
if src == "Current Mana" then
source = "MP"
elseif src == "Current Health" then
source = "HP"
elseif src == "Mana Percent" then
source = "MP%"
else
source = "HP%"
end
if eq == "Above" then
equasion = ">"
elseif eq == "Below" then
equasion = "<"
else
equasion = "="
end
if id > 100 then
table.insert(storage[healPanelName].itemTable, {item = id, sign = equasion, origin = source, value = trigger})
local label = g_ui.createWidget("SpellEntry", healWindow.items.itemList)
label.remove.onClick = function(widget)
table.removevalue(storage[healPanelName].itemTable, label:getText())
label:destroy()
end
label:setText(source .. equasion .. trigger .. ":" .. id)
healWindow.items.id:setItemId(0)
healWindow.items.trigger:setText('')
end
end
if storage[healPanelName].itemTable and #storage[healPanelName].itemTable > 0 then
for _, entry in pairs(storage[healPanelName].itemTable) do
local label = g_ui.createWidget("SpellEntry", healWindow.items.itemList)
label.remove.onClick = function(widget)
table.removevalue(storage[healPanelName].itemTable, entry)
label:destroy()
end
label:setText(entry.origin .. entry.sign .. entry.value .. ":" .. entry.item)
end
end
if storage[healPanelName].spellTable and #storage[healPanelName].spellTable > 0 then
for _, entry in pairs(storage[healPanelName].spellTable) do
local label = g_ui.createWidget("SpellEntry", healWindow.spells.spellList)
label.remove.onClick = function(widget)
table.removevalue(storage[healPanelName].spellTable, entry)
label:destroy()
end
label:setText("(MP>" .. entry.cost .. ") " .. entry.origin .. entry.sign .. entry.value .. ":" .. entry.spell)
end
end
healWindow.closeButton.onClick = function(widget)
healWindow:hide()
end
end
-- spells
macro(100, function()
if not storage[healPanelName].enabled or modules.game_cooldown.isGroupCooldownIconActive(2) or #storage[healPanelName].spellTable == 0 then return end
for _, entry in pairs(storage[healPanelName].spellTable) do
if mana() >= tonumber(entry.cost) and not getSpellCoolDown(entry.spell) then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and hppercent() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and hppercent() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "HP" then
if entry.sign == "=" and hp() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and hp() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and hp() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "MP%" then
if entry.sign == "=" and manapercent() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and manapercent() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and manapercent() <= entry.value then
say(entry.spell)
return
end
elseif entry.origin == "MP" then
if entry.sign == "=" and mana() == entry.value then
say(entry.spell)
return
elseif entry.sign == ">" and mana() >= entry.value then
say(entry.spell)
return
elseif entry.sign == "<" and mana() <= entry.value then
say(entry.spell)
return
end
end
end
end
end)
-- items
macro(500, function()
if not storage[healPanelName].enabled or storage.isUsing or #storage[healPanelName].itemTable == 0 then return end
for _, entry in pairs(storage[healPanelName].itemTable) do
local item = findItem(entry.item)
if item then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
useWith(entry.item, player)
return
elseif entry.sign == ">" and hppercent() >= entry.value then
useWith(entry.item, player)
return
elseif entry.sign == "<" and hppercent() <= entry.value then
useWith(entry.item, player)
return
end
elseif entry.origin == "HP" then
if entry.sign == "=" and hp() == tonumberentry.value then
useWith(entry.item, player)
return
elseif entry.sign == ">" and hp() >= entry.value then
useWith(entry.item, player)
return
elseif entry.sign == "<" and hp() <= entry.value then
useWith(entry.item, player)
return
end
elseif entry.origin == "MP%" then
if entry.sign == "=" and manapercent() == entry.value then
useWith(entry.item, player)
return
elseif entry.sign == ">" and manapercent() >= entry.value then
useWith(entry.item, player)
return
elseif entry.sign == "<" and manapercent() <= entry.value then
useWith(entry.item, player)
return
end
elseif entry.origin == "MP" then
if entry.sign == "=" and mana() == entry.value then
useWith(entry.item, player)
return
elseif entry.sign == ">" and mana() >= entry.value then
useWith(entry.item, player)
return
elseif entry.sign == "<" and mana() <= entry.value then
useWith(entry.item, player)
return
end
end
end
end
end)

View File

@ -0,0 +1,121 @@
setDefaultTab("Main")
local panelName = "advancedFriendHealer"
local ui = setupUI([[
Panel
height: 19
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('Friend Healer')
Button
id: editList
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
]], parent)
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {
minMana = 60,
minFriendHp = 40,
spellName = "exura sio",
spellHeal = true,
distance = 8,
itemHeal = true,
id = 3160
}
end
rootWidget = g_ui.getRootWidget()
sioListWindow = g_ui.createWidget('SioListWindow', rootWidget)
sioListWindow:hide()
ui.title:setOn(storage[panelName].enabled)
sioListWindow.spell:setOn(storage[panelName].spellHeal)
sioListWindow.item:setOn(storage[panelName].itemHeal)
ui.title.onClick = function(widget)
storage[panelName].enabled = not storage[panelName].enabled
widget:setOn(storage[panelName].enabled)
end
ui.editList.onClick = function(widget)
sioListWindow:show()
sioListWindow:raise()
sioListWindow:focus()
end
sioListWindow.spell.onClick = function(widget)
storage[panelName].spellHeal = not storage[panelName].spellHeal
widget:setOn(storage[panelName].spellHeal)
end
sioListWindow.item.onClick = function(widget)
storage[panelName].itemHeal = not storage[panelName].itemHeal
widget:setOn(storage[panelName].itemHeal)
end
sioListWindow.closeButton.onClick = function(widget)
sioListWindow:hide()
end
sioListWindow.spellName.onTextChange = function(widget, text)
storage[panelName].spellName = text
end
local updateMinManaText = function()
sioListWindow.manaInfo:setText("Minimum Mana >= " .. storage[panelName].minMana .. "%")
end
local updateFriendHpText = function()
sioListWindow.friendHp:setText("Heal Friend Below " .. storage[panelName].minFriendHp .. "% hp")
end
local updateDistanceText = function()
sioListWindow.distText:setText("Max Distance: " .. storage[panelName].distance)
end
sioListWindow.Distance.onValueChange = function(scroll, value)
storage[panelName].distance = value
updateDistanceText()
end
updateDistanceText()
sioListWindow.minMana.onValueChange = function(scroll, value)
storage[panelName].minMana = value
updateMinManaText()
end
sioListWindow.minFriendHp.onValueChange = function(scroll, value)
storage[panelName].minFriendHp = value
updateFriendHpText()
end
sioListWindow.itemId:setItemId(storage[panelName].id)
sioListWindow.itemId.onItemChange = function(widget)
storage[panelName].id = widget:getItemId()
end
sioListWindow.spellName:setText(storage[panelName].spellName)
sioListWindow.minMana:setValue(storage[panelName].minMana)
sioListWindow.minFriendHp:setValue(storage[panelName].minFriendHp)
sioListWindow.Distance:setValue(storage[panelName].distance)
local healItem
macro(200, function()
if storage[panelName].enabled and storage[panelName].spellName:len() > 0 and manapercent() > storage[panelName].minMana then
for _, spec in ipairs(getSpectators()) do
if spec:isPlayer() and storage[panelName].minFriendHp >= spec:getHealthPercent() and isFriend(spec:getName()) then
if storage[panelName].spellHeal then
saySpell(storage[panelName].spellName .. ' "' .. spec:getName(), 100)
end
healItem = findItem(storage[panelName].id)
if storage[panelName].itemHeal and distanceFromPlayer(spec:getPosition()) <= storage[panelName].distance and healItem then
useWith(storage[panelName].id, spec)
delay(300)
end
end
end
end
end)
addSeparator()

View File

@ -0,0 +1,150 @@
local listPanelName = "playerList"
local ui = setupUI([[
Panel
height: 18
Button
id: editList
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 18
text: Player Lists
]], parent)
ui:setId(listPanelName)
if not storage[listPanelName] then
storage[listPanelName] = {
enemyList = {},
friendList = {},
groupMembers = true,
outfits = false,
marks = false
}
end
rootWidget = g_ui.getRootWidget()
playerListWindow = g_ui.createWidget('PlayerListsWindow', rootWidget)
playerListWindow:hide()
playerListWindow.Members:setOn(storage[listPanelName].groupMembers)
playerListWindow.Members.onClick = function(widget)
storage[listPanelName].groupMembers = not storage[listPanelName].groupMembers
widget:setOn(storage[listPanelName].groupMembers)
end
playerListWindow.Outfit:setOn(storage[listPanelName].outfits)
playerListWindow.Outfit.onClick = function(widget)
storage[listPanelName].outfits = not storage[listPanelName].outfits
widget:setOn(storage[listPanelName].outfits)
end
playerListWindow.Marks:setOn(storage[listPanelName].marks)
playerListWindow.Marks.onClick = function(widget)
storage[listPanelName].marks = not storage[listPanelName].marks
widget:setOn(storage[listPanelName].marks)
end
if storage[listPanelName].enemyList and #storage[listPanelName].enemyList > 0 then
for _, name in ipairs(storage[listPanelName].enemyList) do
local label = g_ui.createWidget("PlayerName", playerListWindow.EnemyList)
label.remove.onClick = function(widget)
table.removevalue(storage[listPanelName].enemyList, label:getText())
label:destroy()
end
label:setText(name)
end
end
playerListWindow.AddFriend.onClick = function(widget)
local friendName = playerListWindow.FriendName:getText()
if friendName:len() > 0 and not table.contains(storage[listPanelName].enemyList, friendName, true) then
table.insert(storage[listPanelName].enemyList, friendName)
local label = g_ui.createWidget("PlayerName", playerListWindow.EnemyList)
label.remove.onClick = function(widget)
table.removevalue(storage[listPanelName].enemyList, label:getText())
label:destroy()
end
label:setText(friendName)
playerListWindow.FriendName:setText('')
end
end
if storage[listPanelName].friendList and #storage[listPanelName].friendList > 0 then
for _, name in ipairs(storage[listPanelName].friendList) do
local label = g_ui.createWidget("PlayerName", playerListWindow.FriendList)
label.remove.onClick = function(widget)
table.removevalue(storage[listPanelName].friendList, label:getText())
label:destroy()
end
label:setText(name)
end
end
playerListWindow.AddFriend.onClick = function(widget)
local friendName = playerListWindow.FriendName:getText()
if friendName:len() > 0 and not table.contains(storage[listPanelName].friendList, friendName, true) then
table.insert(storage[listPanelName].friendList, friendName)
local label = g_ui.createWidget("PlayerName", playerListWindow.FriendList)
label.remove.onClick = function(widget)
table.removevalue(storage[listPanelName].friendList, label:getText())
label:destroy()
end
label:setText(friendName)
playerListWindow.FriendName:setText('')
end
end
playerListWindow.AddEnemy.onClick = function(widget)
local enemyName = playerListWindow.EnemyName:getText()
if enemyName:len() > 0 and not table.contains(storage[listPanelName].enemyList, enemyName, true) then
table.insert(storage[listPanelName].enemyList, enemyName)
local label = g_ui.createWidget("PlayerName", playerListWindow.EnemyList)
label.remove.onClick = function(widget)
table.removevalue(storage[listPanelName].enemyList, label:getText())
label:destroy()
end
label:setText(enemyName)
playerListWindow.EnemyName:setText('')
end
end
ui.editList.onClick = function(widget)
playerListWindow:show()
playerListWindow:raise()
playerListWindow:focus()
end
playerListWindow.closeButton.onClick = function(widget)
playerListWindow:hide()
end
function refreshStatus()
for _, spec in ipairs(getSpectators()) do
if spec:isPlayer() and not spec:isLocalPlayer() then
if storage[listPanelName].outfits then
specOutfit = spec:getOutfit()
if isEnemy(spec:getName()) then
specOutfit.head = 112
specOutfit.body = 112
specOutfit.legs = 112
specOutfit.feet = 112
spec:setOutfit(specOutfit)
elseif isFriend(spec:getName()) then
specOutfit.head = 88
specOutfit.body = 88
specOutfit.legs = 88
specOutfit.feet = 88
spec:setOutfit(specOutfit)
end
end
end
end
end
refreshStatus()
onCreatureAppear(function(creature)
if creature:isPlayer() then
refreshStatus()
end
end)
addSeparator()

View File

@ -0,0 +1,691 @@
-- if you want to change tab, in line below insert: setDefaultTab("tab name")
attackPanelName = "attackbot"
local ui = setupUI([[
Panel
height: 38
BotSwitch
id: title
anchors.top: parent.top
anchors.left: parent.left
text-align: center
width: 130
!text: tr('AttackBot')
Button
id: settings
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 3
height: 17
text: Setup
Button
id: mode
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.horizontalCenter
text: Mode: PVP
margin-right: 2
margin-top: 4
font: cipsoftFont
height: 17
Button
id: safe
anchors.top: settings.bottom
anchors.left: parent.horizontalCenter
anchors.right: parent.right
text: PVP Safe
margin-left: 2
margin-top: 4
height: 17
font:cipsoftFont
]])
ui:setId(attackPanelName)
local i = 1
local j = 1
local k = 1
local pvpDedicated = false
local item = false
UI.Separator()
if not storage[attackPanelName] or not storage[attackPanelName].attackTable then
storage[attackPanelName] = {
pvpMode = false,
pvpSafe = true,
enabled = false,
attackTable = {}
}
end
local categories = {
"Select Category",
"Area Spell (exevo mas san, exevo gran mas flam etc.)",
"Adjacent (exori, exori gran)",
"Front Sweep (exori min)",
"Wave (exevo tera hur, exevo gran vis lux)",
"Targeted Spell (exori ico, exori flam etc.)",
"Targeted Rune (sudden death, heavy magic missle etc.)",
"Area Rune (great fireball, avalanche etc.)"
}
local labels = {
"",
"Area Spell",
"Adjacent",
"Front Sweep",
"Wave",
"Targeted Spell",
"Targeted Rune",
"Area Rune",
}
local range = {
"Select Range",
"Range: 1",
"Range: 2",
"Range: 3",
"Range: 4",
"Range: 5",
"Range: 6",
"Range: 7",
"Range: 8",
"Range: 9"
}
local pattern = {
"Pattern",
"Single (exori frigo, SD)",
"Large AOE (mas tera)",
"Medium AOE (mas frigo)",
"Small AOE (mas san)",
"Large Wave (tera hur)",
"Medium Wave (frigo hur)",
"Small Wave (gran frigo hur)",
"Beam (exevo vis lux)",
"Adjacent (exori)",
"Area Rune (GFB, AVA)"
}
local updateModeText = function()
local text
if storage[attackPanelName].pvpMode then
text = "PVP"
ui.mode:setColor("yellow")
else
text = "HUNT"
ui.mode:setColor("green")
end
ui.mode:setText("MODE: " .. text)
end
updateModeText()
local updatePvpColor = function()
if storage[attackPanelName].pvpSafe then
ui.safe:setColor("green")
else
ui.safe:setColor("white")
end
end
updatePvpColor()
ui.title:setOn(storage[attackPanelName].enabled)
ui.title.onClick = function(widget)
storage[attackPanelName].enabled = not storage[attackPanelName].enabled
widget:setOn(storage[attackPanelName].enabled)
end
ui.mode.onClick = function(widget)
storage[attackPanelName].pvpMode = not storage[attackPanelName].pvpMode
updateModeText()
end
ui.safe.onClick = function(widget)
storage[attackPanelName].pvpSafe = not storage[attackPanelName].pvpSafe
updatePvpColor()
end
ui.settings.onClick = function(widget)
attackWindow:show()
attackWindow:raise()
attackWindow:focus()
end
rootWidget = g_ui.getRootWidget()
if rootWidget then
attackWindow = g_ui.createWidget('AttackWindow', rootWidget)
attackWindow:hide()
-- functions
local updateCategoryText = function()
attackWindow.category:setText(categories[i])
end
updateCategoryText()
local updateParameter1Text = function()
attackWindow.parameter1:setText(pattern[k])
end
updateParameter1Text()
local updateParameter2Text = function()
attackWindow.parameter2:setText(range[j])
end
updateParameter2Text()
-- checkbox
attackWindow.pvpSpell.onClick = function(widget)
pvpDedicated = not pvpDedicated
attackWindow.pvpSpell:setChecked(pvpDedicated)
end
--buttons
attackWindow.CloseButton.onClick = function(widget)
attackWindow:hide()
end
local inputTypeToggle = function()
if attackWindow.category:getText():lower():find("rune") then
item = true
attackWindow.spellFormula:setText("")
attackWindow.spellFormula:hide()
attackWindow.spellDescription:hide()
attackWindow.itemId:show()
attackWindow.itemDescription:show()
else
item = false
attackWindow.itemId:setItemId(0)
attackWindow.itemId:hide()
attackWindow.itemDescription:hide()
attackWindow.spellFormula:show()
attackWindow.spellDescription:show()
end
end
inputTypeToggle()
attackWindow.categoryNext.onClick = function(widget)
if i == #categories then
i = 1
else
i = i + 1
end
updateCategoryText()
inputTypeToggle()
end
attackWindow.categoryPrev.onClick = function(widget)
if i == 1 then
i = #categories
else
i = i - 1
end
updateCategoryText()
inputTypeToggle()
end
attackWindow.parameter1Next.onClick = function(widget)
if k == #pattern then
k = 1
else
k = k + 1
end
updateParameter1Text()
end
attackWindow.parameter1Prev.onClick = function(widget)
if k == 1 then
k = #pattern
else
k = k - 1
end
updateParameter1Text()
end
attackWindow.parameter2Next.onClick = function(widget)
if j == #range then
j = 1
else
j = j + 1
end
updateParameter2Text()
end
attackWindow.parameter2Prev.onClick = function(widget)
if j == 1 then
j = #range
else
j = j - 1
end
updateParameter2Text()
end
local validVal = function(v)
if type(v) ~= "number" then
local val = tonumber(v)
if not val then return false end
end
if v >= 0 and v < 101 then
return true
else
return false
end
end
local clearValues = function()
attackWindow.spellFormula:setText("")
attackWindow.minMana:setText("")
attackWindow.minMonsters:setText("")
attackWindow.itemId:setItemId(0)
pvpDedicated = false
item = false
attackWindow.pvpSpell:setChecked(false)
i = 1
j = 1
k = 1
updateParameter1Text()
updateParameter2Text()
updateCategoryText()
inputTypeToggle()
end
attackWindow.addButton.onClick = function(widget)
local val
if (item and attackWindow.itemId:getItemId() <= 100) or (not item and attackWindow.spellFormula:getText():len() == 0) then
warn("AttackBot: missing spell or item id!")
elseif not tonumber(attackWindow.minMana:getText()) or not validVal(tonumber(attackWindow.minMana:getText())) then
warn("AttackBot: Mana Values incorrect! it has to be number from between 1 and 100")
elseif not tonumber(attackWindow.minMonsters:getText()) or not validVal(tonumber(attackWindow.minMonsters:getText())) then
warn("AttackBot: Monsters Count incorrect! it has to be number higher than 0")
elseif i == 1 or j == 1 or k == 1 then
warn("AttackBot: Categories not changed! You need to be more precise")
else
if item then
val = attackWindow.itemId:getItemId()
else
val = attackWindow.spellFormula:getText()
end
local pvpText
table.insert(storage[attackPanelName].attackTable, {attack = val, manaCost = tonumber(attackWindow.minMana:getText()), minMonsters = tonumber(attackWindow.minMonsters:getText()), pvp = pvpDedicated, dist = j-1, model = k, category = i})
local label = g_ui.createWidget("AttackEntry", attackWindow.attackList)
if pvpDedicated then
label:setText("(" .. tonumber(attackWindow.minMana:getText()) .. "% MP) " .. labels[i] .. ": " .. val .. " (Range: ".. j-1 .. ")")
label:setColor("yellow")
else
label:setText("(" .. tonumber(attackWindow.minMana:getText()) .. "% MP & mob >= " .. tonumber(attackWindow.minMonsters:getText()) .. ") " .. labels[i] .. ": " .. val .. " (Range: ".. j-1 .. ")")
label:setColor("green")
end
clearValues()
end
end
if storage[attackPanelName].attackTable and #storage[attackPanelName].attackTable > 0 then
for _, entry in pairs(storage[attackPanelName].attackTable) do
local label = g_ui.createWidget("AttackEntry", attackWindow.attackList)
label.remove.onClick = function(widget)
table.removevalue(storage[attackPanelName].attackTable, entry)
label:destroy()
end
if entry.pvp then
label:setText("(" .. entry.manaCost .. "% MP) " .. labels[entry.category] .. ": " .. entry.attack .. " (Range: ".. j-1 .. ")")
label:setColor("yellow")
else
label:setText("(" .. entry.manaCost .. "% MP & mob >= " .. entry.minMonsters .. ") " .. labels[entry.category] .. ": " .. entry.attack .. " (Range: ".. j-1 .. ")")
label:setColor("green")
end
end
end
end
-- executor
-- table example (attack = 3155, manaCost = 50(%), minMonsters = 5, pvp = true, dist = 3, model = 6, category = 3)
-- i = category
-- j = range
-- k = pattern - covered
local patterns = {
"",
[[
0000001000000
0000011100000
0000111110000
0001111111000
0011111111100
0111111111110
1111111111111
0111111111110
0011111111100
0001111111000
0000111110000
0000011100000
0000001000000
]],
[[
00000100000
00011111000
00111111100
01111111110
01111111110
11111111111
01111111110
01111111110
00111111100
00001110000
00000100000
]],
[[
0011100
0111110
1111111
1111111
1111111
0111110
0011100
]],
[[
0000NNN0000
0000NNN0000
0000NNN0000
00000N00000
WWW00N00EEE
WWWWW0EEEEE
WWW00S00EEE
00000S00000
0000SSS0000
0000SSS0000
0000SSS0000
]],
[[
000NNNNN000
000NNNNN000
0000NNN0000
WW00NNN00EE
WWWW0N0EEEE
WWWWW0EEEEE
WWWW0S0EEEE
WW00SSS00EE
0000SSS0000
000SSSSS000
000SSSSS000
]],
[[
00NNN00
00NNN00
WW0N0EE
WWW0EEE
WW0S0EE
00SSS00
00SSS00
]],
[[
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
0000000N0000000
WWWWWWW0EEEEEEE
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
0000000S0000000
]],
"",
""
}
local safePatterns = {
"",
[[
000000010000000
000000111000000
000001111100000
000011111110000
000111111111000
001111111111100
011111111111110
111111111111111
011111111111110
001111111111100
000111111111000
000011111110000
000001111100000
000000111000000
000000010000000
]],
[[
0000011100000
0000111110000
0001111111000
0011111111100
0111111111110
0111111111110
1111111111111
0111111111110
0111111111110
0011111111100
0001111111000
0000111110000
0000011100000
]],
[[
000111000
001111100
011111110
111111111
111111111
111111111
011111110
001111100
000111000
]],
[[
0000NNNNN0000
0000NNNNN0000
0000NNNNN0000
0000NNNNN0000
WWWW0NNN0EEEE
WWWWWNNNEEEEE
WWWWWW0EEEEEE
WWWWWSSSEEEEE
WWWW0SSS0EEEE
0000SSSSS0000
0000SSSSS0000
0000SSSSS0000
0000SSSSS0000
]],
[[
000NNNNNNN000
000NNNNNNN000
000NNNNNNN000
WWWWNNNNNEEEE
WWWWNNNNNEEEE
WWWWWNNNEEEEE
WWWWWW0EEEEEE
WWWWWSSSEEEEE
WWWWSSSSSEEEE
WWWWSSSSSEEEE
000SSSSSSS000
000SSSSSSS000
000SSSSSSS000
]],
[[
00NNNNN00
00NNNNN00
WWNNNNNEE
WWWWNEEEE
WWWW0EEEE
WWWWSEEEE
WWSSSSSEE
00SSSSS00
00SSSSS00
]],
[[
0000000NNN0000000
0000000NNN0000000
0000000NNN0000000
0000000NNN0000000
0000000NNN0000000
0000000NNN0000000
0000000NNN0000000
WWWWWWWNNNEEEEEEE
WWWWWWWW0EEEEEEEE
WWWWWWWSSSEEEEEEE
0000000SSS0000000
0000000SSS0000000
0000000SSS0000000
0000000SSS0000000
0000000SSS0000000
0000000SSS0000000
0000000SSS0000000
]],
"",
""
}
local posN = [[
111
000
000
]]
local posE = [[
001
001
001
]]
local posS = [[
000
000
111
]]
local posW = [[
100
100
100
]]
macro(1000, function()
if not storage[attackPanelName].enabled then return end
if #storage[attackPanelName].attackTable == 0 or isInPz() or not target() or modules.game_cooldown.isGroupCooldownIconActive(1) or modules.game_cooldown.isGroupCooldownIconActive(4) then return end
local monstersN = 0
local monstersE = 0
local monstersS = 0
local monstersW = 0
monstersN = getCreaturesInArea(pos(), posN, 2)
monstersE = getCreaturesInArea(pos(), posE, 2)
monstersS = getCreaturesInArea(pos(), posS, 2)
monstersW = getCreaturesInArea(pos(), posN, 2)
local posTable = {monstersE, monstersN, monstersS, monstersW}
local bestSide = 0
local bestDir
-- pulling out the biggest number
for i, v in pairs(posTable) do
if v > bestSide then
bestSide = v
end
end
-- associate biggest number with turn direction
if monstersN == bestSide then bestDir = 0
elseif monstersE == bestSide then bestDir = 1
elseif monstersS == bestSide then bestDir = 2
elseif monstersW == bestSide then bestDir = 3
end
if player:getDirection() ~= bestDir and bestSide > 0 and CaveBot.isOn() then
turn(bestDir)
end
for _, entry in pairs(storage[attackPanelName].attackTable) do
if (type(entry.attack) == "string" and canCast(entry.attack)) or (type(entry.attack) == "number" and findItem(entry.attack)) then
if manapercent() >= entry.manaCost and distanceFromPlayer(target():getPosition()) <= entry.dist then
if storage[attackPanelName].pvpMode then
if entry.pvp and target():canShoot() then
if type(entry.attack) == "string" then
say(entry.attack)
return
else
if not storage.isUsing then
useWith(entry.attack, target())
return
end
end
end
else
if entry.category == 6 or entry.category == 7 then
if getMonsters(4) >= entry.minMonsters then
if type(entry.attack) == "number" then
if not storage.isUsing then
useWith(entry.attack, target())
return
end
else
say(entry.attack)
return
end
end
else
if killsToRs() > 2 then
local areaTile = getBestTileByPatern(patterns[4], 2, entry.dist, storage[attackPanelName].pvpSafe)
if entry.category == 4 and (not storage[attackPanelName].pvpSafe or isSafe(2, false)) and bestSide >= entry.minMonsters then
say(entry.attack)
return
elseif entry.category == 3 and (not storage[attackPanelName].pvpSafe or isSafe(2, false)) and getMonsters(1) >= entry.minMonsters then
say(entry.attack)
return
elseif entry.category == 5 and getCreaturesInArea(player, patterns[entry.model], 2) >= entry.minMonsters and (not storage[attackPanelName].pvpSafe or getCreaturesInArea(player, safePatterns[entry.model], 3) == 0) then
say(entry.attack)
return
elseif entry.category == 2 and getCreaturesInArea(pos(), patterns[entry.model], 2) >= entry.minMonsters and (not storage[attackPanelName].pvpSafe or getCreaturesInArea(pos(), safePatterns[entry.model], 3) == 0) then
say(entry.attack)
return
elseif entry.category == 8 and areaTile and areaTile.count >= entry.minMonsters then
if not storage.isUsing then
useWith(entry.attack, areaTile.pos:getTopUseThing())
end
return
else
if entry.category == 6 or entry.category == 7 then
if getMonsters(4) >= entry.minMonsters then
if type(entry.attack) == "number" then
if not storage.isUsing then
useWith(entry.attack, target())
return
end
else
say(entry.attack)
return
end
end
end
end
else
if entry.category == 6 or entry.category == 7 then
if getMonsters(4) >= entry.minMonsters then
if type(entry.attack) == "number" then
if not storage.isUsing then
useWith(entry.attack, target())
return
end
else
say(entry.attack)
return
end
end
end
end
end
end
end
end
end
end)

View File

@ -0,0 +1,207 @@
AttackEntry < Label
background-color: alpha
text-offset: 2 0
focusable: true
height: 16
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
width: 15
height: 15
AttackWindow < MainWindow
!text: tr('AttackBot')
size: 490 350
@onEscape: self:hide()
TextList
id: attackList
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
padding: 1
size: 470 150
margin-left: 3
margin-top: 3
margin-left: 3
vertical-scrollbar: attackListScrollBar
VerticalScrollBar
id: attackListScrollBar
anchors.top: attackList.top
anchors.bottom: attackList.bottom
anchors.right: attackList.right
step: 14
pixels-scroll: true
Label
id: category
anchors.top: attackList.bottom
anchors.left: attackList.left
anchors.right: attackList.right
text-align: center
margin-top: 5
image-source: /images/ui/panel_flat
image-border: 5
height: 21
margin-left: 25
margin-right: 25
NextButton
id: categoryNext
anchors.left: category.right
anchors.verticalCenter: category.verticalCenter
margin-left: 10
PreviousButton
id: categoryPrev
anchors.right: category.left
anchors.verticalCenter: category.verticalCenter
margin-right: 10
Label
id: parameter1
anchors.top: category.bottom
anchors.left: category.left
anchors.right: category.horizontalCenter
margin-top: 5
margin-right: 25
height: 21
text-align: center
image-source: /images/ui/panel_flat
image-border: 5
NextButton
id: parameter1Next
anchors.left: parameter1.right
anchors.verticalCenter: parameter1.verticalCenter
margin-left: 10
PreviousButton
id: parameter1Prev
anchors.right: parameter1.left
anchors.verticalCenter: parameter1.verticalCenter
margin-right: 10
Label
id: parameter2
anchors.top: category.bottom
anchors.left: category.horizontalCenter
anchors.right: category.right
margin-top: 5
margin-left: 25
height: 21
text-align: center
image-source: /images/ui/panel_flat
image-border: 5
NextButton
id: parameter2Next
anchors.left: parameter2.right
anchors.verticalCenter: parameter2.verticalCenter
margin-left: 10
PreviousButton
id: parameter2Prev
anchors.right: parameter2.left
anchors.verticalCenter: parameter2.verticalCenter
margin-right: 10
TextEdit
id: spellFormula
anchors.left: parent.left
anchors.top: parameter2Prev.bottom
margin-top: 27
margin-left: 5
width: 200
Label
id: spellDescription
anchors.left: prev.left
anchors.right: prev.right
anchors.bottom: prev.top
margin-bottom: 2
text-align: center
text: Insert Spell Formula Below
BotItem
id: itemId
anchors.left: parent.left
anchors.top: parameter2Prev.bottom
margin-top: 20
margin-left: 5
Label
id: itemDescription
anchors.left: itemId.right
margin-left: 5
anchors.verticalCenter: itemId.verticalCenter
text: < insert id or drag item here
Label
anchors.left: parameter2Prev.left
anchors.top: parameter2Prev.bottom
margin-top: 17
text-align: center
text: Min Monsters:
TextEdit
id: minMonsters
anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
width: 30
Label
anchors.left: parameter2Prev.left
anchors.top: prev.bottom
margin-top: 10
text-align: center
text: Min Mana%:
TextEdit
id: minMana
anchors.left: minMonsters.left
anchors.verticalCenter: prev.verticalCenter
width: 30
CheckBox
id: pvpSpell
anchors.left: minMonsters.right
width: 100
margin-left: 10
anchors.verticalCenter: minMonsters.verticalCenter
text: Spell for PVP
Button
id: addButton
anchors.horizontalCenter: pvpSpell.horizontalCenter
anchors.verticalCenter: minMana.verticalCenter
text-align: center
text: Add
margin-left: 5
margin-left: 5
size: 45 21
HorizontalSeparator
id: BottomSeparator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: CloseButton.top
margin-bottom: 8
Button
id: CloseButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,145 @@
BotServerData < Panel
size: 340 70
image-source: /images/ui/window
image-border: 6
padding: 3
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text-align: center
!text: tr("BotServer Data")
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
margin-top: 23
text-align: center
text: Channel Name:
margin-left: 6
TextEdit
id: Channel
anchors.top: parent.top
anchors.left: prev.right
margin-top: 20
width: 150
margin-left: 5
text-align: center
Button
id: Random
anchors.left: prev.right
anchors.top: prev.top
anchors.right: parent.right
text-align: center
text: Randomize
margin-left: 6
margin-right: 6
Label
id: label
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 6
margin-bottom: 4
text-align: center
text: Status:
BotLabel
id: ServerStatus
anchors.left: prev.right
anchors.bottom: parent.bottom
margin-left: 10
margin-bottom: 4
text-align: center
text: CONNECTED
BotLabel
id: Participants
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 8
margin-bottom: 4
text-align: center
Label
id: label
anchors.right: Participants.left
anchors.bottom: parent.bottom
margin-right: 10
margin-bottom: 4
text-align: center
text: Members:
FeaturePanel < Panel
size: 340 150
image-source: /images/ui/panel_flat
image-border: 5
padding: 3
Label
id: title
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text-align: center
text: Features
HorizontalSeparator
id: sep
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
BotSwitch
id: Feature1
anchors.top: prev.bottom
anchors.left: parent.left
margin-left: 3
margin-top: 5
text: Mana info
BotSwitch
id: Feature2
anchors.top: sep.bottom
anchors.left: prev.right
margin-top: 5
margin-left: 5
text: MWall info
BotServerWindow < MainWindow
!text: tr('BotServer')
size: 370 310
@onEscape: self:hide()
BotServerData
id: Data
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
FeaturePanel
id: Features
anchors.top: prev.bottom
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 10
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,76 @@
setDefaultTab("Tools")
UI.Label("-- [[ ANTI PUSH Panel ]] --")
addSeparator()
local panelName = "castle"
local ui = setupUI([[
Panel
height: 40
BotItem
id: item
anchors.top: parent.top
anchors.left: parent.left
margin-top: 2
BotSwitch
id: skip
anchors.top: parent.top
anchors.left: item.right
anchors.right: parent.right
anchors.bottom: item.verticalCenter
text-align: center
!text: tr('Skip Tiles Near Target')
margin-left: 2
BotSwitch
id: title
anchors.top: item.verticalCenter
anchors.left: item.right
anchors.right: parent.right
anchors.bottom: item.bottom
text-align: center
!text: tr('Drop Items Around')
margin-left: 2
]], parent)
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {
id = 2983,
around = false,
enabled = false
}
end
ui.skip:setOn(storage[panelName].around)
ui.skip.onClick = function(widget)
storage[panelName].around = not storage[panelName].around
widget:setOn(storage[panelName].around)
end
ui.title:setOn(storage[panelName].enabled)
ui.title.onClick = function(widget)
storage[panelName].enabled = not storage[panelName].enabled
widget:setOn(storage[panelName].enabled)
end
ui.item:setItemId(storage[panelName].id)
ui.item.onItemChange = function(widget)
storage[panelName].id = widget:getItemId()
end
macro(175, function()
if storage[panelName].enabled then
local blockItem = findItem(storage[panelName].id)
for _, tile in pairs(g_map.getTiles(posz())) do
if distanceFromPlayer(tile:getPosition()) == 1 and tile:isWalkable() and tile:getTopUseThing():getId() ~= storage[panelName].id and (not storage[panelName].around or not target() or (target() and getDistanceBetween(targetPos(), tile:getPosition() > 1))) then
g_game.move(blockItem, tile:getPosition())
return
end
end
storage[panelName].enabled = false
ui.title:setOn(storage[panelName].enabled)
end
end)
addSeparator()

View File

@ -0,0 +1,412 @@
UturaComboBoxPopupMenu < ComboBoxPopupMenu
UturaComboBoxPopupMenuButton < ComboBoxPopupMenuButton
UturaComboBox < ComboBox
@onSetup: |
self:addOption("Utura")
self:addOption("Utura Gran")
CureConditions < Panel
id: Cure
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 200 180
Label
id: label1
anchors.top: parent.top
anchors.left: parent.left
margin-top: 10
margin-left: 5
text: Poison
color: #ffaa00
Label
id: label11
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 40
text: Mana:
TextEdit
id: PoisonCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CurePoison
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 10
text: Curse
color: #ffaa00
Label
id: label22
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 44
text: Mana:
TextEdit
id: CurseCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CureCurse
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label3
anchors.left: label2.left
anchors.top: label2.bottom
margin-top: 10
text: Bleed
color: #ffaa00
Label
id: label33
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 46
text: Mana:
TextEdit
id: BleedCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CureBleed
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 10
text: Burn
color: #ffaa00
Label
id: label44
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 50
text: Mana:
TextEdit
id: BurnCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CureBurn
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label5
anchors.left: label4.left
anchors.top: label4.bottom
margin-top: 10
text: Electify
color: #ffaa00
Label
id: label55
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 33
text: Mana:
TextEdit
id: ElectrifyCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CureElectrify
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label6
anchors.left: label5.left
anchors.top: label5.bottom
margin-top: 10
text: Paralyse
color: #ffaa00
Label
id: label66
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 26
text: Mana:
TextEdit
id: ParalyseCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: CureParalyse
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label7
anchors.left: label6.left
anchors.top: label6.bottom
margin-top: 10
margin-left: 12
text: Spell:
TextEdit
id: ParalyseSpell
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
HoldConditions < Panel
id: Hold
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 200 180
Label
id: label1
anchors.top: parent.top
anchors.left: parent.left
margin-top: 10
margin-left: 5
text: Haste
color: #ffaa00
Label
id: label11
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 44
text: Mana:
TextEdit
id: HasteCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: HoldHaste
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 10
margin-left: 12
text: Spell:
TextEdit
id: HasteSpell
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
Label
id: label3
anchors.left: label1.left
anchors.top: label2.bottom
margin-top: 10
text: Utana Vid
color: #ffaa00
Label
id: label33
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 21
text: Mana:
TextEdit
id: UtanaCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: HoldUtana
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 10
text: Utamo Vita
color: #ffaa00
Label
id: label44
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 12
text: Mana:
TextEdit
id: UtamoCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: HoldUtamo
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label5
anchors.left: label4.left
anchors.top: label4.bottom
margin-top: 10
text: Recovery
color: #ffaa00
Label
id: label55
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 20
text: Mana:
TextEdit
id: UturaCost
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 3
width: 40
CheckBox
id: HoldUtura
anchors.verticalCenter: prev.verticalCenter
anchors.right: parent.right
margin-right: 10
Label
id: label6
anchors.left: label5.left
anchors.top: label5.bottom
margin-top: 10
margin-left: 12
text: Spell:
UturaComboBox
id: UturaType
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
margin-left: 10
width: 100
CheckBox
id: IgnoreInPz
anchors.left: label5.left
anchors.top: label6.bottom
margin-top: 15
Label
id: label
anchors.verticalCenter: IgnoreInPz.verticalCenter
anchors.left: prev.right
margin-top: 3
margin-left: 5
text: Don't Cast in Protection Zones
font: cipsoftFont
ConditionsWindow < MainWindow
!text: tr('Condition Manager')
size: 445 270
@onEscape: self:hide()
CureConditions
id: Cure
anchors.top: parent.top
anchors.left: parent.left
margin-top: 7
Label
id: label
anchors.top: parent.top
anchors.left: parent.left
text: Cure Conditions
color: #88e3dd
margin-left: 10
HoldConditions
id: Hold
anchors.top: parent.top
anchors.right: parent.right
margin-top: 7
Label
id: label
anchors.top: parent.top
anchors.right: parent.right
text: Hold Conditions
color: #88e3dd
margin-right: 100
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,306 @@
SpellSourceBoxPopupMenu < ComboBoxPopupMenu
SpellSourceBoxPopupMenuButton < ComboBoxPopupMenuButton
SpellSourceBox < ComboBox
@onSetup: |
self:addOption("Current Mana")
self:addOption("Current Health")
self:addOption("Mana Percent")
self:addOption("Health Percent")
SpellConditionBoxPopupMenu < ComboBoxPopupMenu
SpellConditionBoxPopupMenuButton < ComboBoxPopupMenuButton
SpellConditionBox < ComboBox
@onSetup: |
self:addOption("Below")
self:addOption("Above")
self:addOption("Equal To")
SpellEntry < Label
background-color: alpha
text-offset: 2 0
focusable: true
height: 16
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
width: 15
height: 15
ItemEntry < Label
background-color: alpha
text-offset: 2 0
focusable: true
height: 16
$focus:
background-color: #00000055
Button
id: remove
!text: tr('x')
anchors.right: parent.right
margin-right: 15
width: 15
height: 15
SpellHealing < Panel
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 460 130
Label
id: whenSpell
anchors.left: spellList.right
anchors.top: parent.top
text: When
margin-top: 10
margin-left: 7
SpellSourceBox
id: spellSource
anchors.top: parent.top
anchors.left: whenSpell.right
margin-top: 5
margin-left: 35
width: 128
Label
id: isSpell
anchors.left: spellList.right
anchors.top: whenSpell.bottom
text: Is
margin-top: 9
margin-left: 7
SpellConditionBox
id: spellCondition
anchors.left: spellSource.left
anchors.top: spellSource.bottom
marin-top: 15
width: 80
TextEdit
id: spellValue
anchors.left: spellCondition.right
anchors.top: spellCondition.top
anchors.bottom: spellCondition.bottom
width: 49
Label
id: castSpell
anchors.left: isSpell.left
anchors.top: isSpell.bottom
text: Cast
margin-top: 9
TextEdit
id: spellFormula
anchors.left: spellCondition.left
anchors.top: spellCondition.bottom
anchors.right: spellValue.right
Label
id: manaSpell
anchors.left: castSpell.left
anchors.top: castSpell.bottom
text: Mana Cost:
margin-top: 8
TextEdit
id: manaCost
anchors.left: spellFormula.left
anchors.top: spellFormula.bottom
width: 40
TextList
id: spellList
anchors.left: parent.left
anchors.bottom: parent.bottom
padding: 1
size: 240 116
margin-bottom: 3
margin-left: 3
vertical-scrollbar: spellListScrollBar
VerticalScrollBar
id: spellListScrollBar
anchors.top: spellList.top
anchors.bottom: spellList.bottom
anchors.right: spellList.right
step: 14
pixels-scroll: true
Button
id: addSpell
anchors.right: spellFormula.right
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 10
text: Add
size: 40 17
font: cipsoftFont
Button
id: MoveUp
anchors.right: prev.left
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 5
text: Move Up
size: 55 17
font: cipsoftFont
Button
id: MoveDown
anchors.right: prev.left
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 5
text: Move Down
size: 55 17
font: cipsoftFont
ItemHealing < Panel
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 460 130
Label
id: whenItem
anchors.left: itemList.right
anchors.top: parent.top
text: When
margin-top: 10
margin-left: 7
SpellSourceBox
id: itemSource
anchors.top: parent.top
anchors.left: whenItem.right
margin-top: 5
margin-left: 35
width: 128
Label
id: isItem
anchors.left: itemList.right
anchors.top: whenItem.bottom
text: Is
margin-top: 9
margin-left: 7
SpellConditionBox
id: itemCondition
anchors.left: itemSource.left
anchors.top: itemSource.bottom
marin-top: 15
width: 80
TextEdit
id: itemValue
anchors.left: itemCondition.right
anchors.top: itemCondition.top
anchors.bottom: itemCondition.bottom
width: 49
Label
id: useItem
anchors.left: isItem.left
anchors.top: isItem.bottom
text: Use
margin-top: 15
BotItem
id: itemId
anchors.left: itemCondition.left
anchors.top: itemCondition.bottom
TextList
id: itemList
anchors.left: parent.left
anchors.bottom: parent.bottom
padding: 1
size: 240 116
margin-top: 3
margin-bottom: 3
margin-left: 3
vertical-scrollbar: itemListScrollBar
VerticalScrollBar
id: itemListScrollBar
anchors.top: itemList.top
anchors.bottom: itemList.bottom
anchors.right: itemList.right
step: 14
pixels-scroll: true
Button
id: addItem
anchors.right: itemValue.right
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 10
text: Add
size: 40 17
font: cipsoftFont
Button
id: MoveUp
anchors.right: prev.left
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 5
text: Move Up
size: 55 17
font: cipsoftFont
Button
id: MoveDown
anchors.right: prev.left
anchors.bottom: parent.bottom
margin-bottom: 2
margin-right: 5
text: Move Down
size: 55 17
font: cipsoftFont
HealWindow < MainWindow
!text: tr('Self Healer')
size: 490 350
@onEscape: self:hide()
SpellHealing
id: spells
anchors.top: parent.top
anchors.left: parent.left
ItemHealing
id: items
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 10
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,16 @@
local vocation = player:getVocation()
local vocText = nil
if vocation == 1 or vocation == 11 then
vocText = "EK"
elseif vocation == 2 or vocation == 12 then
vocText = "RP"
elseif vocation == 3 or vocation == 13 then
vocText = "MS"
elseif vocation == 4 or vocation == 14 then
vocText = "ED"
end
macro(10000, function()
g_window.setTitle("Tibia - " .. player:getName() .. " - " .. lvl() .. "lvl - " .. vocText)
end, batTab)

View File

@ -0,0 +1,105 @@
AlarmsWindow < MainWindow
!text: tr('Alarms')
size: 270 200
@onEscape: self:hide()
BotSwitch
id: playerAttack
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
text: Player Attack
BotSwitch
id: playerDetected
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 4
text-align: center
text: Player Detected
CheckBox
id: playerDetectedLogout
anchors.top: playerDetected.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-top: 3
margin-left: 4
text: Logout
BotSwitch
id: creatureDetected
anchors.left: parent.left
anchors.right: parent.right
anchors.top: playerDetected.bottom
margin-top: 4
text-align: center
text: Creature Detected
BotSwitch
id: healthBelow
anchors.left: parent.left
anchors.top: prev.bottom
anchors.right: parent.horizontalCenter
text-align: center
margin-top: 4
text: Health < 50%
HorizontalScrollBar
id: healthValue
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: healthBelow.top
margin-left: 3
margin-top: 2
minimum: 1
maximum: 100
step: 1
BotSwitch
id: manaBelow
anchors.left: parent.left
anchors.top: healthBelow.bottom
anchors.right: parent.horizontalCenter
text-align: center
margin-top: 4
text: Mana < 50%
HorizontalScrollBar
id: manaValue
anchors.left: parent.horizontalCenter
anchors.right: parent.right
anchors.top: manaBelow.top
margin-left: 3
margin-top: 2
minimum: 1
maximum: 100
step: 1
BotSwitch
id: privateMessage
anchors.left: parent.left
anchors.top: manaBelow.bottom
anchors.right: parent.right
text-align: center
margin-top: 4
text: Private Message
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5

View File

@ -0,0 +1,375 @@
local analyserPanelName = "AnalysersPanel"
local ui = setupUI([[
Panel
height: 18
Button
id: analyzersMain
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 18
text: Hunt Analysers
]], parent)
ui:setId(analyserPanelName)
if not storage[analyserPanelName] then
storage[analyserPanelName] = {
bestHit = 0,
bestHeal = 0,
lootItems = {}
}
end
analyzersWindow = g_ui.createWidget('MainAnalyzer', modules.game_interface.getRightPanel())
huntWindow = g_ui.createWidget('HuntAnalyser', modules.game_interface.getRightPanel())
impactWindow = g_ui.createWidget('ImpactAnalyser', modules.game_interface.getRightPanel())
lootWindow = g_ui.createWidget('LootAnalyser', modules.game_interface.getRightPanel())
xpWindow = g_ui.createWidget('XpAnalyser', modules.game_interface.getRightPanel())
analyzersWindow:setup()
huntWindow:setup()
impactWindow:setup()
lootWindow:setup()
xpWindow:setup()
rootWidget = g_ui.getRootWidget()
lootListWindow = g_ui.createWidget('LootWindow', rootWidget)
lootListWindow:hide()
function refresh()
analyzersWindow:setContentMinimumHeight(105)
analyzersWindow:setContentMaximumHeight(105)
huntWindow:setContentMinimumHeight(30)
impactWindow:setContentMinimumHeight(30)
impactWindow:setContentMaximumHeight(185)
lootWindow:setContentMinimumHeight(30)
xpWindow:setContentMinimumHeight(30)
xpWindow:setContentMaximumHeight(65)
end
refresh()
function huntWindowToggle()
if huntWindow:isVisible() then
huntWindow:close()
else
huntWindow:open()
end
end
function impactWindowToggle()
if impactWindow:isVisible() then
impactWindow:close()
else
impactWindow:open()
end
end
function lootWindowToggle()
if lootWindow:isVisible() then
lootWindow:close()
else
lootWindow:open()
end
end
function xpWindowToggle()
if xpWindow:isVisible() then
xpWindow:close()
else
xpWindow:open()
end
end
ui.analyzersMain.onClick = function(widget)
if analyzersWindow:isVisible() then
analyzersWindow:close()
else
analyzersWindow:open()
end
end
lootWindow.contentsPanel.LootEdit.onClick = function(widget)
lootListWindow:show()
lootListWindow:raise()
lootListWindow:focus()
end
if storage[analyserPanelName].lootItems and #storage[analyserPanelName].lootItems > 0 then
for _, name in ipairs(storage[analyserPanelName].lootItems) do
local label = g_ui.createWidget("LootItemName", lootListWindow.LootList)
label.remove.onClick = function(widget)
table.removevalue(storage[analyserPanelName].lootItems, label:getText())
label:destroy()
end
label:setText(name)
end
end
lootListWindow.AddLoot.onClick = function(widget)
local lootName = lootListWindow.LootName:getText()
if lootName:len() > 0 and not table.contains(storage[analyserPanelName].lootItems, lootName, true) then
table.insert(storage[analyserPanelName].lootItems, lootName)
local label = g_ui.createWidget("LootItemName", lootListWindow.LootList)
label.remove.onClick = function(widget)
table.removevalue(storage[analyserPanelName].lootItems, label:getText())
label:destroy()
end
label:setText(lootName)
lootListWindow.LootName:setText('')
end
end
lootListWindow.closeButton.onClick = function(widget)
lootListWindow:hide()
end
analyzersWindow.contentsPanel.HuntButton.onClick = function(widget)
huntWindowToggle()
end
analyzersWindow.contentsPanel.lootSupplyButton.onClick = function(widget)
lootWindowToggle()
end
analyzersWindow.contentsPanel.impactButton.onClick = function(widget)
impactWindowToggle()
end
analyzersWindow.contentsPanel.xpButton.onClick = function(widget)
xpWindowToggle()
end
local uptime
local launchTime = now
local startTime = now
function sessionTime()
uptime = math.floor((now - launchTime)/1000)
local hours = string.format("%02.f", math.floor(uptime/3600))
local mins = string.format("%02.f", math.floor(uptime/60 - (hours*60)))
return hours .. ":" .. mins .. "h"
end
local startExp = exp()
function expGained()
return exp() - startExp
end
function expForLevel(level)
return math.floor((50*level*level*level)/3 - 100*level*level + (850*level)/3 - 200)
end
function expH()
return (expGained() / (now - startTime))
end
function format_thousand(v)
if not v then return 0 end
local s = string.format("%d", math.floor(v))
local pos = string.len(s) % 3
if pos == 0 then pos = 3 end
return string.sub(s, 1, pos)
.. string.gsub(string.sub(s, pos+1), "(...)", ".%1")
end
function checkExpSpeed()
local player = g_game.getLocalPlayer()
if not player then return end
local currentExp = player:getExperience()
local currentTime = now/1000
if player.lastExps ~= nil then
player.expSpeed = (currentExp - player.lastExps[1][1])/(currentTime - player.lastExps[1][2])
else
player.lastExps = {}
end
table.insert(player.lastExps, {currentExp, currentTime})
if #player.lastExps > 30 then
table.remove(player.lastExps, 1)
end
return player.expSpeed
end
function nextLevelData(time)
if checkExpSpeed() ~= nil then
expPerHour = math.floor(checkExpSpeed() * 3600)
if expPerHour > 0 then
nextLevelExp = expForLevel(player:getLevel()+1)
hoursLeft = (nextLevelExp - player:getExperience()) / expPerHour
minutesLeft = math.floor((hoursLeft - math.floor(hoursLeft))*60)
hoursLeft = math.floor(hoursLeft)
timeLeft = tostring(hoursLeft .. ":" .. minutesLeft .. "h")
end
end
if time then
return expPerHour
else
return timeLeft
end
end
function sum(t)
local sum = 0
for k,v in pairs(t) do
sum = sum + v
end
return sum
end
local cumulatedDamage = 0
local cumulatedHealing = 0
local allHits = {}
local allHeals = {}
local dps
local hps
local kills = {}
local droppedItems = {}
onTextMessage(function(mode, text)
-- [[ kill counter ]] --
if string.find(text, "Loot of") then
local split = string.split(text, ":")
local mobName = string.split(split[1], "of ")[2]:trim()
table.insert(kills, mobName)
local killCount = {}
for i, entry in pairs(kills) do
if killCount[entry] then
killCount[entry] = killCount[entry] + 1
else
killCount[entry] = 1
end
end
for i, child in pairs(huntWindow.contentsPanel.MessagePanel:getChildren()) do
child:destroy()
end
for k,v in pairs(killCount) do
local label = g_ui.createWidget("MonsterLabel", huntWindow.contentsPanel.MessagePanel)
label:setText(v .. "x " .. k)
end
-- [[ loot counter ]] --
local monsterDrop = string.split(split[2], ",")
if #monsterDrop > 0 then
for i=1,#monsterDrop do
local drop = monsterDrop[i]:trim()
for i, entry in pairs(storage[analyserPanelName].lootItems) do
if string.match(drop, entry) then
local entryCount
if tonumber(string.match(drop, "%d+")) then
entryCount = tonumber(string.match(drop, "%d+"))
else
entryCount = 1
end
if droppedItems[entry] then
droppedItems[entry] = droppedItems[entry] + entryCount
else
droppedItems[entry] = entryCount
end
end
end
end
end
for i, child in pairs(lootWindow.contentsPanel.MessagePanel:getChildren()) do
child:destroy()
end
for k,v in pairs(droppedItems) do
local label = g_ui.createWidget("MonsterLabel", lootWindow.contentsPanel.MessagePanel)
label:setText(v .. "x " .. k)
end
end
-- damage
if string.find(text, "hitpoints due to your attack") then
table.insert(allHits, tonumber(string.match(text, "%d+")))
if dps then
if now - startTime > 1000 then
local dmgSum = sum(allHits)
dps = dmgSum
allHits = {}
startTime = now
end
else
dps = 0
end
local dmgValue = tonumber(string.match(text, "%d+"))
cumulatedDamage = cumulatedDamage + dmgValue
if storage[analyserPanelName].bestHit < dmgValue then
storage[analyserPanelName].bestHit = dmgValue
end
end
-- healing
if string.find(text, "You heal") then
table.insert(allHeals, tonumber(string.match(text, "%d+")))
if hps then
if now - startTime > 1000 then
local healSum = sum(allHeals)
hps = healSum
allHeals = {}
startTime = now
end
else
hps = 0
end
local healValue = tonumber(string.match(text, "%d+"))
cumulatedHealing = cumulatedHealing + healValue
if storage[analyserPanelName].bestHeal < healValue then
storage[analyserPanelName].bestHeal = healValue
end
end
-- [[ waste]] --
if string.find(text, "Using one of") then
local splitTwo = string.split(text, "Using one of")
if #splitTwo == 1 then
local itemAmount = string.match(splitTwo[1], "%d+")
end
end
end)
function hourVal(v)
if not v then return end
return (v/uptime)*3600
end
function expH()
return (expGained()/uptime)*3600
end
local lootWorth
macro(1000, function()
-- [[ profit ]] --
lootWorth = 0
for k, v in pairs(droppedItems) do
if lootitems[k] then
lootWorth = lootWorth + (lootitems[k]*v)
end
end
-- [[ Hunt Window ]] --
huntWindow.contentsPanel.sessionValue:setText(sessionTime())
huntWindow.contentsPanel.xpGainValue:setText(format_thousand(expGained()))
huntWindow.contentsPanel.xpHourValue:setText(format_thousand(expH()))
if cumulatedDamage then huntWindow.contentsPanel.damageValue:setText(format_thousand(cumulatedDamage)) end
if cumulatedHealing then huntWindow.contentsPanel.healingValue:setText(format_thousand(cumulatedHealing)) end
huntWindow.contentsPanel.damageHourValue:setText(format_thousand(hourVal(cumulatedDamage)))
huntWindow.contentsPanel.healingHourValue:setText(format_thousand(hourVal(cumulatedHealing)))
huntWindow.contentsPanel.lootValue:setText(format_thousand(lootWorth))
-- [[ XP Window ]] --
xpWindow.contentsPanel.xpValue:setText(format_thousand(expGained()))
xpWindow.contentsPanel.hourValue:setText(format_thousand(expH()))
if not nextLevelData() then xpWindow.contentsPanel.ttnlValue:setText("-") else xpWindow.contentsPanel.ttnlValue:setText(nextLevelData()) end
-- [[ Impact Window ]] --
if cumulatedDamage then impactWindow.contentsPanel.damageValue:setText(format_thousand(cumulatedDamage)) end
if dps then impactWindow.contentsPanel.maxDpsValue:setText(format_thousand(dps)) end
impactWindow.contentsPanel.allTimeHighValue:setText(format_thousand(storage[analyserPanelName].bestHit))
if cumulatedHealing then impactWindow.contentsPanel.healingValue:setText(format_thousand(cumulatedHealing)) end
if hps then impactWindow.contentsPanel.maxHpsValue:setText(format_thousand(hps)) end
impactWindow.contentsPanel.allTimeHighHealValue:setText(format_thousand(storage[analyserPanelName].bestHeal))
-- [[ Loot Window ]] --
lootWindow.contentsPanel.lootValue:setText(format_thousand(lootWorth))
lootWindow.contentsPanel.lootHourValue:setText(format_thousand(hourVal(lootWorth)))
end)

View File

@ -0,0 +1,47 @@
MainAnalyzer < MiniWindow
!text: tr('Analytics Selector')
height: 125
icon: /images/topbuttons/analyzers
&autoOpen: false
MiniWindowContents
Button
id: HuntButton
!text: tr('Hunting Session Analyser')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 3
margin-left: 3
margin-right: 3
Button
id: lootSupplyButton
!text: tr('Loot & Supplies Analyser')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Button
id: impactButton
!text: tr('Impact Analyser')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Button
id: xpButton
!text: tr('XP Analyser')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3

View File

@ -0,0 +1,3 @@
macro(60000, function()
turn(math.random(0,3))
end)

View File

@ -0,0 +1,76 @@
AntiPush = function(parent)
if not parent then
parent = panel
end
local panelName = "antiPushPanel"
local ui = g_ui.createWidget("ItemsPanel", parent)
ui:setId(panelName)
if not storage[panelName] then
storage[panelName] = {}
end
ui.title:setText("Anti-Push Items")
ui.title:setOn(storage[panelName].enabled)
ui.title.onClick = function(widget)
storage[panelName].enabled = not storage[panelName].enabled
widget:setOn(storage[panelName].enabled)
end
if type(storage[panelName].items) ~= 'table' then
storage[panelName].items = {3031, 3035, 0, 0, 0}
end
for i=1,5 do
ui.items:getChildByIndex(i).onItemChange = function(widget)
storage[panelName].items[i] = widget:getItemId()
end
ui.items:getChildByIndex(i):setItemId(storage[panelName].items[i])
end
macro(100, function()
if not storage[panelName].enabled then
return
end
local tile = g_map.getTile(player:getPosition())
if not tile then
return
end
local topItem = tile:getTopUseThing()
if topItem and topItem:isStackable() then
topItem = topItem:getId()
else
topItem = 0
end
local candidates = {}
for i, item in pairs(storage[panelName].items) do
if item >= 100 and item ~= topItem and findItem(item) then
table.insert(candidates, item)
end
end
if #candidates == 0 then
return
end
if type(storage[panelName].lastItem) ~= 'number' or storage[panelName].lastItem > #candidates then
storage[panelName].lastItem = 1
end
local item = findItem(candidates[storage[panelName].lastItem])
g_game.move(item, player:getPosition(), 1)
storage[panelName].lastItem = storage[panelName].lastItem + 1
end)
macro(175, "Pull Nearby Items", function()
local trashitem = nil
for _, tile in pairs(g_map.getTiles(posz())) do
if distanceFromPlayer(tile:getPosition()) == 1 and #tile:getItems() ~= 0 and not tile:getTopUseThing():isNotMoveable() then
trashitem = tile:getTopUseThing()
g_game.move(trashitem, pos(), trashitem:getCount())
return
end
end
end)
end
AntiPush(setDefaultTab("Tools"))
addSeparator()

View File

@ -0,0 +1,8 @@
if player:getBlessings() == 0 then
say("!bless")
schedule(2000, function()
if player:getBlessings() == 0 then
error("!! Blessings not bought !!")
end
end)
end

View File

@ -0,0 +1,315 @@
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 error("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 error("Invalid cavebot action: " .. action)
end
if not widget.action or not widget.value then
return error("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 error("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("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() error('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
error("Error 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
error("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:canShoot() and findPath(playerPos, spec:getPosition(), 20, {ignoreNonPathable = true, precision = 1}) then
table.insert(monsters, {mob = spec, dist = getDistanceBetween(pos, spec:getPosition())})
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(10000, 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
error("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
error("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)

View File

@ -0,0 +1,72 @@
CaveBot.Extensions.Bank = {}
CaveBot.Extensions.Bank.setup = function()
CaveBot.registerAction("bank", "#00FFFF", function(value, retries)
local data = string.split(value, ",")
local waitVal = 300
local amount = 0
local actionType
local npcName
if #data ~= 3 and #data ~= 2 then
warn("CaveBot[Bank]: incorrect value!")
return false
else
actionType = data[1]:trim():lower()
npcName = data[2]:trim()
if #data == 3 then
amount = tonumber(data[3]:trim())
end
end
if actionType ~= "withdraw" and actionType ~= "deposit" then
warn("CaveBot[Bank]: incorrect action type! should be withdraw/deposit, is: " .. actionType)
return false
elseif actionType == "withdraw" then
local value = tonumber(amount)
if not value then
warn("CaveBot[Bank]: incorrect amount value! should be number, is: " .. amount)
return false
end
end
if retries > 5 then
print("CaveBot[Bank]: too many tries, skipping")
return false
end
local npc = getCreatureByName(npcName)
if not npc then
print("CaveBot[Bank]: NPC not found, skipping")
return false
end
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end
if actionType == "deposit" then
NPC.say("hi")
schedule(waitVal, function() NPC.say("deposit all") end)
schedule(waitVal*2, function() NPC.say("yes") end)
CaveBot.delay(waitVal*3)
return true
else
NPC.say("hi")
schedule(waitVal, function() NPC.say("withdraw") end)
schedule(waitVal*2, function() NPC.say(value) end)
schedule(waitVal*3, function() NPC.say("yes") end)
CaveBot.delay(waitVal*4)
return true
end
end)
CaveBot.Editor.registerAction("bank", "bank", {
value="action, NPC name",
title="Banker",
description="action type(withdraw/deposit), NPC name, if withdraw: amount",
})
end

View File

@ -0,0 +1,116 @@
CaveBot.Extensions.BuySupplies = {}
storage.buySuppliesCap = 0
CaveBot.Extensions.BuySupplies.setup = function()
CaveBot.registerAction("BuySupplies", "#00FFFF", function(value, retries)
local item1Count = 0
local item2Count = 0
local item3Count = 0
local item4Count = 0
local item5Count = 0
local val = string.split(value, ",")
local waitVal
if #val == 0 or #val > 2 then
warn("CaveBot[BuySupplies]: incorrect BuySupplies value")
return false
elseif #val == 2 then
waitVal = tonumber(val[2]:trim())
end
local npc = getCreatureByName(val[1]:trim())
if not npc then
print("CaveBot[BuySupplies]: NPC not found")
return false
end
if not waitVal and #val == 2 then
warn("CaveBot[BuySupplies]: incorrect delay values!")
elseif waitVal and #val == 2 then
delay(waitVal)
end
if retries > 30 then
print("CaveBot[BuySupplies]: Too many tries, can't buy")
return false
end
if freecap() == storage.buySuppliesCap then
storage.buySuppliesCap = 0
print("CaveBot[BuySupplies]: Bought Everything, proceeding")
return true
end
delay(800)
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end
for _, container in pairs(getContainers()) do
for _, item in ipairs(container:getItems()) do
if (storage[suppliesPanelName].item1 > 100) and (item:getId() == storage[suppliesPanelName].item1) then
item1Count = item1Count + item:getCount()
end
if (storage[suppliesPanelName].item2 > 100) and (item:getId() == storage[suppliesPanelName].item2) then
item2Count = item2Count + item:getCount()
end
if (storage[suppliesPanelName].item3 > 100) and (item:getId() == storage[suppliesPanelName].item3) then
item3Count = item3Count + item:getCount()
end
if (storage[suppliesPanelName].item4 > 100) and (item:getId() == storage[suppliesPanelName].item4) then
item4Count = item4Count + item:getCount()
end
if (storage[suppliesPanelName].item5 > 100) and (item:getId() == storage[suppliesPanelName].item5) then
item5Count = item5Count + item:getCount()
end
end
end
local itemList = {
item1 = {ID = storage[suppliesPanelName].item1, maxAmount = storage[suppliesPanelName].item1Max, currentAmount = item1Count},
item2 = {ID = storage[suppliesPanelName].item2, maxAmount = storage[suppliesPanelName].item2Max, currentAmount = item2Count},
item3 = {ID = storage[suppliesPanelName].item3, maxAmount = storage[suppliesPanelName].item3Max, currentAmount = item3Count},
item4 = {ID = storage[suppliesPanelName].item4, maxAmount = storage[suppliesPanelName].item4Max, currentAmount = item4Count},
item5 = {ID = storage[suppliesPanelName].item5, maxAmount = storage[suppliesPanelName].item5Max, currentAmount = item5Count}
}
if not NPC.isTrading() then
NPC.say("hi")
schedule(500, function() NPC.say("trade") end)
else
storage.buySuppliesCap = freecap()
end
for i, item in pairs(itemList) do
if item["ID"] > 100 then
local amountToBuy = item["maxAmount"] - item["currentAmount"]
if amountToBuy > 100 then
for i=1, math.ceil(amountToBuy/100), 1 do
NPC.buy(item["ID"], math.min(100, amountToBuy))
amountToBuy = amountToBuy - math.min(100, amountToBuy)
print("CaveBot[BuySupplies]: bought " .. amountToBuy .. "x " .. item["ID"])
return "retry"
end
else
if amountToBuy > 0 then
NPC.buy(item["ID"], amountToBuy)
print("CaveBot[BuySupplies]: bought " .. amountToBuy .. "x " .. item["ID"])
return "retry"
end
end
end
end
return "retry"
end)
CaveBot.Editor.registerAction("buysupplies", "buy supplies", {
value="NPC name",
title="Buy Supplies",
description="NPC Name, delay(in ms, optional)",
})
end

View File

@ -0,0 +1,224 @@
local cavebotMacro = nil
local config = nil
-- ui
local configWidget = UI.Config()
local ui = UI.createWidget("CaveBotPanel")
ui.list = ui.listPanel.list -- shortcut
CaveBot.actionList = ui.list
if CaveBot.Editor then
CaveBot.Editor.setup()
end
if CaveBot.Config then
CaveBot.Config.setup()
end
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.setup then
callbacks.setup()
end
end
-- main loop, controlled by config
local actionRetries = 0
local prevActionResult = true
cavebotMacro = macro(20, function()
if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then
CaveBot.resetWalking()
return -- target bot or looting is working, wait
end
if CaveBot.doWalking() then
return -- executing walking
end
local actions = ui.list:getChildCount()
if actions == 0 then return end
local currentAction = ui.list:getFocusedChild()
if not currentAction then
currentAction = ui.list:getFirstChild()
end
local action = CaveBot.Actions[currentAction.action]
local value = currentAction.value
local retry = false
if action then
local status, result = pcall(function()
CaveBot.resetWalking()
return action.callback(value, actionRetries, prevActionResult)
end)
if status then
if result == "retry" then
actionRetries = actionRetries + 1
retry = true
elseif type(result) == 'boolean' then
actionRetries = 0
prevActionResult = result
else
error("Invalid return from cavebot action (" .. currentAction.action .. "), should be \"retry\", false or true, is: " .. tostring(result))
end
else
error("Error while executing cavebot action (" .. currentAction.action .. "):\n" .. result)
end
else
error("Invalid cavebot action: " .. currentAction.action)
end
if retry then
return
end
if currentAction ~= ui.list:getFocusedChild() then
-- focused child can change durring action, get it again and reset state
currentAction = ui.list:getFocusedChild() or ui.list:getFirstChild()
actionRetries = 0
prevActionResult = true
end
local nextAction = ui.list:getChildIndex(currentAction) + 1
if nextAction > actions then
nextAction = 1
end
ui.list:focusChild(ui.list:getChildByIndex(nextAction))
end)
-- config, its callback is called immediately, data can be nil
local lastConfig = ""
config = Config.setup("cavebot_configs", configWidget, "cfg", function(name, enabled, data)
if enabled and CaveBot.Recorder.isOn() then
CaveBot.Recorder.disable()
CaveBot.setOff()
return
end
local currentActionIndex = ui.list:getChildIndex(ui.list:getFocusedChild())
ui.list:destroyChildren()
if not data then return cavebotMacro.setOff() end
local cavebotConfig = nil
for k,v in ipairs(data) do
if type(v) == "table" and #v == 2 then
if v[1] == "config" then
local status, result = pcall(function()
return json.decode(v[2])
end)
if not status then
error("Error while parsing CaveBot extensions from config:\n" .. result)
else
cavebotConfig = result
end
elseif v[1] == "extensions" then
local status, result = pcall(function()
return json.decode(v[2])
end)
if not status then
error("Error while parsing CaveBot extensions from config:\n" .. result)
else
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.onConfigChange then
callbacks.onConfigChange(name, enabled, result[extension])
end
end
end
else
CaveBot.addAction(v[1], v[2])
end
end
end
CaveBot.Config.onConfigChange(name, enabled, cavebotConfig)
actionRetries = 0
CaveBot.resetWalking()
prevActionResult = true
cavebotMacro.setOn(enabled)
cavebotMacro.delay = nil
if lastConfig == name then
-- restore focused child on the action list
ui.list:focusChild(ui.list:getChildByIndex(currentActionIndex))
end
lastConfig = name
end)
-- ui callbacks
ui.showEditor.onClick = function()
if not CaveBot.Editor then return end
if ui.showEditor:isOn() then
CaveBot.Editor.hide()
ui.showEditor:setOn(false)
else
CaveBot.Editor.show()
ui.showEditor:setOn(true)
end
end
ui.showConfig.onClick = function()
if not CaveBot.Config then return end
if ui.showConfig:isOn() then
CaveBot.Config.hide()
ui.showConfig:setOn(false)
else
CaveBot.Config.show()
ui.showConfig:setOn(true)
end
end
-- public function, you can use them in your scripts
CaveBot.isOn = function()
return config.isOn()
end
CaveBot.isOff = function()
return config.isOff()
end
CaveBot.setOn = function(val)
if val == false then
return CaveBot.setOff(true)
end
config.setOn()
end
CaveBot.setOff = function(val)
if val == false then
return CaveBot.setOn(true)
end
config.setOff()
end
CaveBot.delay = function(value)
cavebotMacro.delay = math.max(cavebotMacro.delay or 0, now + value)
end
CaveBot.gotoLabel = function(label)
label = label:lower()
for index, child in ipairs(ui.list:getChildren()) do
if child.action == "label" and child.value:lower() == label then
ui.list:focusChild(child)
return true
end
end
return false
end
CaveBot.save = function()
local data = {}
for index, child in ipairs(ui.list:getChildren()) do
table.insert(data, {child.action, child.value})
end
if CaveBot.Config then
table.insert(data, {"config", json.encode(CaveBot.Config.save())})
end
local extension_data = {}
for extension, callbacks in pairs(CaveBot.Extensions) do
if callbacks.onSave then
local ext_data = callbacks.onSave()
if type(ext_data) == "table" then
extension_data[extension] = ext_data
end
end
end
table.insert(data, {"extensions", json.encode(extension_data, 2)})
config.save(data)
end

View File

@ -0,0 +1,58 @@
CaveBotAction < Label
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #00000055
CaveBotPanel < Panel
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 2
margin-bottom: 5
Panel
id: listPanel
height: 100
margin-top: 2
TextList
id: list
anchors.fill: parent
vertical-scrollbar: listScrollbar
margin-right: 15
focusable: false
auto-focus: first
VerticalScrollBar
id: listScrollbar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
pixels-scroll: true
step: 10
BotSwitch
id: showEditor
margin-top: 2
$on:
text: Hide waypoints editor
$!on:
text: Show waypoints editor
BotSwitch
id: showConfig
margin-top: 2
$on:
text: Hide config
$!on:
text: Show config

View File

@ -0,0 +1,94 @@
-- config for bot
CaveBot.Config = {}
CaveBot.Config.values = {}
CaveBot.Config.default_values = {}
CaveBot.Config.value_setters = {}
CaveBot.Config.setup = function()
CaveBot.Config.ui = UI.createWidget("CaveBotConfigPanel")
local ui = CaveBot.Config.ui
local add = CaveBot.Config.add
add("ping", "Server ping", 100)
add("walkDelay", "Walk delay", 10)
add("mapClick", "Use map click", false)
add("mapClickDelay", "Map click delay", 100)
add("ignoreFields", "Ignore fields", false)
add("skipBlocked", "Skip blocked path", false)
add("useDelay", "Delay after use", 400)
end
CaveBot.Config.show = function()
CaveBot.Config.ui:show()
end
CaveBot.Config.hide = function()
CaveBot.Config.ui:hide()
end
CaveBot.Config.onConfigChange = function(configName, isEnabled, configData)
for k, v in pairs(CaveBot.Config.default_values) do
CaveBot.Config.value_setters[k](v)
end
if not configData then return end
for k, v in pairs(configData) do
if CaveBot.Config.value_setters[k] then
CaveBot.Config.value_setters[k](v)
end
end
end
CaveBot.Config.save = function()
return CaveBot.Config.values
end
CaveBot.Config.add = function(id, title, defaultValue)
if CaveBot.Config.values[id] then
return error("Duplicated config key: " .. id)
end
local panel
local setter -- sets value
if type(defaultValue) == "number" then
panel = UI.createWidget("CaveBotConfigNumberValuePanel", CaveBot.Config.ui)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setText(value, true)
end
setter(defaultValue)
panel.value.onTextChange = function(widget, newValue)
newValue = tonumber(newValue)
if newValue then
CaveBot.Config.values[id] = newValue
CaveBot.save()
end
end
elseif type(defaultValue) == "boolean" then
panel = UI.createWidget("CaveBotConfigBooleanValuePanel", CaveBot.Config.ui)
setter = function(value)
CaveBot.Config.values[id] = value
panel.value:setOn(value, true)
end
setter(defaultValue)
panel.value.onClick = function(widget)
widget:setOn(not widget:isOn())
CaveBot.Config.values[id] = widget:isOn()
CaveBot.save()
end
else
return error("Invalid default value of config for key " .. id .. ", should be number or boolean")
end
panel.title:setText(tr(title) .. ":")
CaveBot.Config.value_setters[id] = setter
CaveBot.Config.values[id] = defaultValue
CaveBot.Config.default_values[id] = defaultValue
end
CaveBot.Config.get = function(id)
if CaveBot.Config.values[id] == nil then
return error("Invalid CaveBot.Config.get, id: " .. id)
end
return CaveBot.Config.values[id]
end

View File

@ -0,0 +1,57 @@
CaveBotConfigPanel < Panel
id: cavebotEditor
visible: false
layout:
type: verticalBox
fit-children: true
HorizontalSeparator
margin-top: 5
Label
text-align: center
text: CaveBot Config
margin-top: 5
CaveBotConfigNumberValuePanel < Panel
height: 20
margin-top: 5
BotTextEdit
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
CaveBotConfigBooleanValuePanel < Panel
height: 20
margin-top: 5
BotSwitch
id: value
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 5
width: 50
$on:
text: On
$!on:
text: Off
Label
id: title
anchors.left: parent.left
anchors.verticalCenter: prev.verticalCenter
margin-left: 5

View File

@ -0,0 +1,179 @@
CaveBot.Extensions.DWithdraw = {}
comparePosition = function(pPos, tPos)
return (getDistanceBetween(pPos, tPos) <= 1)
end
local depotIDs = {3497, 3498, 3499, 3500}
storage.stopSearch = false
storage.lootContainerOpen = false
local i = 1
CaveBot.Extensions.DWithdraw.setup = function()
CaveBot.registerAction("dpwithdraw", "#00FFFF", function(value, retries)
if freecap() < 200 then
print("CaveBot[DepotWithdraw]: cap limit reached, proceeding")
return true
end
if retries > 600 then
print("CaveBot[DepotWithdraw]: actions limit reached, proceeding")
return true
end
delay(50)
if not value or #string.split(value, ",") ~= 2 then return end
local destName = string.split(value, ",")[1]:trim()
local destId = tonumber(string.split(value, ",")[2]:trim())
local destContainer
for i, container in pairs(getContainers()) do
if container:getName():lower() == destName:lower() then
destContainer = container
end
if string.find(container:getName():lower(), "depot box") then
if #container:getItems() == 0 then
print("CaveBot[DepotWithdraw]: all items withdrawn")
return true
end
end
end
if not destContainer then
print("CaveBot[DepotWithdraw]: container not found!")
return false
end
if destContainer:getCapacity() == destContainer:getSize() then
for j, item in pairs(destContainer:getItems()) do
if item:getId() == destId then
g_game.open(item, destContainer)
return "retry"
end
end
print("CaveBot[DepotWithdraw]: loot containers full!")
return true
end
local tileList = {}
local tPos
local depotClear = false
local depotOpen = false
local depotBoxOpen = false
for _,tile in pairs(g_map.getTiles(posz())) do
for i,thing in pairs(tile:getThings()) do
if table.find(depotIDs, thing:getId()) then
table.insert(tileList, {tileObj = tile, distance = getDistanceBetween(pos(), tile:getPosition()), depotID = thing:getId()})
end
end
end
table.sort(tileList, function(a,b) return a.distance < b.distance end)
::findEmptyDP::
if tileList[i] and not storage.stopSearch then
if tileList[i].depotID == 3498 then
tPos = {x = tileList[i].tileObj:getPosition().x + 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3499 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y + 1, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3500 then
tPos = {x = tileList[i].tileObj:getPosition().x - 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3497 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y - 1, z = tileList[i].tileObj:getPosition().z}
end
if tPos then
local dest = g_map.getTile(tPos)
if not comparePosition(pos(), dest:getPosition()) then
if not dest:getCreatures()[1] and dest:isWalkable() then
if autoWalk(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
else
i = i + 1
goto findEmptyDP
end
end
end
end
if tileList[i].tileObj and not table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) and comparePosition(pos(), tileList[i].tileObj:getPosition()) then
for j=1,table.getn(tileList[i].tileObj:getThings()),1 do
if not tileList[i].tileObj:getThings()[j]:isNotMoveable() then
delay(500)
g_game.move(tileList[i].tileObj:getThings()[j], pos(), tileList[i].tileObj:getThings()[j]:getCount())
end
end
if table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) then
depotClear = true
end
else
depotClear = true
end
if depotClear then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "locker" then
depotOpen = true
end
end
end
if tileList[i].tileObj and depotClear and not depotOpen and not storage.lootContainerOpen then
delay(500)
g_game.use(tileList[i].tileObj:getTopUseThing())
depotOpen = true
end
i = 1
--Version Check to know what to do with the depot--
if g_game.getClientVersion() > 910 then
if depotOpen then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "depot chest" then
depotBoxOpen = true
end
end
if findItem(3502) and not depotBoxOpen then
delay(500)
g_game.use(findItem(3502))
depotBoxOpen = true
end
end
if depotBoxOpen and not storage.lootContainerOpen then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "depot chest" then
for _, item in ipairs(container:getItems()) do
if item:isContainer() and table.find({22797, 22798}, item:getId()) then
g_game.open(findItem(22797), container)
delay(500)
for _, cont in pairs(g_game.getContainers()) do
if string.find(cont:getName():lower(), "depot box") then
storage.lootContainerOpen = true
break
end
end
end
end
break
end
end
end
for i, container in pairs(g_game.getContainers()) do
if string.find(container:getName():lower(), "depot box") then
for j, item in ipairs(container:getItems()) do
g_game.move(item, destContainer:getSlotPosition(destContainer:getItemsCount()), item:getCount())
return "retry"
end
end
end
end
return "retry"
end)
CaveBot.Editor.registerAction("dpwithdraw", "dpwithdraw", {
value="shopping bag, 21411",
title="Loot Withdraw",
description="insert destination container name and it's ID",
})
end
onPlayerPositionChange(function(newPos, oldPos)
storage.lootContainerOpen = false
storage.stopSearch = false
end)

View File

@ -0,0 +1,27 @@
CaveBot.Extensions.Depositer = {}
local ui
-- first function called, here you should setup your UI
CaveBot.Extensions.Depositer.setup = function()
--ui = UI.createWidget('Label')
--ui:setText("Depositer UI")
end
-- called when cavebot config changes, configData is a table but it can be nil
CaveBot.Extensions.Depositer.onConfigChange = function(configName, isEnabled, configData)
if not configData then return end
end
-- called when cavebot is saving config, should return table or nil
CaveBot.Extensions.Depositer.onSave = function()
return {}
end
-- bellow add you custom functions
-- this function can be used in cavebot function waypoint as: return Depositer.run(retries, prev)
-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua to learn more
CaveBot.Extensions.Depositer.run = function(retries, prev)
return true
end

View File

@ -0,0 +1,376 @@
CaveBot.Extensions.Depositor = {}
local depotIDs = {3497, 3498, 3499, 3500}
local reset = function()
storage.stopSearch = false
storage.lootContainerOpen = false
storage.containersClosed = false
storage.containersReset = false
storage.currentStack = 0
storage.currentNonStack = nonStackMin
storage.lastTry = nil
storage.lootItemsCount = 0
storage.depositDone = false
end
local i = 1
CaveBot.Extensions.Depositor.setup = function()
CaveBot.registerAction("depositor", "#00FFFF", function(value, retries)
if retries > 400 then
print("CaveBot[Depositor]: Depositor actions limit reached, proceeding")
reset()
return true
end
local name = storage["_configs"]["targetbot_configs"]["selected"]
local file = configDir .. "/targetbot_configs/" .. name .. ".json"
local data = g_resources.readFileContents(file)
local lootList = Config.parse(data)['looting']['items']
local lootContainers = Config.parse(data)['looting']['containers']
local mainBp
local stackBp
local nonStackBp
local valueString = string.split(value, ",") -- if 3 then it's old tibia
-- if old tibia then setup backpacks
if #valueString == 3 then
mainBp = tonumber(valueString[1]:trim())
stackBp = tonumber(valueString[2]:trim()) -- non-stack bp count
nonStackBp = tonumber(valueString[3]:trim()) -- stack bp count
if not mainBp or not stackBp or not nonStackBp then
warn("CaveBot[Depositor]: incorrect values! should be 3x ID of containers!")
reset()
return false
end
end
-- start with checking the containers
local lootDestination = {}
for _, container in pairs(lootContainers) do
if not table.find(lootDestination, container['id']) then
table.insert(lootDestination, container['id'])
end
end
-- pretty much every container action is needed only if you want to work with containers
if (value:lower() == "yes" or #valueString == 3) and not storage.containersReset then
-- what is open and what's not
local currentContainers = {}
for i, container in pairs(getContainers()) do
if not table.find(currentContainers, container:getContainerItem():getId()) then
table.insert(currentContainers, container:getContainerItem():getId())
end
end
delay(500) -- slow down this function until containers reset
if #lootDestination > 0 then
-- first closing all that are opened
if not storage.containersClosed then
for i, container in pairs(getContainers()) do
if table.find(lootDestination, container:getContainerItem():getId()) then
g_game.close(container)
return "retry"
end
end
storage.containersClosed = true
end
-- now reopen them
if not storage.containersReset and storage.containersClosed then
for i, container in pairs(getContainers()) do
for j, item in pairs(container:getItems()) do
if table.find(lootDestination, item:getId()) and not table.find(currentContainers, item:getId()) then
g_game.open(item)
return "retry"
end
end
end
storage.containersReset = true
end
end
end
if storage.depositDone then
reset()
print("CaveBot[Depositor]: Deposit finished, proceeding")
return true
end
local tileList = {}
local tPos
local depotClear = false
local depotOpen = false
local depotBoxOpen = false
for _,tile in pairs(g_map.getTiles(posz())) do
for i,thing in pairs(tile:getThings()) do
if table.find(depotIDs, thing:getId()) then
table.insert(tileList, {tileObj = tile, distance = getDistanceBetween(pos(), tile:getPosition()), depotID = thing:getId()})
end
end
end
table.sort(tileList, function(a,b) return a.distance < b.distance end)
::findEmptyDP::
if tileList[i] and not storage.stopSearch then
if tileList[i].depotID == 3498 then
tPos = {x = tileList[i].tileObj:getPosition().x + 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3499 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y + 1, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3500 then
tPos = {x = tileList[i].tileObj:getPosition().x - 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3497 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y - 1, z = tileList[i].tileObj:getPosition().z}
end
if tPos then
local dest = g_map.getTile(tPos)
if not (getDistanceBetween(pos(), dest:getPosition()) <= 1) then
if not dest:getCreatures()[1] and dest:isWalkable() then
if autoWalk(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
else
i = i + 1
goto findEmptyDP
end
end
end
end
if tileList[i] and not table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) and (getDistanceBetween(pos(), tileList[i].tileObj:getPosition()) <= 1) then
for j=1,table.getn(tileList[i].tileObj:getThings()),1 do
if not tileList[i].tileObj:getThings()[j]:isNotMoveable() then
delay(500)
g_game.move(tileList[i].tileObj:getThings()[j], pos(), tileList[i].tileObj:getThings()[j]:getCount())
end
end
if table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) then
depotClear = true
end
else
depotClear = true
end
if depotClear then
for _, container in pairs(getContainers()) do
if container:getName():lower() == "locker" then
depotOpen = true
end
end
end
if tileList[i] and depotClear and not depotOpen and not storage.lootContainerOpen then
delay(500)
g_game.use(tileList[i].tileObj:getTopUseThing())
depotOpen = true
end
i = 1
-- finding depot
if depotOpen then
for _, container in pairs(getContainers()) do
if container:getName():lower() == "depot chest" then
depotBoxOpen = true
end
end
if findItem(3502) and not depotBoxOpen then
delay(500)
g_game.use(findItem(3502))
depotBoxOpen = true
end
end
if depotBoxOpen and not storage.lootContainerOpen then
for _, container in pairs(getContainers()) do
if container:getName():lower() == "depot chest" then
for _, item in ipairs(container:getItems()) do
if #valueString ~= 3 then -- new depot
if item:isContainer() and table.find({22797, 22798}, item:getId()) then
delay(500)
storage.lootContainerOpen = true
break
end
else
if item:isContainer() and item:getId() == mainBp then
delay(500)
g_game.use(item, container)
storage.lootContainerOpen = true
break
end
end
end
break
end
end
end
if #valueString == 3 then
delay(150)
for _, container in pairs(getContainers()) do
if container:getContainerItem():getId() == mainBp then
storage.lootContainerOpen = true
storage.isDepositing = true
break
end
end
end
local looting = {}
for _, lootItem in pairs(lootList) do
if not table.find(looting, lootItem['id']) and not table.find({3031, 3035, 3043}, lootItem['id']) then
table.insert(looting, lootItem['id'])
end
end
delay(200)
local currentItems = 0
for _, container in pairs(getContainers()) do
for _, item in ipairs(container:getItems()) do
if table.find(looting, item:getId()) then
currentItems = currentItems + item:getCount()
end
end
end
if currentItems == 0 then
if value:lower() ~= "yes" and #valueString ~= 3 then
storage.containersClosed = false
storage.containersReset = false
storage.depositDone = true
return "retry"
end
for i, container in pairs(getContainers()) do
for j, item in pairs(container:getItems()) do
if table.find(lootDestination, container:getContainerItem():getId()) and table.find(lootDestination, item:getId()) then
g_game.open(item, container)
return "retry"
end
end
end
storage.containersClosed = false
storage.containersReset = false
storage.depositDone = true
return "retry"
end
-- only if old depot
local stackMin
local stackMax
local nonStackMin
local nonStackMax
if #valueString == 3 then
-- backpacks setup
local stack = 0
local nonStack = 0
for i, container in pairs(getContainers()) do
if container:getContainerItem():getId() == mainBp then
for i, item in pairs(container:getItems()) do
if item:getId() == stackBp then
stack = stack + 1
elseif item:getId() == nonStackBp then
nonStack = nonStack + 1
end
end
end
end
stackMax = stack - 1
nonStackMin = stack
nonStackMax = (stack + nonStack) - 1
storage.currentStack = 0
storage.currentNonStack = nonStackMin
if storage.lootItemsCount == currentItems then
if storage.lastTry == 1 then
if storage.currentStack < stackMax then
storage.currentStack = storage.currentStack + 1
else
warn("CaveBot[Depositer]: Stack Backpack full! Proceeding.")
reset()
return true
end
elseif storage.lastTry == 2 then
if storage.currentNonStack < nonStackMax then
storage.currentNonStack = storage.currentNonStack + 1
else
warn("CaveBot[Depositer]: Non-Stack Backpack full! Proceeding.")
reset()
return true
end
end
end
storage.lootItemsCount = currentItems
end
if #looting > 0 then
if #valueString ~= 3 then -- version check, if value is set of 3 i
for i, depotcontainer in pairs(getContainers()) do
containerItemId = depotcontainer:getContainerItem():getId()
--check if open depot
if containerItemId == 3502 then
-- check all containers and items
for l, lootcontainer in pairs(getContainers()) do
for j, item in ipairs(lootcontainer:getItems()) do
-- now the criteria
if table.find(looting, item:getId()) then
-- move the item
if item:isStackable() then
g_game.move(item, depotcontainer:getSlotPosition(1), item:getCount())
return "retry"
else
g_game.move(item, depotcontainer:getSlotPosition(0), item:getCount())
return "retry"
end
end
end
end
end
end
else -- to be written, last part missing is stashing items for old depots
for i, depotcontainer in pairs(getContainers()) do
containerItemId = depotcontainer:getContainerItem():getId()
--check if open depot
if containerItemId == mainBp then
-- check all containers and items
for l, lootcontainer in pairs(getContainers()) do
for j, item in ipairs(lootcontainer:getItems()) do
-- now the criteria
if table.find(looting, item:getId()) then
-- move the item
if item:isStackable() then
g_game.move(item, depotcontainer:getSlotPosition(storage.currentStack), item:getCount())
storage.lastTry = 1
return "retry"
else
g_game.move(item, depotcontainer:getSlotPosition(storage.currentNonStack), item:getCount())
storage.lastTry = 2
return "retry"
end
end
end
end
end
end
end
else
warn("no items in looting list!")
reset()
return false
end
return "retry"
end)
CaveBot.Editor.registerAction("depositor", "depositor", {
value="no",
title="Depositor",
description="No - just deposit \n Yes - also reopen loot containers \n mainID, stackId, nonStackId - for older tibia",
})
end
onPlayerPositionChange(function(newPos, oldPos)
if CaveBot.isOn() then
reset()
end
end)

View File

@ -0,0 +1,47 @@
CaveBot.Extensions.OpenDoors = {}
CaveBot.Extensions.OpenDoors.setup = function()
CaveBot.registerAction("OpenDoors", "#00FFFF", function(value, retries)
local pos = regexMatch(value, "\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)")
if not pos[1] then
error("CaveBot[OpenDoors]: invalid value. It should be position (x,y,z), is: " .. value)
return false
end
if retries >= 5 then
print("CaveBot[OpenDoors]: too many tries, can't open doors")
return false -- tried 5 times, can't open
end
pos = {x=tonumber(pos[1][2]), y=tonumber(pos[1][3]), z=tonumber(pos[1][4])}
local doorTile
if not doorTile then
for i, tile in ipairs(g_map.getTiles(posz())) do
if tile:getPosition().x == pos.x and tile:getPosition().y == pos.y and tile:getPosition().z == pos.z then
doorTile = tile
end
end
end
if not doorTile then
return false
end
if not doorTile:isWalkable() then
use(doorTile:getTopUseThing())
return "retry"
else
print("CaveBot[OpenDoors]: possible to cross, proceeding")
return true
end
end)
CaveBot.Editor.registerAction("opendoors", "open doors", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Door position",
description="doors position (x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
})
end

View File

@ -0,0 +1,174 @@
CaveBot.Editor = {}
CaveBot.Editor.Actions = {}
-- also works as registerAction(action, params), then text == action
-- params are options for text editor or function to be executed when clicked
-- you have many examples how to use it bellow
CaveBot.Editor.registerAction = function(action, text, params)
if type(text) ~= 'string' then
params = text
text = action
end
local color = nil
if type(params) ~= 'function' then
local raction = CaveBot.Actions[action]
if not raction then
return error("CaveBot editor error: action " .. action .. " doesn't exist")
end
CaveBot.Editor.Actions[action] = params
color = raction.color
end
local button = UI.createWidget('CaveBotEditorButton', CaveBot.Editor.ui.buttons)
button:setText(text)
if color then
button:setColor(color)
end
button.onClick = function()
if type(params) == 'function' then
params()
return
end
CaveBot.Editor.edit(action, nil, function(action, value)
local focusedAction = CaveBot.actionList:getFocusedChild()
local index = CaveBot.actionList:getChildCount()
if focusedAction then
index = CaveBot.actionList:getChildIndex(focusedAction)
end
local widget = CaveBot.addAction(action, value)
CaveBot.actionList:moveChildToIndex(widget, index + 1)
CaveBot.actionList:focusChild(widget)
CaveBot.save()
end)
end
return button
end
CaveBot.Editor.setup = function()
CaveBot.Editor.ui = UI.createWidget("CaveBotEditorPanel")
local ui = CaveBot.Editor.ui
local registerAction = CaveBot.Editor.registerAction
registerAction("move up", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
local index = CaveBot.actionList:getChildIndex(action)
if index < 2 then return end
CaveBot.actionList:moveChildToIndex(action, index - 1)
CaveBot.actionList:ensureChildVisible(action)
CaveBot.save()
end)
registerAction("edit", function()
local action = CaveBot.actionList:getFocusedChild()
if not action or not action.onDoubleClick then return end
action.onDoubleClick(action)
end)
registerAction("move down", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
local index = CaveBot.actionList:getChildIndex(action)
if index >= CaveBot.actionList:getChildCount() then return end
CaveBot.actionList:moveChildToIndex(action, index + 1)
CaveBot.actionList:ensureChildVisible(action)
CaveBot.save()
end)
registerAction("remove", function()
local action = CaveBot.actionList:getFocusedChild()
if not action then return end
action:destroy()
CaveBot.save()
end)
registerAction("label", {
value="labelName",
title="Label",
description="Add label",
multiline=false
})
registerAction("delay", {
value="500",
title="Delay",
description="Delay next action (in milliseconds)",
multiline=false,
validation="^\\s*[0-9]{1,10}\\s*$"
})
registerAction("gotolabel", "go to label", {
value="labelName",
title="Go to label",
description="Go to label",
multiline=false
})
registerAction("goto", "go to", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Go to position",
description="Go to position (x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)$"
})
registerAction("use", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,
title="Use",
description="Use item from position (x,y,z) or from inventory (itemId)",
multiline=false
})
registerAction("usewith", "use with", {
value=function() return "itemId," .. posx() .. "," .. posy() .. "," .. posz() end,
title="Use with",
description="Use item at position (itemid,x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
})
registerAction("say", {
value="text",
title="Say",
description="Enter text to say",
multiline=false
})
registerAction("function", {
title="Edit bot function",
multiline=true,
value=CaveBot.Editor.ExampleFunctions[1][2],
examples=CaveBot.Editor.ExampleFunctions,
width=650
})
ui.autoRecording.onClick = function()
if ui.autoRecording:isOn() then
CaveBot.Recorder.disable()
else
CaveBot.Recorder.enable()
end
end
-- callbacks
onPlayerPositionChange(function(pos)
ui.pos:setText("Position: " .. pos.x .. ", " .. pos.y .. ", " .. pos.z)
end)
ui.pos:setText("Position: " .. posx() .. ", " .. posy() .. ", " .. posz())
end
CaveBot.Editor.show = function()
CaveBot.Editor.ui:show()
end
CaveBot.Editor.hide = function()
CaveBot.Editor.ui:hide()
end
CaveBot.Editor.edit = function(action, value, callback) -- callback = function(action, value)
local params = CaveBot.Editor.Actions[action]
if not params then return end
if not value then
if type(params.value) == 'function' then
value = params.value()
elseif type(params.value) == 'string' then
value = params.value
end
end
UI.EditorWindow(value, params, function(newText)
callback(action, newText)
end)
end

View File

@ -0,0 +1,44 @@
CaveBotEditorButton < Button
CaveBotEditorPanel < Panel
id: cavebotEditor
visible: false
layout:
type: verticalBox
fit-children: true
Label
id: pos
text-align: center
text: -
Panel
id: buttons
margin-top: 2
layout:
type: grid
cell-size: 86 20
cell-spacing: 1
flow: true
fit-children: true
Label
text: Double click on action from action list to edit it
text-align: center
text-auto-resize: true
text-wrap: true
margin-top: 3
margin-left: 2
margin-right: 2
BotSwitch
id: autoRecording
text: Auto Recording
margin-top: 3
BotButton
margin-top: 3
margin-bottom: 3
text: Documentation
@onClick: g_platform.openUrl("http://bot.otclient.ovh/")

View File

@ -0,0 +1,90 @@
CaveBot.Editor.ExampleFunctions = {}
local function addExampleFunction(title, text)
return table.insert(CaveBot.Editor.ExampleFunctions, {title, text:trim()})
end
addExampleFunction("Click to browse example functions", [[
-- available functions/variables:
-- prev - result of previous action (true or false)
-- retries - number of retries of current function, goes up by one when you return "retry"
-- delay(number) - delays bot next action, value in milliseconds
-- gotoLabel(string) - goes to specific label, return true if label exists
-- you can easily access bot extensions, Depositer.run() instead of CaveBot.Extensions.Depositer.run()
-- also you can access bot global variables, like CaveBot, TargetBot
-- use storage variable to store date between calls
-- function should return false, true or "retry"
-- if "retry" is returned, function will be executed again in 20 ms (so better call delay before)
return true
]])
addExampleFunction("buy 200 mana potion from npc Eryn", [[
--buy 200 mana potions
local npc = getCreatureByName("Eryn")
if not npc then
return false
end
if retries > 10 then
return false
end
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, {precision=3})
delay(300)
return "retry"
end
if not NPC.isTrading() then
NPC.say("hi")
NPC.say("trade")
delay(200)
return "retry"
end
NPC.buy(268, 100)
schedule(1000, function()
-- buy again in 1s
NPC.buy(268, 100)
NPC.closeTrade()
NPC.say("bye")
end)
delay(1200)
return true
]])
addExampleFunction("Say hello 5 times with some delay", [[
--say hello
if retries > 5 then
return true -- finish
end
say("hello")
delay(100 + retries * 100)
return "retry"
]])
addExampleFunction("Disable TargetBot", [[
TargetBot.setOff()
return true
]])
addExampleFunction("Enable TargetBot", [[
TargetBot.setOn()
return true
]])
addExampleFunction("Enable TargetBot luring", [[
TargetBot.enableLuring()
return true
]])
addExampleFunction("Disable TargetBot luring", [[
TargetBot.disableLuring()
return true
]])
addExampleFunction("Logout", [[
g_game.safeLogout()
delay(1000)
return "retry"
]])

View File

@ -0,0 +1,58 @@
-- example cavebot extension (remember to add this file to ../cavebot.lua)
CaveBot.Extensions.Example = {}
local ui
-- setup is called automaticly when cavebot is ready
CaveBot.Extensions.Example.setup = function()
ui = UI.createWidget('BotTextEdit')
ui:setText("Hello")
ui.onTextChange = function()
CaveBot.save() -- save new config when you change something
end
-- add custom cavebot action (check out actions.lua)
CaveBot.registerAction("sayhello", "orange", function(value, retries, prev)
local how_many_times = tonumber(value)
if retries >= how_many_times then
return true
end
say("hello " .. (retries + 1))
delay(250)
return "retry"
end)
-- add this custom action to editor (check out editor.lua)
CaveBot.Editor.registerAction("sayhello", "say hello", {
value="5",
title="Say hello",
description="Says hello x times",
validation="[0-9]{1,5}" -- regex, optional
})
end
-- called when cavebot config changes, configData is a table but it can also be nil
CaveBot.Extensions.Example.onConfigChange = function(configName, isEnabled, configData)
if not configData then return end
if configData["text"] then
ui:setText(configData["text"])
end
end
-- called when cavebot is saving config (so when CaveBot.save() is called), should return table or nil
CaveBot.Extensions.Example.onSave = function()
return {text=ui:getText()}
end
-- bellow add you custom functions to be used in cavebot function action
-- an example: return Example.run(retries, prev)
-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua and example_functions.lua to learn more
CaveBot.Extensions.Example.run = function(retries, prev)
-- it will say text 10 times with some delay and then continue
if retries > 10 then
return true
end
say(ui:getText() .. " x" .. retries)
delay(100 + retries * 100)
return "retry"
end

View File

@ -0,0 +1,184 @@
CaveBot.Extensions.InWithdraw = {}
comparePosition = function(pPos, tPos)
return (getDistanceBetween(pPos, tPos) <= 1)
end
local depotIDs = {3497, 3498, 3499, 3500}
storage.stopSearch = false
storage.inboxContainerOpen = false
local i = 1
CaveBot.Extensions.InWithdraw.setup = function()
CaveBot.registerAction("inwithdraw", "#00FFFF", function(value, retries)
local data = string.split(value, ",")
local withdrawId
local count
local itemCount = 0
local depotAmount = 0
if #data ~= 2 then
error("CaveBot[InboxWithdraw]: incorrect withdraw value")
return false
else
withdrawId = tonumber(data[1])
count = tonumber(data[2])
end
for i, container in pairs(getContainers()) do
if not string.find(container:getName():lower(), "inbox") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
itemCount = itemCount + item:getCount()
end
end
end
end
if itemCount >= count then
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "your inbox") then
g_game.close(container)
end
end
print("CaveBot[InboxWithdraw]: enough items, proceeding")
return true
end
if retries > 400 then
print("CaveBot[InboxWithdraw]: actions limit reached, proceeding")
return true
end
delay(200)
local tileList = {}
local tPos
local depotClear = false
local depotOpen = false
local depotBoxOpen = false
for _,tile in pairs(g_map.getTiles(posz())) do
for i,thing in pairs(tile:getThings()) do
if table.find(depotIDs, thing:getId()) then
table.insert(tileList, {tileObj = tile, distance = getDistanceBetween(pos(), tile:getPosition()), depotID = thing:getId()})
end
end
end
table.sort(tileList, function(a,b) return a.distance < b.distance end)
::findEmptyDP::
if tileList[i] and not storage.stopSearch then
if tileList[i].depotID == 3498 then
tPos = {x = tileList[i].tileObj:getPosition().x + 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3499 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y + 1, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3500 then
tPos = {x = tileList[i].tileObj:getPosition().x - 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3497 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y - 1, z = tileList[i].tileObj:getPosition().z}
end
if tPos then
local dest = g_map.getTile(tPos)
if not comparePosition(pos(), dest:getPosition()) then
if not dest:getCreatures()[1] and dest:isWalkable() then
if autoWalk(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
else
i = i + 1
goto findEmptyDP
end
end
end
end
if tileList[i].tileObj and not table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) and comparePosition(pos(), tileList[i].tileObj:getPosition()) then
for j=1,table.getn(tileList[i].tileObj:getThings()),1 do
if not tileList[i].tileObj:getThings()[j]:isNotMoveable() then
delay(500)
g_game.move(tileList[i].tileObj:getThings()[j], pos(), tileList[i].tileObj:getThings()[j]:getCount())
end
end
if table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) then
depotClear = true
end
else
depotClear = true
end
if depotClear then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "locker" then
depotOpen = true
end
end
end
if tileList[i].tileObj and depotClear and not depotOpen and not storage.inboxContainerOpen then
delay(500)
g_game.use(tileList[i].tileObj:getTopUseThing())
depotOpen = true
end
i = 1
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "your inbox" then
depotBoxOpen = true
end
end
if depotOpen and not depotBoxOpen then
if findItem(12902) then
delay(500)
g_game.use(findItem(12902))
depotBoxOpen = true
end
end
if depotBoxOpen and not storage.inboxContainerOpen then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "your" then
storage.inboxContainerOpen = true
end
end
end
delay(500)
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "your") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
depotAmount = depotAmount + item:getCount()
end
end
break
end
end
local destination
for i, container in pairs(getContainers()) do
if container:getCapacity() > container:getSize() 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
destination = container
end
end
if itemCount < count and destination then
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "your inbox") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
if item:isStackable() then
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), math.min(item:getCount(), (count - itemCount)))
return "retry"
else
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), 1)
return "retry"
end
return "retry"
end
end
end
end
end
return "retry"
end)
CaveBot.Editor.registerAction("inwithdraw", "in withdraw", {
value="id,amount",
title="Withdraw Items",
description="insert item id and amount",
})
end

View File

@ -0,0 +1,23 @@
CaveBot.Extensions.Lure = {}
CaveBot.Extensions.Lure.setup = function()
CaveBot.registerAction("lure", "#00FFFF", function(value, retries)
if value == "start" then
TargetBot.enableLuring()
return true
elseif value == "stop" then
TargetBot.disableLuring()
return true
else
warn("incorrect lure value!")
return false
end
end)
CaveBot.Editor.registerAction("lure", "lure", {
value="start",
title="Lure",
description="start/stop",
multiline=false,
})
end

View File

@ -0,0 +1,44 @@
CaveBot.Extensions.PosCheck = {}
storage.posCheckRetries = 0
CaveBot.Extensions.PosCheck.setup = function()
CaveBot.registerAction("PosCheck", "#00FFFF", function(value, retries)
local tilePos
local data = string.split(value, ",")
if #data ~= 5 then
error("wrong travel format, should be: label, distance, x, y, z")
return false
end
local tilePos = player:getPosition()
tilePos.x = tonumber(data[3])
tilePos.y = tonumber(data[4])
tilePos.z = tonumber(data[5])
if storage.posCheckRetries > 10 then
storage.posCheckRetries = 0
print("CaveBot[CheckPos]: waypoints locked, too many tries, unclogging cavebot and proceeding")
return false
elseif (tilePos.z == player:getPosition().z) and (getDistanceBetween(player:getPosition(), tilePos) <= tonumber(data[2])) then
storage.posCheckRetries = 0
print("CaveBot[CheckPos]: position reached, proceeding")
return true
else
storage.posCheckRetries = storage.posCheckRetries + 1
CaveBot.gotoLabel(data[1])
print("CaveBot[CheckPos]: position not-reached, going back to label: " .. data[1])
return false
end
end)
CaveBot.Editor.registerAction("poscheck", "pos check", {
value=function() return "label" .. "," .. "distance" .. "," .. posx() .. "," .. posy() .. "," .. posz() end,
title="Location Check",
description="label name, accepted dist from coordinates, x, y, z",
multiline=false,
})
end

View File

@ -0,0 +1,65 @@
-- auto recording for cavebot
CaveBot.Recorder = {}
local isEnabled = nil
local lastPos = nil
local function setup()
local function addPosition(pos)
CaveBot.addAction("goto", pos.x .. "," .. pos.y .. "," .. pos.z, true)
lastPos = pos
end
onPlayerPositionChange(function(newPos, oldPos)
if CaveBot.isOn() or not isEnabled then return end
if not lastPos then
-- first step
addPosition(oldPos)
elseif newPos.z ~= oldPos.z or math.abs(oldPos.x - newPos.x) > 1 or math.abs(oldPos.y - newPos.y) > 1 then
-- stairs/teleport
addPosition(oldPos)
elseif math.max(math.abs(lastPos.x - newPos.x), math.abs(lastPos.y - newPos.y)) > 5 then
-- 5 steps from last pos
addPosition(newPos)
end
end)
onUse(function(pos, itemId, stackPos, subType)
if CaveBot.isOn() or not isEnabled then return end
if pos.x ~= 0xFFFF then
lastPos = pos
CaveBot.addAction("use", pos.x .. "," .. pos.y .. "," .. pos.z, true)
end
end)
onUseWith(function(pos, itemId, target, subType)
if CaveBot.isOn() or not isEnabled then return end
if not target:isItem() then return end
local targetPos = target:getPosition()
if targetPos.x == 0xFFFF then return end
lastPos = pos
CaveBot.addAction("usewith", itemId .. "," .. targetPos.x .. "," .. targetPos.y .. "," .. targetPos.z, true)
end)
end
CaveBot.Recorder.isOn = function()
return isEnabled
end
CaveBot.Recorder.enable = function()
CaveBot.setOff()
if isEnabled == nil then
setup()
end
CaveBot.Editor.ui.autoRecording:setOn(true)
isEnabled = true
lastPos = nil
end
CaveBot.Recorder.disable = function()
if isEnabled == true then
isEnabled = false
end
CaveBot.Editor.ui.autoRecording:setOn(false)
CaveBot.save()
end

View File

@ -0,0 +1,68 @@
CaveBot.Extensions.SellAll = {}
storage.sellAllCap = 0
CaveBot.Extensions.SellAll.setup = function()
CaveBot.registerAction("SellAll", "#00FFFF", function(value, retries)
local val = string.split(value, ",")
local wait
if #val > 2 then
warn("CaveBot[SellAll]: incorrect sell all value!")
return false
end
if #val == 2 then
wait = true
else
wait = false
end
local npc = getCreatureByName(value)
if not npc then
print("CaveBot[SellAll]: NPC not found! skipping")
return false
end
if retries > 10 then
print("CaveBot[SellAll]: can't sell, skipping")
return false
end
if freecap() == storage.sellAllCap then
storage.sellAllCap = 0
print("CaveBot[SellAll]: Sold everything, proceeding")
return true
end
delay(800)
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end
if not NPC.isTrading() then
NPC.say("hi")
schedule(500, function() NPC.say("trade") end)
else
storage.sellAllCap = freecap()
end
NPC.sellAll(wait)
if #val == 2 then
print("CaveBot[SellAll]: Sold All with delay")
else
print("CaveBot[SellAll]: Sold All without delay")
end
return "retry"
end)
CaveBot.Editor.registerAction("sellall", "sell all", {
value="NPC",
title="Sell All",
description="Insert NPC name, and 'yes' if sell with delay ",
})
end

View File

@ -0,0 +1,30 @@
CaveBot.Extensions.Supply = {}
local ui
-- first function called, here you should setup your UI
CaveBot.Extensions.Supply.setup = function()
--ui = UI.createWidget('SupplyItemList')
--local widget = UI.createWidget('SupplyItem', ui.list)
--widget.item.onItemChange = function(newItem)
--widget.fields.min.onTextChange = function(newText)
-- make it similar to UI.Container, so if there are no free slots, add another one, keep min 4 slots, check if value min/max is number after edit
end
-- called when cavebot config changes, configData is a table but it can be nil
CaveBot.Extensions.Supply.onConfigChange = function(configName, isEnabled, configData)
if not configData then return end
end
-- called when cavebot is saving config, should return table or nil
CaveBot.Extensions.Supply.onSave = function()
return {}
end
-- bellow add you custom functions
-- this function can be used in cavebot function waypoint as: return Supply.run(retries, prev)
-- there are 2 useful parameters - retries (number) and prev (true/false), check actions.lua to learn more
CaveBot.Extensions.Supply.run = function(retries, prev)
return true
end

View File

@ -0,0 +1,72 @@
SupplyItem < Panel
height: 34
BotItem
id: item
size: 32 32
anchors.left: parent.left
anchors.top: parent.top
margin-top: 1
Panel
id: fields
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: prev.right
anchors.right: parent.right
margin-left: 2
margin-right: 2
Label
id: minLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-right: 2
text-align: center
text: "Min"
Label
id: maxLabel
anchors.top: parent.top
anchors.left: parent.horizontalCenter
anchors.right: parent.right
margin-left: 2
text-align: center
text: "Max"
BotTextEdit
id: min
anchors.top: minLabel.bottom
anchors.left: minLabel.left
anchors.right: minLabel.right
text-align: center
text: 1
BotTextEdit
id: max
anchors.top: maxLabel.bottom
anchors.left: maxLabel.left
anchors.right: maxLabel.right
text-align: center
text: 100
SupplyItemList < Panel
height: 102
ScrollablePanel
id: list
anchors.fill: parent
vertical-scrollbar: scroll
margin-right: 7
layout:
type: verticalBox
cell-height: 34
BotSmallScrollBar
id: scroll
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true

View File

@ -0,0 +1,65 @@
CaveBot.Extensions.SupplyCheck = {}
storage.supplyRetries = 0
CaveBot.Extensions.SupplyCheck.setup = function()
CaveBot.registerAction("supplyCheck", "#00FFFF", function(value)
local softCount = itemAmount(6529) + itemAmount(3549)
local totalItem1 = itemAmount(storage[suppliesPanelName].item1)
local totalItem2 = itemAmount(storage[suppliesPanelName].item2)
local totalItem3 = itemAmount(storage[suppliesPanelName].item3)
local totalItem4 = itemAmount(storage[suppliesPanelName].item4)
local totalItem5 = itemAmount(storage[suppliesPanelName].item5)
if storage.supplyRetries > 50 then
print("CaveBot[SupplyCheck]: Round limit reached, going back on refill.")
storage.supplyRetries = 0
return true
elseif (storage[suppliesPanelName].imbues and player:getSkillLevel(11) ~= 100) then
print("CaveBot[SupplyCheck]: Imbues ran out. Going on refill.")
storage.supplyRetries = 0
return true
elseif (storage[suppliesPanelName].staminaSwitch and stamina() < tonumber(storage[suppliesPanelName].staminaValue)) then
print("CaveBot[SupplyCheck]: Stamina ran out. Going on refill.")
storage.supplyRetries = 0
return true
elseif (softCount < 1 and storage[suppliesPanelName].SoftBoots) then
print("CaveBot[SupplyCheck]: No soft boots left. Going on refill.")
storage.supplyRetries = 0
return true
elseif (totalItem1 < tonumber(storage[suppliesPanelName].item1Min) and storage[suppliesPanelName].item1 > 100) then
print("CaveBot[SupplyCheck]: Not enough item: " .. storage[suppliesPanelName].item1 .. "(only " .. totalItem1 .. " left). Going on refill.")
storage.supplyRetries = 0
return true
elseif (totalItem2 < tonumber(storage[suppliesPanelName].item2Min) and storage[suppliesPanelName].item2 > 100) then
print("CaveBot[SupplyCheck]: Not enough item: " .. storage[suppliesPanelName].item2 .. "(only " .. totalItem2 .. " left). Going on refill.")
storage.supplyRetries = 0
return true
elseif (totalItem3 < tonumber(storage[suppliesPanelName].item3Min) and storage[suppliesPanelName].item3 > 100) then
print("CaveBot[SupplyCheck]: Not enough item: " .. storage[suppliesPanelName].item3 .. "(only " .. totalItem3 .. " left). Going on refill.")
storage.supplyRetries = 0
return true
elseif (totalItem4 < tonumber(storage[suppliesPanelName].item4Min) and storage[suppliesPanelName].item4 > 100) then
print("CaveBot[SupplyCheck]: Not enough item: " .. storage[suppliesPanelName].item4 .. "(only " .. totalItem4 .. " left). Going on refill.")
storage.supplyRetries = 0
return true
elseif (totalItem5 < tonumber(storage[suppliesPanelName].item5Min) and storage[suppliesPanelName].item5 > 100) then
print("CaveBot[SupplyCheck]: Not enough item: " .. storage[suppliesPanelName].item5 .. "(only " .. totalItem5 .. " left). Going on refill.")
storage.supplyRetries = 0
return true
elseif (freecap() < tonumber(storage[suppliesPanelName].capValue) and storage[suppliesPanelName].capSwitch) then
print("CaveBot[SupplyCheck]: Not enough capacity. Going on refill.")
storage.supplyRetries = 0
return true
else
print("CaveBot[SupplyCheck]: Enough supplies. Hunting. Round (" .. storage.supplyRetries .. "/50)")
storage.supplyRetries = storage.supplyRetries + 1
return CaveBot.gotoLabel(value)
end
end)
CaveBot.Editor.registerAction("supplycheck", "supply check", {
value="startHunt",
title="Supply check label",
description="Insert here hunting start label",
})
end

View File

@ -0,0 +1,52 @@
CaveBot.Extensions.Travel = {}
CaveBot.Extensions.Travel.setup = function()
CaveBot.registerAction("Travel", "#00FFFF", function(value, retries)
local data = string.split(value, ",")
local waitVal = 0
if #data < 2 or #data > 3 then
warn("CaveBot[Travel]: incorrect travel value!")
return false
elseif #data == 3 then
waitVal = tonumber(data[3]:trim())
end
if not waitVal then
warn("CaveBot[Travel]: incorrect travel delay value!")
return false
end
if retries > 5 then
print("CaveBot[Travel]: too many tries, can't travel")
return false
end
local npc = getCreatureByName(data[1]:trim())
if not npc then
print("CaveBot[Travel]: NPC not found, can't travel")
return false
end
local pos = player:getPosition()
local npcPos = npc:getPosition()
if math.max(math.abs(pos.x - npcPos.x), math.abs(pos.y - npcPos.y)) > 3 then
autoWalk(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end
NPC.say("hi")
schedule(waitVal, function() NPC.say(data[2]:trim()) end)
schedule(2*waitVal, function() NPC.say("yes") end)
delay(3*waitVal)
print("CaveBot[Travel]: travel action finished")
return true
end)
CaveBot.Editor.registerAction("travel", "travel", {
value="NPC name, city",
title="Travel",
description="NPC name, City name, delay in ms(optional)",
})
end

View File

@ -0,0 +1,93 @@
-- walking
local expectedDirs = {}
local isWalking = {}
local walkPath = {}
local walkPathIter = 0
CaveBot.resetWalking = function()
expectedDirs = {}
walkPath = {}
isWalking = false
end
CaveBot.doWalking = function()
if CaveBot.Config.get("mapClick") then
return false
end
if #expectedDirs == 0 then
return false
end
if #expectedDirs >= 3 then
CaveBot.resetWalking()
end
local dir = walkPath[walkPathIter]
if dir then
g_game.walk(dir, false)
table.insert(expectedDirs, dir)
walkPathIter = walkPathIter + 1
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end
return false
end
-- called when player position has been changed (step has been confirmed by server)
onPlayerPositionChange(function(newPos, oldPos)
if not oldPos or not newPos then return end
local dirs = {{NorthWest, North, NorthEast}, {West, 8, East}, {SouthWest, South, SouthEast}}
local dir = dirs[newPos.y - oldPos.y + 2]
if dir then
dir = dir[newPos.x - oldPos.x + 2]
end
if not dir then
dir = 8 -- 8 is invalid dir, it's fine
end
if not isWalking or not expectedDirs[1] then
-- some other walk action is taking place (for example use on ladder), wait
walkPath = {}
CaveBot.delay(CaveBot.Config.get("ping") + player:getStepDuration(false, dir) + 150)
return
end
if expectedDirs[1] ~= dir then
if CaveBot.Config.get("mapClick") then
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
else
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
return
end
table.remove(expectedDirs, 1)
if CaveBot.Config.get("mapClick") and #expectedDirs > 0 then
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + player:getStepDuration(false, dir))
end
end)
CaveBot.walkTo = function(dest, maxDist, params)
local path = getPath(player:getPosition(), dest, maxDist, params)
if not path or not path[1] then
return false
end
local dir = path[1]
if CaveBot.Config.get("mapClick") then
local ret = autoWalk(path)
if ret then
isWalking = true
expectedDirs = path
CaveBot.delay(CaveBot.Config.get("mapClickDelay") + math.max(CaveBot.Config.get("ping") + player:getStepDuration(false, dir), player:getStepDuration(false, dir) * 2))
end
return ret
end
g_game.walk(dir, false)
isWalking = true
walkPath = path
walkPathIter = 2
expectedDirs = { dir }
CaveBot.delay(CaveBot.Config.get("walkDelay") + player:getStepDuration(false, dir))
return true
end

View File

@ -0,0 +1,221 @@
CaveBot.Extensions.Withdraw = {}
comparePosition = function(pPos, tPos)
return (getDistanceBetween(pPos, tPos) <= 1)
end
local depotContainers = {22797, 22798, 22799, 22800, 22801, 22802, 22803, 22804, 22805, 22806, 22807, 22808, 22809, 22810, 22811, 22812, 22813}
local depotIDs = {3497, 3498, 3499, 3500}
storage.stopSearch = false
storage.lootContainerOpen = false
local i = 1
CaveBot.Extensions.Withdraw.setup = function()
CaveBot.registerAction("withdraw", "#00FFFF", function(value, retries)
local data = string.split(value, ",")
local stashIndex
local withdrawId
local count
local itemCount = 0
local depotAmount
if #data ~= 3 then
warn("incorrect withdraw value")
return false
else
stashIndex = tonumber(data[1])
withdrawId = tonumber(data[2])
count = tonumber(data[3])
end
local withdrawSource = depotContainers[stashIndex]
for i, container in pairs(getContainers()) do
if not string.find(container:getName():lower(), "depot") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
itemCount = itemCount + item:getCount()
end
end
end
end
if itemCount >= count then
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "depot box") then
g_game.close(container)
end
end
print("enough items")
return true
end
if retries > 400 then
return true
end
delay(200)
local tileList = {}
local tPos
local depotClear = false
local depotOpen = false
local depotBoxOpen = false
for _,tile in pairs(g_map.getTiles(posz())) do
for i,thing in pairs(tile:getThings()) do
if table.find(depotIDs, thing:getId()) then
table.insert(tileList, {tileObj = tile, distance = getDistanceBetween(pos(), tile:getPosition()), depotID = thing:getId()})
end
end
end
table.sort(tileList, function(a,b) return a.distance < b.distance end)
::findEmptyDP::
if tileList[i] and not storage.stopSearch then
if tileList[i].depotID == 3498 then
tPos = {x = tileList[i].tileObj:getPosition().x + 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3499 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y + 1, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3500 then
tPos = {x = tileList[i].tileObj:getPosition().x - 1, y = tileList[i].tileObj:getPosition().y, z = tileList[i].tileObj:getPosition().z}
elseif tileList[i].depotID == 3497 then
tPos = {x = tileList[i].tileObj:getPosition().x, y = tileList[i].tileObj:getPosition().y - 1, z = tileList[i].tileObj:getPosition().z}
end
if tPos then
local dest = g_map.getTile(tPos)
if not comparePosition(pos(), dest:getPosition()) then
if not dest:getCreatures()[1] and dest:isWalkable() then
if autoWalk(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
else
i = i + 1
goto findEmptyDP
end
end
end
end
if tileList[i].tileObj and not table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) and comparePosition(pos(), tileList[i].tileObj:getPosition()) then
for j=1,table.getn(tileList[i].tileObj:getThings()),1 do
if not tileList[i].tileObj:getThings()[j]:isNotMoveable() then
delay(500)
g_game.move(tileList[i].tileObj:getThings()[j], pos(), tileList[i].tileObj:getThings()[j]:getCount())
end
end
if table.find(depotIDs, tileList[i].tileObj:getTopLookThing():getId()) then
depotClear = true
end
else
depotClear = true
end
if depotClear then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "locker" then
depotOpen = true
end
end
end
if tileList[i].tileObj and depotClear and not depotOpen and not storage.lootContainerOpen then
delay(500)
g_game.use(tileList[i].tileObj:getTopUseThing())
depotOpen = true
end
i = 1
--Version Check to know what to do with the depot--
if g_game.getClientVersion() > 910 then
if depotOpen then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "depot chest" then
depotBoxOpen = true
end
end
if findItem(3502) and not depotBoxOpen then
delay(500)
g_game.use(findItem(3502))
depotBoxOpen = true
end
end
if depotBoxOpen and not storage.lootContainerOpen then
for _, container in pairs(g_game.getContainers()) do
if container:getName():lower() == "depot chest" then
for _, item in ipairs(container:getItems()) do
if item:isContainer() and table.find({22797, 22798}, item:getId()) then
delay(500)
storage.lootContainerOpen = true
break
end
end
break
end
end
end
local boxOpened = false
for _, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "depot box") then
boxOpened = true
end
end
if not boxOpened then
for _, container in pairs(getContainers()) do
if container:getName():lower() == "depot chest" then
for _, item in pairs(container:getItems()) do
if item:getId() == withdrawSource then
g_game.open(item)
break
end
end
end
end
end
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "depot") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
depotAmount = depotAmount + item:getCount()
end
end
break
end
end
if depotAmount == 0 then
print("lack of withdraw items!")
return false
end
local destination
for i, container in pairs(getContainers()) do
if container:getCapacity() > container:getSize() 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
destination = container
end
end
if itemCount < count and destination then
for i, container in pairs(getContainers()) do
if string.find(container:getName():lower(), "depot box") then
for j, item in pairs(container:getItems()) do
if item:getId() == withdrawId then
if item:isStackable() then
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), math.min(item:getCount(), (count - itemCount)))
return "retry"
else
g_game.move(item, destination:getSlotPosition(destination:getItemsCount()), 1)
return "retry"
end
return "retry"
end
end
end
end
end
return "retry"
end
end)
CaveBot.Editor.registerAction("withdraw", "withdraw", {
value="index,id,amount",
title="Withdraw Items",
description="insert source index, item id and amount",
})
end

View File

@ -0,0 +1,391 @@
AttackComboBoxPopupMenu < ComboBoxPopupMenu
AttackComboBoxPopupMenuButton < ComboBoxPopupMenuButton
AttackComboBox < ComboBox
@onSetup: |
self:addOption("LEADER TARGET")
self:addOption("COMMAND TARGET")
FollowComboBoxPopupMenu < ComboBoxPopupMenu
FollowComboBoxPopupMenuButton < ComboBoxPopupMenuButton
FollowComboBox < ComboBox
@onSetup: |
self:addOption("LEADER TARGET")
self:addOption("SERVER LEADER TARGET")
self:addOption("LEADER")
self:addOption("SERVER LEADER")
ComboTrigger < Panel
id: trigger
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 450 72
Label
id: triggerLabel1
anchors.left: parent.left
anchors.top: parent.top
text: On Say
margin-top: 8
margin-left: 5
color: #ffaa00
Label
id: leaderLabel
anchors.left: triggerLabel1.right
anchors.top: triggerLabel1.top
text: Leader:
margin-left: 35
TextEdit
id: onSayLeader
anchors.left: leaderLabel.right
anchors.top: leaderLabel.top
anchors.bottom: leaderLabel.bottom
margin-left: 5
width: 120
font: cipsoftFont
Label
id: phrase
anchors.left: onSayLeader.right
anchors.top: onSayLeader.top
text: Phrase:
margin-left: 5
TextEdit
id: onSayPhrase
anchors.left: phrase.right
anchors.top: leaderLabel.top
anchors.bottom: leaderLabel.bottom
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onSayToggle
anchors.left: onSayPhrase.right
anchors.top: onSayPhrase.top
margin-top: 1
margin-left: 5
Label
id: triggerLabel2
anchors.left: triggerLabel1.left
anchors.top: triggerLabel1.bottom
text: On Shoot
margin-top: 5
color: #ffaa00
Label
id: leaderLabel1
anchors.left: triggerLabel2.right
anchors.top: triggerLabel2.top
text: Leader:
margin-left: 24
TextEdit
id: onShootLeader
anchors.left: leaderLabel1.right
anchors.top: leaderLabel1.top
anchors.bottom: leaderLabel1.bottom
anchors.right: onSayPhrase.right
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onShootToggle
anchors.left: onShootLeader.right
anchors.top: onShootLeader.top
margin-top: 1
margin-left: 5
Label
id: triggerLabel3
anchors.left: triggerLabel2.left
anchors.top: triggerLabel2.bottom
text: On Cast
margin-top: 5
color: #ffaa00
Label
id: leaderLabel2
anchors.left: triggerLabel3.right
anchors.top: triggerLabel3.top
text: Leader:
margin-left: 32
TextEdit
id: onCastLeader
anchors.left: leaderLabel2.right
anchors.top: leaderLabel2.top
anchors.bottom: leaderLabel2.bottom
anchors.right: onSayPhrase.right
margin-left: 5
width: 120
font: cipsoftFont
CheckBox
id: onCastToggle
anchors.left: onCastLeader.right
anchors.top: onCastLeader.top
margin-top: 1
margin-left: 5
ComboActions < Panel
id: actions
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 220 100
Label
id: label1
anchors.left: parent.left
anchors.top: parent.top
text: Follow:
margin-top: 5
margin-left: 3
height: 15
color: #ffaa00
FollowComboBox
id: followLeader
anchors.left: prev.right
anchors.top: prev.top
margin-left: 7
height: 15
width: 145
font: cipsoftFont
CheckBox
id: followLeaderToggle
anchors.left: followLeader.right
anchors.top: followLeader.top
margin-top: 2
margin-left: 5
Label
id: label2
anchors.left: label1.left
anchors.top: label1.bottom
margin-top: 5
text: Attack:
color: #ffaa00
AttackComboBox
id: attackLeaderTarget
anchors.left: prev.right
anchors.top: prev.top
margin-left: 5
height: 15
width: 145
font: cipsoftFont
CheckBox
id: attackLeaderTargetToggle
anchors.left: attackLeaderTarget.right
anchors.top: attackLeaderTarget.top
margin-top: 2
margin-left: 5
Label
id: label3
anchors.left: label2.left
anchors.top: label2.bottom
margin-top: 5
text: Spell:
color: #ffaa00
TextEdit
id: attackSpell
anchors.left: prev.right
anchors.top: prev.top
anchors.right: attackLeaderTarget.right
margin-left: 17
height: 15
width: 145
font: cipsoftFont
CheckBox
id: attackSpellToggle
anchors.left: attackSpell.right
anchors.top: attackSpell.top
margin-top: 2
margin-left: 5
Label
id: label4
anchors.left: label3.left
anchors.top: label3.bottom
margin-top: 15
text: Attack Item:
color: #ffaa00
BotItem
id: attackItem
anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter
margin-left: 10
CheckBox
id: attackItemToggle
anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter
margin-left: 5
BotSwitch
id: commandsToggle
anchors.left: prev.right
anchors.top: attackItem.top
anchors.right: attackSpellToggle.right
anchors.bottom: attackItem.bottom
margin-left: 5
text: Leader Commands
text-wrap: true
multiline: true
BotServer < Panel
id: server
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 220 100
Label
id: labelX
anchors.left: parent.left
anchors.top: parent.top
text: Leader:
height: 15
color: #ffaa00
margin-left: 3
margin-top: 5
TextEdit
id: botServerLeader
anchors.left: prev.right
anchors.top: prev.top
anchors.right: parent.right
margin-right: 3
margin-left: 9
height: 15
font: cipsoftFont
Button
id: partyButton
anchors.left: labelX.left
anchors.top: botServerLeader.bottom
margin-top: 5
height: 30
text: Join Party
text-wrap: true
multiline: true
BotSwitch
id: botServerToggle
anchors.left: prev.right
anchors.top: botServerLeader.bottom
anchors.right: parent.right
height: 30
margin-left: 3
margin-right: 3
margin-top: 5
text: Server Enabled
BotSwitch
id: targetServerLeaderToggle
anchors.left: partyButton.left
anchors.top: partyButton.bottom
anchors.right: partyButton.right
margin-top: 3
height: 30
text: Leader Targets
BotSwitch
id: Triggers
anchors.left: prev.right
anchors.top: partyButton.bottom
anchors.right: parent.right
margin-top: 3
height: 30
margin-left: 3
margin-right: 3
text: Triggers
ComboWindow < MainWindow
!text: tr('Combo Options')
size: 500 280
@onEscape: self:hide()
ComboTrigger
id: trigger
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 7
Label
id: title
anchors.top: parent.top
anchors.left: parent.left
margin-left: 10
text: Combo Trigger
color: #ff7700
ComboActions
id: actions
anchors.top: trigger.bottom
anchors.left: trigger.left
margin-top: 15
Label
id: title
anchors.top: parent.top
anchors.left: parent.left
margin-left: 10
margin-top: 85
text: Combo Actions
color: #ff7700
BotServer
id: server
anchors.top: actions.top
anchors.left: actions.right
margin-left: 10
Label
id: title
anchors.top: parent.top
anchors.left: server.left
margin-left: 3
margin-top: 85
text: BotServer
color: #ff7700
HorizontalSeparator
id: separator
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: closeButton.top
margin-bottom: 8
Button
id: closeButton
!text: tr('Close')
font: cipsoftFont
anchors.right: parent.right
anchors.bottom: parent.bottom
size: 45 21
margin-top: 15
margin-right: 5
Button
id: toolsButton
!text: tr('Help')
font: cipsoftFont
anchors.right: closeButton.left
anchors.top: closeButton.top
margin-right: 10
size: 45 21
@onClick: g_platform.openUrl("http://bot.otclient.ovh/books/scripts/page/combobot")

View File

@ -0,0 +1,29 @@
setDefaultTab("Cave")
UI.Separator()
UI.Label("Trash items:")
if type(storage.trashItems) ~= "table" or not storage.trashItems then
storage.trashItems = {283, 284, 285}
end
local dropContainer = UI.Container(function(widget, items)
storage.trashItems = items
end, true)
dropContainer:setHeight(35)
dropContainer:setItems(storage.trashItems)
macro(200, "Drop Items", function()
if not storage.trashItems[1] then return end
for _, container in pairs(g_game.getContainers()) do
for __, item in ipairs(container:getItems()) do
for i, trashItem in ipairs(storage.trashItems) do
if item:getId() == trashItem.id then
return g_game.move(item, pos(), item:getCount())
end
end
end
end
end)
UI.Separator()

View File

@ -0,0 +1,29 @@
setDefaultTab("HP")
UI.Separator()
UI.Label("Eatable items:")
if type(storage.foodItems) ~= "table" then
storage.foodItems = {3582, 3577}
end
local foodContainer = UI.Container(function(widget, items)
storage.foodItems = items
end, true)
foodContainer:setHeight(35)
foodContainer:setItems(storage.foodItems)
macro(500, "Eat Food", function()
if player:getRegenerationTime() > 400 or not storage.foodItems[1] then return end
-- search for food in containers
for _, container in pairs(g_game.getContainers()) do
for __, item in ipairs(container:getItems()) do
for i, foodItem in ipairs(storage.foodItems) do
if item:getId() == foodItem.id then
return g_game.use(item)
end
end
end
end
end)

View File

@ -0,0 +1,296 @@
MonsterLabel < Label
opacity: 0.87
text-offset: 2 0
focusable: false
height: 16
HuntAnalyser < MiniWindow
!text: tr('Hunt Analyser')
height: 222
icon: /images/topbuttons/analyzers
&save: true
&autoOpen: false
MiniWindowContents
Label
id: session
!text: tr('Session:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 3
margin-left: 3
margin-right: 3
BotLabel
id: sessionValue
!text: tr('00:00h')
anchors.right: parent.right
anchors.verticalCenter: session.verticalCenter
margin-left: 3
margin-right: 3
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: xpGain
!text: tr('XP Gain:')
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
Label
id: xpGainValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: xpGain.verticalCenter
margin-right: 3
width: 110
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: xpHour
!text: tr('XP/h:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: xpHourValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: xpHour.verticalCenter
margin-left: 3
width: 100
text-align: right
margin-right: 3
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: loot
!text: tr('Loot:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: lootValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: loot.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: supplies
!text: tr('Supplies:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: suppliesValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: supplies.verticalCenter
margin-left: 3
margin-right: 3
width: 110
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: balance
!text: tr('Balance:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: balanceValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: balance.verticalCenter
margin-left: 3
margin-right: 3
width: 110
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: damage
!text: tr('Damage:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: damageValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: damage.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: damageHour
!text: tr('Damage/h:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: damageHourValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: damageHour.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: healing
!text: tr('Healing:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: healingValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: healing.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: healingHour
!text: tr('Healing/h:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: healingHourValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: healingHour.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: Label
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 10
margin-left: 3
!text: tr('Monsters Killed:')
TextList
id: MessagePanel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 3
margin-left: 3
margin-right: 3
height: 400

View File

@ -0,0 +1,195 @@
ImpactAnalyser < MiniWindow
!text: tr('Impact Analyser')
height: 205
icon: /images/topbuttons/analyzers
&save: true
&autoOpen: false
MiniWindowContents
Label
id: damageCategory
!text: tr('Damage')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
margin-top: 5
height: 20
font: sans-bold-16px
text-align: center
margin-left: 3
margin-right: 3
Label
id: damageTotal
!text: tr('Total:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
margin-left: 3
margin-right: 3
Label
id: damageValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: damageTotal.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: maxDps
!text: tr('DPS:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: maxDpsValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: maxDps.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: allTimeHigh
!text: tr('All-Time High:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: allTimeHighValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: allTimeHigh.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: healingCategory
!text: tr('Healing')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 15
height: 20
font: sans-bold-16px
text-align: center
margin-left: 3
margin-right: 3
Label
id: healingTotal
!text: tr('Total:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 5
margin-left: 3
margin-right: 3
Label
id: healingValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: healingTotal.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: maxHps
!text: tr('HPS:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: maxHpsValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: maxHps.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
Label
id: allTimeHighHeal
!text: tr('All-Time High:')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
margin-left: 3
margin-right: 3
Label
id: allTimeHighHealValue
!text: tr('0')
anchors.right: parent.right
anchors.verticalCenter: allTimeHighHeal.verticalCenter
margin-left: 3
margin-right: 3
width: 100
text-align: right
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More