Updated vithrax bot config to version 1.2, fixed market bug and battle button bug

This commit is contained in:
OTCv8 2020-11-19 21:39:21 +01:00
parent bc1574f2e7
commit 407553647d
91 changed files with 467 additions and 523 deletions

View File

@ -283,7 +283,7 @@ function checkCreatures()
for i=#creatures + 1,maxCreatures do
if battleButtons[i]:isHidden() then break end
battleButtons[i]:hide()
battleButton:setOn(false)
battleButtons[i]:setOn(false)
end
battlePanel:getLayout():enableUpdates()

View File

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

View File

@ -1,303 +0,0 @@
{
"rope": 9596,
"castle": {
"enabled": false,
"id": 2983,
"around": false
},
"supplyRetries": 0,
"foodItems": [
{
"count": 1,
"id": 3582
},
{
"count": 1,
"id": 3577
}
],
"isUsing": false,
"healbot": {
"spellTable": [
],
"enabled": false,
"itemTable": [
]
},
"supplies": {
"item3Max": 0,
"item2Min": 0,
"item4Max": 0,
"item1Min": 0,
"item2": 0,
"SoftBoots": false,
"item1": 0,
"item1Max": 0,
"item5Max": 0,
"item3Min": 0,
"item4Min": 0,
"imbues": false,
"item5Min": 0,
"staminaSwitch": false,
"item5": 0,
"staminaValue": 900,
"capSwitch": false,
"item3": 0,
"capValue": 0,
"item2Max": 0,
"item4": 0
},
"shovel": 9596,
"buySuppliesCap": 0,
"jewelleryEquipper": {
"pzCheck": true,
"ammyMin": 30,
"ringMin": 30,
"valueAmmy": false,
"ringMax": 80,
"valueRing": false,
"ringId": 3048,
"ammyId": 3081,
"ammySwitch": true,
"ringValue": "HP",
"ringSwitch": true,
"ammyMax": 80,
"ammyValue": "HP"
},
"attackbot": {
"pvpSafe": true,
"enabled": false,
"attackTable": [
{
"dist": 5,
"minMonsters": 4,
"pvp": false,
"category": 2,
"model": 3,
"attack": "exevo gran mas tera",
"manaCost": 25
},
{
"dist": 4,
"minMonsters": 2,
"pvp": false,
"category": 8,
"model": 11,
"attack": 3161,
"manaCost": 10
},
{
"dist": 4,
"minMonsters": 1,
"pvp": false,
"category": 6,
"model": 2,
"attack": "exori max frigo",
"manaCost": 10
},
{
"dist": 4,
"minMonsters": 2,
"pvp": false,
"category": 5,
"model": 8,
"attack": "exevo gran frigo hur",
"manaCost": 10
},
{
"dist": 9,
"minMonsters": 1,
"pvp": false,
"category": 7,
"model": 2,
"attack": 3155,
"manaCost": 1
},
{
"dist": 4,
"minMonsters": 1,
"pvp": false,
"category": 6,
"model": 2,
"attack": "exori frigo",
"manaCost": 1
},
{
"dist": 4,
"minMonsters": 1,
"pvp": true,
"category": 6,
"model": 2,
"attack": "exori max frigo",
"manaCost": 1
},
{
"dist": 9,
"manaCost": 1,
"pvp": true,
"category": 7,
"model": 2,
"attack": 3155,
"minMonsters": 1
}
],
"pvpMode": false
},
"scythe": 9596,
"inboxContainerOpen": false,
"advancedFriendHealer": {
"itemHeal": true,
"minFriendHp": 40,
"id": 3160,
"minMana": 60,
"distance": 8,
"spellHeal": true,
"spellName": "exura sio"
},
"autoTradeMessage": "I'm using OTClientV8!",
"BotServerChannel": "6797953434510",
"combobot": {
"attackSpellEnabled": false,
"attackLeaderTargetEnabled": false,
"onShootEnabled": false,
"item": 3155,
"followLeaderEnabled": false,
"serverTriggers": true,
"serverEnabled": false,
"attack": "",
"onSayEnabled": false,
"sayLeader": "",
"spell": "",
"shootLeader": "",
"attackItemToggle": false,
"serverLeaderTarget": false,
"commandsEnabled": true,
"sayPhrase": "",
"onCastEnabled": false,
"serverLeader": "",
"enabled": false,
"follow": "",
"castLeader": ""
},
"antiPushPanel": {
"items": [
3031,
3035,
0,
0,
0
]
},
"_macros": {
"": false,
"Stake Bodies": false,
"Supply Sorter": false
},
"lootContainerOpen": false,
"sellAllCap": 0,
"_configs": {
"targetbot_configs": {
"enabled": false
},
"cavebot_configs": {
"enabled": false
}
},
"trashItems": [
{
"count": 1,
"id": 283
},
{
"count": 1,
"id": 284
},
{
"count": 1,
"id": 285
}
],
"serverMembers": "[\"Otcliento\"]",
"stopSearch": false,
"BOTserver": {
"manaInfo": true,
"mwallInfo": true,
"mwalls": [
]
},
"ConditionPanel": {
"cureBurn": false,
"curseCost": 80,
"electrifyCost": 22,
"ignoreInPz": true,
"bleedCost": 45,
"cureCurse": false,
"holdHaste": false,
"curePosion": false,
"hasteSpell": "utani hur",
"cureParalyse": false,
"cureBleed": false,
"holdUtura": false,
"paralyseSpell": "utani hur",
"poisonCost": 20,
"utanaCost": 440,
"uturaType": "",
"utamoCost": 40,
"enabled": false,
"uturaCost": 100,
"cureElectrify": false,
"holdUtamo": false,
"paralyseCost": 40,
"hasteCost": 40,
"holdUtana": false,
"burnCost": 30
},
"machete": 9596,
"alarms": {
"playerDetected": false,
"creatureDetected": false,
"manaValue": 50,
"playerAttack": false,
"manaBelow": false,
"privateMessage": false,
"playerDetectedLogout": false,
"enabled": false,
"healthBelow": false,
"healthValue": 40
},
"playerList": {
"enemyList": [
],
"outfits": false,
"groupMembers": true,
"marks": false,
"friendList": [
]
},
"pushmax": {
"pushMaxRuneId": 3188,
"mwallBlockId": 2128,
"pushDelay": 1060,
"enabled": true,
"pushMaxKey": "PageUp"
},
"AnalysersPanel": {
"lootItems": [
],
"bestHit": 0,
"bestHeal": 0
},
"posCheckRetries": 0,
"killSteal": {
"setting": true,
"enabled": false,
"hp": 20,
"id": 3155
}
}

View File

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

View File

@ -377,7 +377,7 @@ macro(100, function()
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
if CaveBot.walkTo(p, 20, {ignoreNonPathable=true, precision=1, ignoreStairs=false}) then
delay(100)
end
end)

View File

@ -89,14 +89,8 @@ 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())
@ -140,19 +134,31 @@ end)
local resetTimer = now
onKeyDown(function(keys)
if not target or not storage[pushPanelName].enabled then return end
if 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
if not target then
local tile = getTileUnderCursor()
if tile and getDistanceBetween(pos(), tile:getPosition()) <= 1 then
if tile:getCreatures()[1] then
target = tile:getCreatures()[1]
tile:setText("PUSH TARGET")
end
end
else
local tile = getTileUnderCursor()
if tile and not tile:getCreatures()[1] then
targetTile = tile
tile:setText("DESTINATION")
end
end
resetTimer = now
end
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
if not storage[pushPanelName].enabled then return end
if keys == storage.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
@ -167,9 +173,11 @@ onKeyPress(function(keys)
resetTimer = 0
end
end)
onCreaturePositionChange(function(creature, newPos, oldPos)
if target and target:isPlayer() and storage[pushPanelName].enabled then
if target and storage[pushPanelName].enabled then
if creature:getName() == target:getName() then
target = nil
targetTile = nil
for _, tile in ipairs(g_map.getTiles(posz())) do
if getDistanceBetween(pos(), tile:getPosition()) < 3 then

View File

@ -49,6 +49,154 @@ if rootWidget then
healWindow = g_ui.createWidget('HealWindow', rootWidget)
healWindow:hide()
local refreshSpells = function()
if storage[healPanelName].spellTable and #storage[healPanelName].spellTable > 0 then
for i, child in pairs(healWindow.spells.spellList:getChildren()) do
child:destroy()
end
for _, entry in pairs(storage[healPanelName].spellTable) do
local label = g_ui.createWidget("SpellEntry", healWindow.spells.spellList)
label.enabled:setChecked(entry.enabled)
label.enabled.onClick = function(widget)
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
end
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
end
refreshSpells()
local refreshItems = function()
if storage[healPanelName].itemTable and #storage[healPanelName].itemTable > 0 then
for i, child in pairs(healWindow.items.itemList:getChildren()) do
child:destroy()
end
for _, entry in pairs(storage[healPanelName].itemTable) do
local label = g_ui.createWidget("SpellEntry", healWindow.items.itemList)
label.enabled:setChecked(entry.enabled)
label.enabled.onClick = function(widget)
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
end
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
end
refreshItems()
healWindow.spells.MoveUp.onClick = function(widget)
local input = healWindow.spells.spellList:getFocusedChild()
if not input then return end
local index = healWindow.spells.spellList:getChildIndex(input)
if index < 2 then return end
local move
if storage[healPanelName].spellTable and #storage[healPanelName].spellTable > 0 then
for _, entry in pairs(storage[healPanelName].spellTable) do
if entry.index == index -1 then
move = entry
end
if entry.index == index then
move.index = index
entry.index = index -1
end
end
end
table.sort(storage[healPanelName].spellTable, function(a,b) return a.index < b.index end)
healWindow.spells.spellList:moveChildToIndex(input, index - 1)
healWindow.spells.spellList:ensureChildVisible(input)
end
healWindow.spells.MoveDown.onClick = function(widget)
local input = healWindow.spells.spellList:getFocusedChild()
if not input then return end
local index = healWindow.spells.spellList:getChildIndex(input)
if index >= healWindow.spells.spellList:getChildCount() then return end
local move
local move2
if storage[healPanelName].spellTable and #storage[healPanelName].spellTable > 0 then
for _, entry in pairs(storage[healPanelName].spellTable) do
if entry.index == index +1 then
move = entry
end
if entry.index == index then
move2 = entry
end
end
if move and move2 then
move.index = index
move2.index = index + 1
end
end
table.sort(storage[healPanelName].spellTable, function(a,b) return a.index < b.index end)
healWindow.spells.spellList:moveChildToIndex(input, index + 1)
healWindow.spells.spellList:ensureChildVisible(input)
end
healWindow.items.MoveUp.onClick = function(widget)
local input = healWindow.items.itemList:getFocusedChild()
if not input then return end
local index = healWindow.items.itemList:getChildIndex(input)
if index < 2 then return end
local move
if storage[healPanelName].itemTable and #storage[healPanelName].itemTable > 0 then
for _, entry in pairs(storage[healPanelName].itemTable) do
if entry.index == index -1 then
move = entry
end
if entry.index == index then
move.index = index
entry.index = index - 1
end
end
end
table.sort(storage[healPanelName].itemTable, function(a,b) return a.index < b.index end)
healWindow.items.itemList:moveChildToIndex(input, index - 1)
healWindow.items.itemList:ensureChildVisible(input)
end
healWindow.items.MoveDown.onClick = function(widget)
local input = healWindow.items.itemList:getFocusedChild()
if not input then return end
local index = healWindow.items.itemList:getChildIndex(input)
if index >= healWindow.items.itemList:getChildCount() then return end
local move
local move2
if storage[healPanelName].itemTable and #storage[healPanelName].itemTable > 0 then
for _, entry in pairs(storage[healPanelName].itemTable) do
if entry.index == index +1 then
move = entry
end
if entry.index == index then
move2 = entry
end
end
if move and move2 then
move.index = index
move2.index = index + 1
end
end
table.sort(storage[healPanelName].itemTable, function(a,b) return a.index < b.index end)
healWindow.items.itemList:moveChildToIndex(input, index + 1)
healWindow.items.itemList:ensureChildVisible(input)
end
healWindow.spells.addSpell.onClick = function(widget)
local spellFormula = healWindow.spells.spellFormula:getText():trim()
@ -93,17 +241,12 @@ if rootWidget then
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)
table.insert(storage[healPanelName].spellTable, {index = #storage[healPanelName].spellTable+1, spell = spellFormula, sign = equasion, origin = source, cost = manaCost, value = spellTrigger, enabled = true})
healWindow.spells.spellFormula:setText('')
healWindow.spells.spellValue:setText('')
healWindow.spells.manaCost:setText('')
end
refreshSpells()
end
healWindow.items.addItem.onClick = function(widget)
@ -117,12 +260,11 @@ if rootWidget then
if not trigger then
warn("HealBot: incorrect trigger value!")
healWindow.items.id:setItemId(0)
healWindow.items.trigger:setText('')
healWindow.items.itemId:setItemId(0)
healWindow.items.itemValue:setText('')
return
end
if src == "Current Mana" then
source = "MP"
elseif src == "Current Health" then
@ -142,37 +284,10 @@ if rootWidget then
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)
table.insert(storage[healPanelName].itemTable, {index = #storage[healPanelName].itemTable+1,item = id, sign = equasion, origin = source, value = trigger, enabled = true})
refreshItems()
healWindow.items.itemId:setItemId(0)
healWindow.items.itemValue:setText('')
end
end
@ -186,7 +301,7 @@ 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 mana() >= tonumber(entry.cost) and not getSpellCoolDown(entry.spell) and entry.enabled then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
say(entry.spell)
@ -242,7 +357,7 @@ macro(500, function()
for _, entry in pairs(storage[healPanelName].itemTable) do
local item = findItem(entry.item)
if item then
if item and entry.enabled then
if entry.origin == "HP%" then
if entry.sign == "=" and hppercent() == entry.value then
useWith(entry.item, player)

View File

@ -71,7 +71,8 @@ local categories = {
"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.)"
"Area Rune (great fireball, avalanche etc.)",
"Empowerment (utito tempo)"
}
local labels = {
@ -83,6 +84,7 @@ local labels = {
"Targeted Spell",
"Targeted Rune",
"Area Rune",
"Buff"
}
local range = {
@ -109,7 +111,8 @@ local pattern = {
"Small Wave (gran frigo hur)",
"Beam (exevo vis lux)",
"Adjacent (exori)",
"Area Rune (GFB, AVA)"
"Area Rune (GFB, AVA)",
"Empowerment"
}
local updateModeText = function()
@ -290,6 +293,86 @@ if rootWidget then
inputTypeToggle()
end
local refreshAttacks = function()
if storage[attackPanelName].attackTable and #storage[attackPanelName].attackTable > 0 then
for i, child in pairs(attackWindow.attackList:getChildren()) do
child:destroy()
end
for _, entry in pairs(storage[attackPanelName].attackTable) do
local label = g_ui.createWidget("AttackEntry", attackWindow.attackList)
label.enabled:setChecked(entry.enabled)
label.enabled.onClick = function(widget)
entry.enabled = not entry.enabled
label.enabled:setChecked(entry.enabled)
end
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: ".. entry.dist .. ")")
label:setColor("yellow")
else
label:setText("(" .. entry.manaCost .. "% MP & mob >= " .. entry.minMonsters .. ") " .. labels[entry.category] .. ": " .. entry.attack .. " (Range: ".. entry.dist .. ")")
label:setColor("green")
end
end
end
end
refreshAttacks()
attackWindow.MoveUp.onClick = function(widget)
local input = attackWindow.attackList:getFocusedChild()
if not input then return end
local index = attackWindow.attackList:getChildIndex(input)
if index < 2 then return end
local move
if storage[attackPanelName].attackTable and #storage[attackPanelName].attackTable > 0 then
for _, entry in pairs(storage[attackPanelName].attackTable) do
if entry.index == index -1 then
move = entry
end
if entry.index == index then
move.index = index
entry.index = index -1
end
end
end
table.sort(storage[attackPanelName].attackTable, function(a,b) return a.index < b.index end)
attackWindow.attackList:moveChildToIndex(input, index - 1)
attackWindow.attackList:ensureChildVisible(input)
end
attackWindow.MoveDown.onClick = function(widget)
local input = attackWindow.attackList:getFocusedChild()
if not input then return end
local index = attackWindow.attackList:getChildIndex(input)
if index >= attackWindow.attackList:getChildCount() then return end
local move
local move2
if storage[attackPanelName].attackTable and #storage[attackPanelName].attackTable > 0 then
for _, entry in pairs(storage[attackPanelName].attackTable) do
if entry.index == index +1 then
move = entry
end
if entry.index == index then
move2 = entry
end
end
if move and move2 then
move.index = index
move2.index = index + 1
end
end
table.sort(storage[attackPanelName].attackTable, function(a,b) return a.index < b.index end)
attackWindow.attackList:moveChildToIndex(input, index + 1)
attackWindow.attackList:ensureChildVisible(input)
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
@ -306,36 +389,11 @@ if rootWidget then
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
table.insert(storage[attackPanelName].attackTable, {index = #storage[attackPanelName].attackTable+1, attack = val, manaCost = tonumber(attackWindow.minMana:getText()), minMonsters = tonumber(attackWindow.minMonsters:getText()), pvp = pvpDedicated, dist = j-1, model = k, category = i, enabled = true})
refreshAttacks()
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
@ -345,6 +403,7 @@ end
-- k = pattern - covered
local patterns = {
"",
"",
[[
0000001000000
@ -440,6 +499,7 @@ local patterns = {
}
local safePatterns = {
"",
"",
[[
000000010000000
@ -571,7 +631,7 @@ local posW = [[
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
if #storage[attackPanelName].attackTable == 0 or isInPz() or not target() or modules.game_cooldown.isGroupCooldownIconActive(1) then return end
local monstersN = 0
local monstersE = 0
@ -606,53 +666,71 @@ macro(1000, function()
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())
if entry.enabled then
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 then
if type(entry.attack) == "string" and target():canShoot() then
say(entry.attack)
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
else
if not storage.isUsing and target():canShoot() 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())
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
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 getBestTileByPatern(patterns[5], 2, entry.dist, storage[attackPanelName].pvpSafe) and getBestTileByPatern(patterns[5], 2, entry.dist, storage[attackPanelName].pvpSafe).count >= entry.minMonsters then
if not storage.isUsing then
useWith(entry.attack, getBestTileByPatern(patterns[5], 2, entry.dist, storage[attackPanelName].pvpSafe).pos:getTopUseThing())
end
return
elseif entry.category == 9 and not isBuffed() and getMonsters(entry.dist) >= entry.minMonsters then
say(entry.attack)
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
return
else
if entry.category == 6 or entry.category == 7 then
if getMonsters(4) >= entry.minMonsters then
@ -668,20 +746,6 @@ macro(1000, function()
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

View File

@ -1,9 +1,18 @@
AttackEntry < Label
background-color: alpha
text-offset: 2 0
text-offset: 18 0
focusable: true
height: 16
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
$focus:
background-color: #00000055
@ -17,7 +26,7 @@ AttackEntry < Label
AttackWindow < MainWindow
!text: tr('AttackBot')
size: 490 350
size: 520 350
@onEscape: self:hide()
TextList
@ -26,7 +35,7 @@ AttackWindow < MainWindow
anchors.right: parent.right
anchors.top: parent.top
padding: 1
size: 470 150
size: 500 150
margin-left: 3
margin-top: 3
margin-left: 3
@ -147,7 +156,7 @@ AttackWindow < MainWindow
Label
anchors.left: parameter2Prev.left
anchors.top: parameter2Prev.bottom
margin-top: 17
margin-top: 10
text-align: center
text: Min Monsters:
@ -173,21 +182,39 @@ AttackWindow < MainWindow
CheckBox
id: pvpSpell
anchors.left: minMonsters.right
anchors.right: minMana.right
anchors.top: minMana.bottom
width: 100
margin-left: 10
anchors.verticalCenter: minMonsters.verticalCenter
margin-left: 30
margin-top: 6
text: Spell for PVP
Button
id: addButton
anchors.horizontalCenter: pvpSpell.horizontalCenter
anchors.verticalCenter: minMana.verticalCenter
anchors.right: CloseButton.right
anchors.bottom: BottomSeparator.top
text-align: center
text: Add
margin-left: 5
margin-left: 5
size: 45 21
margin-bottom: 10
size: 80 20
margin-right: 15
Button
id: MoveUp
anchors.right: prev.right
anchors.bottom: prev.top
size: 80 20
text: Move Up
margin-bottom: 2
Button
id: MoveDown
anchors.right: prev.right
anchors.bottom: prev.top
size: 80 20
text: Move Down
text-align: center
margin-bottom: 2
HorizontalSeparator
id: BottomSeparator

View File

@ -17,10 +17,19 @@ SpellConditionBox < ComboBox
SpellEntry < Label
background-color: alpha
text-offset: 2 0
text-offset: 18 0
focusable: true
height: 16
CheckBox
id: enabled
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 15
height: 15
margin-top: 2
margin-left: 3
$focus:
background-color: #00000055
@ -53,7 +62,7 @@ SpellHealing < Panel
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 460 130
size: 490 130
Label
id: whenSpell
@ -124,7 +133,7 @@ SpellHealing < Panel
anchors.left: parent.left
anchors.bottom: parent.bottom
padding: 1
size: 240 116
size: 270 116
margin-bottom: 3
margin-left: 3
vertical-scrollbar: spellListScrollBar
@ -171,7 +180,7 @@ ItemHealing < Panel
image-source: /images/ui/panel_flat
image-border: 6
padding: 3
size: 460 130
size: 490 130
Label
id: whenItem
@ -228,7 +237,7 @@ ItemHealing < Panel
anchors.left: parent.left
anchors.bottom: parent.bottom
padding: 1
size: 240 116
size: 270 116
margin-top: 3
margin-bottom: 3
margin-left: 3
@ -274,7 +283,7 @@ ItemHealing < Panel
HealWindow < MainWindow
!text: tr('Self Healer')
size: 490 350
size: 520 350
@onEscape: self:hide()
SpellHealing

View File

@ -1,13 +1,15 @@
-- lib ver 1.4
-- lib ver 1.41
-- 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
local var = false
for i=1,4 do
if (player:getSkillLevel(i) - player:getSkillBaseLevel(i)) > 5 and (4*(player:getSkillLevel(i) - player:getSkillBaseLevel(i))) < player:getSkillLevel(i) then
var = true
end
end
return var
end
function killsToRs()
@ -17,7 +19,6 @@ 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
@ -607,7 +608,7 @@ function reachGroundItem(id)
end
end
if distanceFromPlayer(targetTile) > 1 then
if autoWalk(targetTile, 10, {ignoreNonPathable = true, precision=1}) then
if CaveBot.walkTo(targetTile, 10, {ignoreNonPathable = true, precision=1}) then
delay(200)
end
else
@ -627,7 +628,7 @@ function useGroundItem(id)
end
if targetTile then
if distanceFromPlayer(targetTile) > 1 then
if autoWalk(targetTile, 20, {ignoreNonWalkable = true, ignoreNonPathable = true, precision=1}) then
if CaveBot.walkTo(targetTile, 20, {ignoreNonWalkable = true, ignoreNonPathable = true, precision=1}) then
delay(200)
end
else

View File

@ -105,7 +105,7 @@ if storage[analyserPanelName].lootItems and #storage[analyserPanelName].lootItem
end
lootListWindow.AddLoot.onClick = function(widget)
local lootName = lootListWindow.LootName:getText()
local lootName = lootListWindow.LootName:getText():lower()
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)

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

@ -43,7 +43,7 @@ CaveBot.Extensions.Bank.setup = function()
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})
CaveBot.walkTo(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end

View File

@ -46,7 +46,7 @@ CaveBot.Extensions.BuySupplies.setup = function()
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})
CaveBot.walkTo(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end

View File

@ -82,7 +82,7 @@ CaveBot.Extensions.DWithdraw.setup = function()
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
if CaveBot.walkTo(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end

View File

@ -126,7 +126,7 @@ CaveBot.Extensions.Depositor.setup = function()
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
if CaveBot.walkTo(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end

View File

@ -20,6 +20,10 @@ addExampleFunction("Click to browse example functions", [[
return true
]])
addExampleFunction("Check for stamina and imbues", [[
if stamina() < 900 or player:getSkillLevel(11) ~= 100 then CaveBot.setOff() return false else return true end
]])
addExampleFunction("buy 200 mana potion from npc Eryn", [[
--buy 200 mana potions
local npc = getCreatureByName("Eryn")

View File

@ -79,7 +79,7 @@ CaveBot.Extensions.InWithdraw.setup = function()
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
if CaveBot.walkTo(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
@ -150,7 +150,7 @@ CaveBot.Extensions.InWithdraw.setup = function()
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
if container:getCapacity() > #container:getItems() 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

View File

@ -38,7 +38,7 @@ CaveBot.Extensions.SellAll.setup = function()
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})
CaveBot.walkTo(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end

View File

@ -13,43 +13,43 @@ CaveBot.Extensions.SupplyCheck.setup = function()
if storage.supplyRetries > 50 then
print("CaveBot[SupplyCheck]: Round limit reached, going back on refill.")
storage.supplyRetries = 0
return true
return false
elseif (storage[suppliesPanelName].imbues and player:getSkillLevel(11) ~= 100) then
print("CaveBot[SupplyCheck]: Imbues ran out. Going on refill.")
storage.supplyRetries = 0
return true
return false
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
return false
elseif (softCount < 1 and storage[suppliesPanelName].SoftBoots) then
print("CaveBot[SupplyCheck]: No soft boots left. Going on refill.")
storage.supplyRetries = 0
return true
return false
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
return false
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
return false
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
return false
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
return false
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
return false
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
return false
else
print("CaveBot[SupplyCheck]: Enough supplies. Hunting. Round (" .. storage.supplyRetries .. "/50)")
storage.supplyRetries = storage.supplyRetries + 1

View File

@ -30,7 +30,7 @@ CaveBot.Extensions.Travel.setup = function()
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})
CaveBot.walkTo(npcPos, 20, {ignoreNonPathable = true, precision=3})
delay(300)
return "retry"
end

View File

@ -82,7 +82,7 @@ CaveBot.Extensions.Withdraw.setup = function()
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
if CaveBot.walkTo(dest:getPosition(), {ignoreNonPathable=true}) then
storage.stopSearch = true
delay(100)
end
@ -186,7 +186,7 @@ CaveBot.Extensions.Withdraw.setup = function()
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
if container:getCapacity() > #container:getItems() 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

View File

@ -44,7 +44,7 @@ targetbotMacro = macro(100, function()
local highestPriorityParams = nil
for i, creature in ipairs(creatures) do
local path = findPath(player:getPosition(), creature:getPosition(), 7, {ignoreLastCreature=true, ignoreNonPathable=true, ignoreCost=true})
if creature:isMonster() and path then
if creature:isMonster() and creature:getType() < 3 and path then
local params = TargetBot.Creature.calculateParams(creature, path) -- return {craeture, config, danger, priority}
dangerLevel = dangerLevel + params.danger
if params.priority > 0 then

View File

@ -0,0 +1,10 @@
macro(50, function()
if not g_game.isAttacking() then return end
if target() and target():isNpc() then
NPC.say("hi")
NPC.say("trade")
end
delay(950)
end)

View File

@ -1,11 +1,22 @@
setDefaultTab("Cave")
UI.Separator()
function containerIsFull(c)
if not c then return false end
if c:getCapacity() > #c:getItems() then
return false
else
return true
end
end
-- config
local ammoBp = "crystal backpack"
local potionBp = "camouflage backpack"
local runeBp = "yellow backpack"
local runeBp = "red backpack"
-- script
@ -39,7 +50,7 @@ macro(500, "Supply Sorter", function()
for i, container in pairs(getContainers()) do
if (container:getName():lower() ~= potionBp:lower() and (string.find(container:getName(), "backpack") or string.find(container:getName(), "bag") or string.find(container:getName(), "chess"))) and not string.find(container:getName():lower(), "loot") then
for j, item in pairs(container:getItems()) do
if table.find(potions, item:getId()) then
if table.find(potions, item:getId()) and not containerIsFull(potionsContainer) then
g_game.move(item, potionsContainer:getSlotPosition(potionsContainer:getItemsCount()), item:getCount())
end
end
@ -52,7 +63,7 @@ macro(500, "Supply Sorter", function()
for i, container in pairs(getContainers()) do
if (container:getName():lower() ~= runeBp:lower() and (string.find(container:getName(), "backpack") or string.find(container:getName(), "bag") or string.find(container:getName(), "chess"))) and not string.find(container:getName():lower(), "loot") then
for j, item in pairs(container:getItems()) do
if table.find(runes, item:getId()) then
if table.find(runes, item:getId()) and not containerIsFull(runesContainer) then
g_game.move(item, runesContainer:getSlotPosition(runesContainer:getItemsCount()), item:getCount())
end
end
@ -65,7 +76,7 @@ macro(500, "Supply Sorter", function()
for i, container in pairs(getContainers()) do
if (container:getName():lower() ~= ammoBp:lower() and (string.find(container:getName(), "backpack") or string.find(container:getName(), "bag") or string.find(container:getName(), "chess"))) and not string.find(container:getName():lower(), "loot") then
for j, item in pairs(container:getItems()) do
if table.find(ammo, item:getId()) then
if table.find(ammo, item:getId()) and not containerIsFull(ammoContainer) then
g_game.move(item, ammoContainer:getSlotPosition(ammoContainer:getItemsCount()), item:getCount())
end
end

View File

@ -1384,7 +1384,7 @@ function Market.onMarketEnter(depotItems, offers, balance, vocation, items)
for i = 1, #marketItems[MarketCategory.TibiaCoins] do
local item = marketItems[MarketCategory.TibiaCoins][i].displayItem
depotItems[item:getId()] = tibiaCoins
information.depotItems[item:getId()] = tibiaCoins
end
-- update the items widget to match depot items
@ -1425,9 +1425,10 @@ end
function Market.onCoinBalance(coins, transferableCoins)
tibiaCoins = coins
if not information or type(information.depotItems) ~= "table" then return end
if not marketItems[MarketCategory.TibiaCoins] then return end
for i = 1, #marketItems[MarketCategory.TibiaCoins] do
local item = marketItems[MarketCategory.TibiaCoins][i].displayItem
depotItems[item:getId()] = tibiaCoins
information.depotItems[item:getId()] = tibiaCoins
end
end

View File

@ -282,24 +282,13 @@ function onSkillButtonClick(button)
end
function onExperienceChange(localPlayer, value)
local postFix = ""
if value > 1e15 then
postFix = "B"
value = math.floor(value / 1e9)
elseif value > 1e12 then
postFix = "M"
value = math.floor(value / 1e6)
elseif value > 1e9 then
postFix = "K"
value = math.floor(value / 1e3)
end
setSkillValue('experience', comma_value(value) .. postFix)
setSkillValue('experience', comma_value(value))
end
function onLevelChange(localPlayer, value, percent)
setSkillValue('level', value)
local text = tr('You have %s percent to go', 100 - percent) .. '\n' ..
tr('%s of experience left', expToAdvance(localPlayer:getLevel(), localPlayer:getExperience()))
tr('%s of experience left', comma_value(expToAdvance(localPlayer:getLevel(), localPlayer:getExperience())))
if localPlayer.expSpeed ~= nil then
local expPerHour = math.floor(localPlayer.expSpeed * 3600)
@ -308,7 +297,7 @@ function onLevelChange(localPlayer, value, percent)
local hoursLeft = (nextLevelExp - localPlayer:getExperience()) / expPerHour
local minutesLeft = math.floor((hoursLeft - math.floor(hoursLeft))*60)
hoursLeft = math.floor(hoursLeft)
text = text .. '\n' .. tr('%d of experience per hour', expPerHour)
text = text .. '\n' .. comma_value(expPerHour) .. tr(' of experience per hour')
text = text .. '\n' .. tr('Next level in %d hours and %d minutes', hoursLeft, minutesLeft)
end
end