474 lines
9.4 KiB
Lua
474 lines
9.4 KiB
Lua
local turtleUtils = {}
|
|
|
|
local DEFAULT_IDLE_TIME = 2
|
|
local DEFAULT_MIN_FUEL_NEEDED = 1000
|
|
|
|
local function isTurtleInventoryFull()
|
|
for i=1, 16, 1 do
|
|
if turtle.getItemCount(i) == 0 then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
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.isInventoryFull = function(withCompact)
|
|
local isFull = isTurtleInventoryFull()
|
|
|
|
if isFull and withCompact then
|
|
turtleUtils.compactInventory()
|
|
return isTurtleInventoryFull()
|
|
end
|
|
|
|
return isFull
|
|
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.digAllUp = function()
|
|
while true do
|
|
local ok, err = turtle.digUp()
|
|
|
|
if not ok then
|
|
return ok, err
|
|
end
|
|
end
|
|
end
|
|
|
|
turtleUtils.digAllDown = function()
|
|
while true do
|
|
local ok, err = turtle.digDown()
|
|
|
|
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(fuelLevel)
|
|
fuelLevel = fuelLevel or turtle.getFuelLevel()
|
|
local rawPercentage = (fuelLevel / turtle.getFuelLimit()) * 100
|
|
|
|
return math.floor(rawPercentage * 100) / 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.goGround = function()
|
|
while true do
|
|
if turtle.inspectDown() then
|
|
return true
|
|
end
|
|
|
|
local ok, errorMessage = turtle.down()
|
|
|
|
if not ok then
|
|
return false, errorMessage
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
turtleUtils.forceForward = function()
|
|
local ok = turtle.forward()
|
|
|
|
if ok then
|
|
return ok
|
|
end
|
|
|
|
turtleUtils.digAll()
|
|
|
|
return turtle.forward()
|
|
end
|
|
|
|
turtleUtils.forceDown = function()
|
|
local ok = turtle.down()
|
|
|
|
if ok then
|
|
return ok
|
|
end
|
|
|
|
turtle.digAllDown()
|
|
|
|
return turtle.down()
|
|
end
|
|
|
|
turtleUtils.forceUp = function()
|
|
local ok = turtle.up()
|
|
|
|
if ok then
|
|
return ok
|
|
end
|
|
|
|
turtle.digAllUp()
|
|
|
|
return turtle.up()
|
|
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 |