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

325 lines
11 KiB
Lua

TargetBot.Looting = {}
TargetBot.Looting.list = {} -- list of containers to loot
local ui
local items = {}
local containers = {}
local itemsById = {}
local containersById = {}
local dontSave = false
TargetBot.Looting.setup = function()
ui = UI.createWidget("TargetBotLootingPanel")
UI.Container(TargetBot.Looting.onItemsUpdate, true, nil, ui.items)
UI.Container(TargetBot.Looting.onContainersUpdate, true, nil, ui.containers)
ui.everyItem.onClick = function()
ui.everyItem:setOn(not ui.everyItem:isOn())
TargetBot.save()
end
ui.maxDangerPanel.value.onTextChange = function()
local value = tonumber(ui.maxDangerPanel.value:getText())
if not value then
ui.maxDangerPanel.value:setText(0)
end
if dontSave then return end
TargetBot.save()
end
ui.minCapacityPanel.value.onTextChange = function()
local value = tonumber(ui.minCapacityPanel.value:getText())
if not value then
ui.minCapacityPanel.value:setText(0)
end
if dontSave then return end
TargetBot.save()
end
end
TargetBot.Looting.onItemsUpdate = function()
if dontSave then return end
TargetBot.save()
TargetBot.Looting.updateItemsAndContainers()
end
TargetBot.Looting.onContainersUpdate = function()
if dontSave then return end
TargetBot.save()
TargetBot.Looting.updateItemsAndContainers()
end
TargetBot.Looting.update = function(data)
dontSave = true
TargetBot.Looting.list = {}
ui.items:setItems(data['items'] or {})
ui.containers:setItems(data['containers'] or {})
ui.everyItem:setOn(data['everyItem'])
ui.maxDangerPanel.value:setText(data['maxDanger'] or 10)
ui.minCapacityPanel.value:setText(data['minCapacity'] or 100)
TargetBot.Looting.updateItemsAndContainers()
dontSave = false
--vBot
vBot.lootConainers = {}
vBot.lootItems = {}
for i, item in ipairs(ui.containers:getItems()) do
table.insert(vBot.lootConainers, item['id'])
end
for i, item in ipairs(ui.items:getItems()) do
table.insert(vBot.lootItems, item['id'])
end
end
TargetBot.Looting.save = function(data)
data['items'] = ui.items:getItems()
data['containers'] = ui.containers:getItems()
data['maxDanger'] = tonumber(ui.maxDangerPanel.value:getText())
data['minCapacity'] = tonumber(ui.minCapacityPanel.value:getText())
data['everyItem'] = ui.everyItem:isOn()
end
TargetBot.Looting.updateItemsAndContainers = function()
items = ui.items:getItems()
containers = ui.containers:getItems()
itemsById = {}
containersById = {}
for i, item in ipairs(items) do
itemsById[item.id] = 1
end
for i, container in ipairs(containers) do
containersById[container.id] = 1
end
end
local waitTill = 0
local waitingForContainer = nil
local status = ""
local lastFoodConsumption = 0
TargetBot.Looting.getStatus = function()
return status
end
TargetBot.Looting.process = function(targets, dangerLevel)
if (not items[1] and not ui.everyItem:isOn()) or not containers[1] then
status = ""
return false
end
if dangerLevel > tonumber(ui.maxDangerPanel.value:getText()) then
status = "High danger"
return false
end
if player:getFreeCapacity() < tonumber(ui.minCapacityPanel.value:getText()) then
status = "No cap"
TargetBot.Looting.list = {}
return false
end
local loot = storage.extras.lootLast and TargetBot.Looting.list[#TargetBot.Looting.list] or TargetBot.Looting.list[1]
if loot == nil then
status = ""
return false
end
if waitTill > now then
return true
end
local containers = g_game.getContainers()
local lootContainers = TargetBot.Looting.getLootContainers(containers)
-- check if there's container for loot and has empty space for it
if not lootContainers[1] then
-- there's no space, don't loot
status = "No space"
return false
end
status = "Looting"
for index, container in pairs(containers) do
if container.lootContainer then
TargetBot.Looting.lootContainer(lootContainers, container)
return true
end
end
local pos = player:getPosition()
local dist = math.max(math.abs(pos.x-loot.pos.x), math.abs(pos.y-loot.pos.y))
local maxRange = storage.extras.looting or 40
if loot.tries > 30 or loot.pos.z ~= pos.z or dist > maxRange then
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
return true
end
local tile = g_map.getTile(loot.pos)
if dist >= 3 or not tile then
loot.tries = loot.tries + 1
TargetBot.walkTo(loot.pos, 20, { ignoreNonPathable = true, precision = 2 })
return true
end
local container = tile:getTopUseThing()
if not container or not container:isContainer() then
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
return true
end
g_game.open(container)
waitTill = now + math.min(g_game.getPing(),100)
waitingForContainer = container:getId()
return true
end
TargetBot.Looting.getLootContainers = function(containers)
local lootContainers = {}
local openedContainersById = {}
local toOpen = nil
for index, container in pairs(containers) do
openedContainersById[container:getContainerItem():getId()] = 1
if containersById[container:getContainerItem():getId()] and not container.lootContainer then
if container:getItemsCount() < container:getCapacity() or container:hasPages() then
table.insert(lootContainers, container)
else -- it's full, open next container if possible
for slot, item in ipairs(container:getItems()) do
if item:isContainer() and containersById[item:getId()] then
toOpen = {item, container}
break
end
end
end
end
end
if not lootContainers[1] then
if toOpen then
g_game.open(toOpen[1], toOpen[2])
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
-- check containers one more time, maybe there's any loot container
for index, container in pairs(containers) do
if not containersById[container:getContainerItem():getId()] and not container.lootContainer then
for slot, item in ipairs(container:getItems()) do
if item:isContainer() and containersById[item:getId()] then
g_game.open(item)
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
end
end
end
-- can't find any lootContainer, let's check slots, maybe there's one
for slot = InventorySlotFirst, InventorySlotLast do
local item = getInventoryItem(slot)
if item and item:isContainer() and not openedContainersById[item:getId()] then
-- container which is not opened yet, let's open it
g_game.open(item)
waitTill = now + 500 -- wait 0.5s
return lootContainers
end
end
end
return lootContainers
end
TargetBot.Looting.lootContainer = function(lootContainers, container)
-- loot items
local nextContainer = nil
for i, item in ipairs(container:getItems()) do
if item:isContainer() and not itemsById[item:getId()] then
nextContainer = item
elseif itemsById[item:getId()] or (ui.everyItem:isOn() and not item:isContainer()) then
item.lootTries = (item.lootTries or 0) + 1
if item.lootTries < 5 then -- if can't be looted within 0.5s then skip it
return TargetBot.Looting.lootItem(lootContainers, item)
end
elseif storage.foodItems and storage.foodItems[1] and lastFoodConsumption + 5000 < now then
for _, food in ipairs(storage.foodItems) do
if item:getId() == food.id then
g_game.use(item)
lastFoodConsumption = now
return
end
end
end
end
-- no more items to loot, open next container
if nextContainer then
nextContainer.lootTries = (nextContainer.lootTries or 0) + 1
if nextContainer.lootTries < 2 then -- max 0.6s to open it
g_game.open(nextContainer, container)
waitTill = now + 300 -- give it 0.3s to open
waitingForContainer = nextContainer:getId()
return
end
end
-- looting finished, remove container from list
container.lootContainer = false
g_game.close(container)
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
end
onTextMessage(function(mode, text)
if TargetBot.isOff() then return end
if #TargetBot.Looting.list == 0 then return end
if string.find(text:lower(), "you are not the owner") then -- if we are not the owners of corpse then its a waste of time to try to loot it
table.remove(TargetBot.Looting.list, storage.extras.lootLast and #TargetBot.Looting.list or 1)
end
end)
TargetBot.Looting.lootItem = function(lootContainers, item)
if item:isStackable() then
local count = item:getCount()
for _, container in ipairs(lootContainers) do
for slot, citem in ipairs(container:getItems()) do
if item:getId() == citem:getId() and citem:getCount() < 100 then
g_game.move(item, container:getSlotPosition(slot - 1), count)
waitTill = now + 300 -- give it 0.3s to move item
return
end
end
end
end
local container = lootContainers[1]
g_game.move(item, container:getSlotPosition(container:getItemsCount()), 1)
waitTill = now + 300 -- give it 0.3s to move item
end
onContainerOpen(function(container, previousContainer)
if container:getContainerItem():getId() == waitingForContainer then
container.lootContainer = true
waitingForContainer = nil
end
end)
onCreatureDisappear(function(creature)
if isInPz() then return end
if not TargetBot.isOn() 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 mpos = creature:getPosition()
local name = creature:getName()
if pos.z ~= mpos.z or math.max(math.abs(pos.x-mpos.x), math.abs(pos.y-mpos.y)) > 6 then return end
schedule(20, function() -- check in 20ms if there's container (dead body) on that tile
if not containers[1] then return end
if TargetBot.Looting.list[20] then return end -- too many items to loot
local tile = g_map.getTile(mpos)
if not tile then return end
local container = tile:getTopUseThing()
if not container or not container:isContainer() then return end
if not findPath(player:getPosition(), mpos, 6, {ignoreNonPathable=true, ignoreCreatures=true, ignoreCost=true}) then return end
table.insert(TargetBot.Looting.list, {pos=mpos, creature=name, container=container:getId(), added=now, tries=0})
table.sort(TargetBot.Looting.list, function(a,b)
a.dist = distanceFromPlayer(a.pos)
b.dist = distanceFromPlayer(b.pos)
return a.dist > b.dist
end)
container:setMarked('#000088')
end)
end)