minecraft-cc-tools/libs/turtle-utils.lua
2024-05-26 00:07:09 +02:00

379 lines
7.9 KiB
Lua

local turtleUtils = {}
local DEFAULT_IDLE_TIME = 2
local DEFAULT_MIN_FUEL_NEEDED = 1000
turtleUtils.isFuelItem = function(item)
item = item or {}
if item.name == 'minecraft:stick' or item.name == 'minecraft:coal_coke_block' or item.name == 'immersiveengineering:coke' or item.name == 'silentgear:netherwood_charcoal' or item.name == 'silentgear:netherwood_charcoal_block' then
return true
end
local tags = item.tags or {}
return tags['minecraft:coals'] or tags['minecraft:logs'] or tags['minecraft:planks'] or tags['minecraft:saplings'] or false
end
turtleUtils.isSeedInSlot = function(slot)
local item = turtle.getItemDetail(slot, true)
local tags = item and item.tags or {}
return tags['forge:seeds'] or tags['mysticalagriculture:seeds'] or false
end
turtleUtils.getInventory = function(side)
side = side or 'front'
local inv = peripheral.wrap(side)
if inv and peripheral.hasType(inv, 'inventory') then
return inv
end
return nil
end
turtleUtils.isInventoryEmpty = function()
for i=1, 16, 1 do
if turtle.getItemCount(i) > 0 then
return false
end
end
return true
end
turtleUtils.getItemName = function(slotIndex)
local item = turtle.getItemDetail(slotIndex)
return item and item.name
end
turtleUtils.getItemCount = function(slotIndex)
return turtle.getItemCount(slotIndex)
end
local function waitFor(predicate, sleepTime)
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while true do
local result = predicate()
if result ~= nil and result ~= false then
return result
end
os.sleep(sleepTime)
end
end
turtleUtils.waitForInventory = function(side, sleepTime)
return waitFor(function()
return turtleUtils.getInventory(side)
end, sleepTime)
end
turtleUtils.waitForMatureCrop = function(inspectFn, sleepTime)
inspectFn = inspectFn or turtle.inspect
if not inspectFn() then
return nil
end
return waitFor(function()
return turtleUtils.getMatureCrop(inspectFn)
end, sleepTime)
end
turtleUtils.trySuck = function(suckFn, sleepTime)
suckFn = suckFn or turtle.suck
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while not suckFn() do
os.sleep(sleepTime)
end
end
turtleUtils.trySuckUp = function()
return turtleUtils.trySuck(turtle.suckUp)
end
turtleUtils.trySuckDown = function()
return turtleUtils.trySuck(turtle.suckDown)
end
turtleUtils.tryDrop = function(dropFn, sleepTime)
dropFn = dropFn or turtle.drop
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while true do
if turtle.getItemCount() == 0 then
return false
end
local dropOk = dropFn();
if (dropOk) then
return true
end
os.sleep(sleepTime)
end
end
turtleUtils.tryDropUp = function()
return turtleUtils.tryDrop(turtle.dropUp)
end
turtleUtils.tryDropDown = function()
return turtleUtils.tryDrop(turtle.dropDown)
end
turtleUtils.dropSlot = function(slotIndex, dropFn)
dropFn = dropFn or turtle.drop
if turtle.getItemCount(slotIndex) == 0 then
return false
end
if turtle.getSelectedSlot() ~= slotIndex then
turtle.select(slotIndex)
end
local ok, err = dropFn()
if not ok then
return false
end
return true
end
turtleUtils.digAll = function()
while true do
local ok, err = turtle.dig()
if not ok then
return ok, err
end
end
end
turtleUtils.countFreeSlots = function()
local freeSlots = 0
for i=1, 16, 1 do
if turtle.getItemCount(i) == 0 then
freeSlots = freeSlots + 1
end
end
return freeSlots
end
turtleUtils.getFuelPercentage = function()
return (turtle.getFuelLevel() / turtle.getFuelLimit()) * 100
end
turtleUtils.getMatureCrop = function(inspectFn)
inspectFn = inspectFn or turtle.inspect
local isBlock, block = inspectFn()
local blockStateAge = block and block.state and block.state.age
if blockStateAge == 7 then
return block.name
end
return nil
end
turtleUtils.selectFirstEmptySlot = function()
for i = 1, 16, 1 do
if turtle.getItemCount(i) == 0 then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.selectItemByName = function(itemName)
for i = 1, 16, 1 do
if itemName == turtleUtils.getItemName(i) then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.selectItemBy = function(predicate)
for i = 1, 16, 1 do
if predicate(i) then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.ensureSelected = function(slot)
if turtle.getSelectedSlot() ~= slot then
return turtle.select(slot)
end
return true
end
turtleUtils.refuel = function(minFuel, suckFn, sleepTime)
suckFn = suckFn or turtle.suck
sleepTime = sleepTime or DEFAULT_IDLE_TIME
if not turtleUtils.selectFirstEmptySlot() then
return false, 'turtle inventory is full'
end
while turtle.getFuelLevel() < minFuel do
local suckRes = suckFn()
if suckRes then
local ok = turtle.refuel()
if not ok then
return false, 'selected item is not considered as fuel'
end
else
os.sleep(sleepTime)
end
end
return true
end
turtleUtils.refuelAllFromInventory = function()
local initialSelectedSlot = turtle.getSelectedSlot()
for i=1, 16, 1 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
turtle.refuel()
end
end
if turtle.getSelectedSlot() ~= initialSelectedSlot then
turtle.select(initialSelectedSlot)
end
end
-- utils functions used by "compactInventory"
local function getItemsMap()
local result = {}
for i=1, 16, 1 do
local itemName = turtleUtils.getItemName(i)
if itemName then
local nbSlotUsed = result[itemName] or 0
result[itemName] = nbSlotUsed + 1
end
end
return result
end
local function getSlotsWithSpaces(itemName)
local slots = {}
for i=1, 16, 1 do
if turtleUtils.getItemName(i) == itemName then
if turtle.getItemSpace(i) > 0 then
table.insert(slots, i)
end
end
end
return slots
end
local function compactItem(itemName)
while true do
local slots = getSlotsWithSpaces(itemName)
if #slots < 2 then
break
end
turtle.select(slots[1])
turtle.transferTo(slots[2], turtle.getItemSpace(slots[2]))
end
end
-- Compact the internal turtle inventory (return the number of freed slots)
turtleUtils.compactInventory = function()
local initialNbFreeSlots = turtleUtils.countFreeSlots()
local initialSelectedSlot = turtle.getSelectedSlot()
local itemsMap = getItemsMap()
for itemName, nbSlotUsed in pairs(itemsMap) do
if nbSlotUsed > 1 then
compactItem(itemName)
end
end
if initialSelectedSlot ~= turtle.getSelectedSlot() then
turtle.select(initialSelectedSlot)
end
return turtleUtils.countFreeSlots() - initialNbFreeSlots
end
-- utils functions used by "refuelWithBuffer"
local function getSuckFn(side)
if side == 'front' then
return turtle.suck
elseif side == 'top' then
return turtle.suckUp
elseif side == 'bottom' then
return turtle.suckDown
end
end
local function findFuelSlot(inventory)
for slot in pairs(inventory.list()) do
local item = inventory.getItemDetail(slot)
if item and turtleUtils.isFuelItem(item) then
return slot
end
end
end
-- Refuel using a buffer chest
turtleUtils.refuelWithBuffer = function(sideFuel, sideBuffer, minFuelNeeded, sleepTime)
sleepTime = sleepTime or DEFAULT_IDLE_TIME
minFuelNeeded = minFuelNeeded or DEFAULT_MIN_FUEL_NEEDED
local suckFn = getSuckFn(sideBuffer)
if not suckFn then
return false, 'refuelWithBuffer: sideBuffer should be "front", "top" or "bottom"'
end
local fuelInventory = turtleUtils.waitForInventory(sideFuel, sleepTime)
local bufferInventory = turtleUtils.waitForInventory(sideBuffer, sleepTime)
local ok, errMessage = turtleUtils.refuel(minFuelNeeded, function()
local fuelSlot = findFuelSlot(fuelInventory)
if fuelSlot ~= nil then
fuelInventory.pushItems(peripheral.getName(bufferInventory), fuelSlot)
end
return suckFn()
end, sleepTime)
return ok, errMessage
end
return turtleUtils