style: format goo.lua

This commit is contained in:
Guillaume ARM 2026-05-31 11:38:07 +02:00
parent 75d638b42f
commit f4f7e36460

View File

@ -1,385 +1,390 @@
local _VERSION = '0.2.0'; local _VERSION = "0.2.0"
local args = table.pack(...); local args = table.pack(...)
local command = args[1]; local command = args[1]
local GOO_BLOCK_PREFIX = 'justdirethings:gooblock_tier'; local GOO_BLOCK_PREFIX = "justdirethings:gooblock_tier"
local MIN_START_FUEL = 50; local MIN_START_FUEL = 50
local REFUEL_THRESHOLD = 1000; local REFUEL_THRESHOLD = 1000
local WAIT_SECONDS = 2; local WAIT_SECONDS = 2
local FUEL_ITEMS = { local FUEL_ITEMS = {
'justdirethings:coal_t4', "justdirethings:coal_t4",
'justdirethings:coal_t3', "justdirethings:coal_t3",
'justdirethings:coal_t2', "justdirethings:coal_t2",
'justdirethings:coal_t1', "justdirethings:coal_t1",
'minecraft:coal', "minecraft:coal",
}; }
local PROCESS_ITEMS = { local PROCESS_ITEMS = {
['minecraft:iron_block'] = { tier = 1, label = 'iron' }, ["minecraft:iron_block"] = { tier = 1, label = "iron" },
['minecraft:coal_block'] = { tier = 1, label = 'coal' }, ["minecraft:coal_block"] = { tier = 1, label = "coal" },
['mekanism:block_charcoal'] = { tier = 1, label = 'charcoal' }, ["mekanism:block_charcoal"] = { tier = 1, label = "charcoal" },
['minecraft:gold_block'] = { tier = 2, label = 'gold' }, ["minecraft:gold_block"] = { tier = 2, label = "gold" },
['minecraft:diamond_block'] = { tier = 3, label = 'diamond' }, ["minecraft:diamond_block"] = { tier = 3, label = "diamond" },
['minecraft:netherite_block'] = { tier = 4, label = 'netherite' }, ["minecraft:netherite_block"] = { tier = 4, label = "netherite" },
}; }
local FEEDING_ITEMS_BY_TIER = { local FEEDING_ITEMS_BY_TIER = {
[1] = { 'minecraft:sugar', 'minecraft:rotten_flesh' }, [1] = { "minecraft:sugar", "minecraft:rotten_flesh" },
[2] = { 'minecraft:nether_wart' }, [2] = { "minecraft:nether_wart" },
[3] = { 'minecraft:chorus_fruit' }, [3] = { "minecraft:chorus_fruit" },
[4] = { 'minecraft:sculk' }, [4] = { "minecraft:sculk" },
}; }
local loggedBlockedItems = {}; local loggedBlockedItems = {}
local function isFlag(name) local function isFlag(name)
return function(arg) return function(arg)
return arg == '-' .. name or arg == '--' .. name; return arg == "-" .. name or arg == "--" .. name
end end
end end
local isHelpFlag = isFlag('help'); local isHelpFlag = isFlag("help")
local isVersionFlag = isFlag('version'); local isVersionFlag = isFlag("version")
local function printUsage() local function printUsage()
print('goo usage:'); print("goo usage:")
print(); print()
print('\t\t\tgoo start'); print("\t\t\tgoo start")
print('\t\t\tgoo version'); print("\t\t\tgoo version")
print('\t\t\tgoo help'); print("\t\t\tgoo help")
end end
if command == 'version' or isVersionFlag(command) then if command == "version" or isVersionFlag(command) then
print('goo v' .. _VERSION); print("goo v" .. _VERSION)
return; return
end end
if command == nil or command == '' or command == 'help' or isHelpFlag(command) then if command == nil or command == "" or command == "help" or isHelpFlag(command) then
printUsage(); printUsage()
return; return
end end
if command ~= 'start' then if command ~= "start" then
printUsage(); printUsage()
return; return
end end
if not turtle then if not turtle then
error('goo must be run on a turtle'); error("goo must be run on a turtle")
end end
local function hasPickaxeEquipped() local function hasPickaxeEquipped()
local left = turtle.getEquippedLeft(); local left = turtle.getEquippedLeft()
local right = turtle.getEquippedRight(); local right = turtle.getEquippedRight()
return (left and string.match(left.name or '', '_pickaxe$') ~= nil) return (left and string.match(left.name or "", "_pickaxe$") ~= nil)
or (right and string.match(right.name or '', '_pickaxe$') ~= nil); or (right and string.match(right.name or "", "_pickaxe$") ~= nil)
end end
local function turnAround() local function turnAround()
turtle.turnRight(); turtle.turnRight()
turtle.turnRight(); turtle.turnRight()
end end
local function parseGooTier(blockName) local function parseGooTier(blockName)
local tier = string.match(blockName or '', '^' .. GOO_BLOCK_PREFIX .. '(%d+)$'); local tier = string.match(blockName or "", "^" .. GOO_BLOCK_PREFIX .. "(%d+)$")
return tonumber(tier); return tonumber(tier)
end end
local function isGooBlock(blockName) local function isGooBlock(blockName)
return parseGooTier(blockName) ~= nil; return parseGooTier(blockName) ~= nil
end end
local function isProcessBlock(blockName) local function isProcessBlock(blockName)
return PROCESS_ITEMS[blockName] ~= nil; return PROCESS_ITEMS[blockName] ~= nil
end end
local function inspectGoo() local function inspectGoo()
local ok, inspected = turtle.inspectUp(); local ok, inspected = turtle.inspectUp()
if not ok then if not ok then
error('expected a Just Dire Things goo block above the turtle'); error("expected a Just Dire Things goo block above the turtle")
end end
local tier = parseGooTier(inspected.name); local tier = parseGooTier(inspected.name)
if not tier then if not tier then
error('expected a Just Dire Things goo block above the turtle, got ' .. tostring(inspected.name)); error("expected a Just Dire Things goo block above the turtle, got " .. tostring(inspected.name))
end end
return tier, inspected; return tier, inspected
end end
local function findItemSlot(itemName) local function findItemSlot(itemName)
for slot = 1, 16 do for slot = 1, 16 do
local item = turtle.getItemDetail(slot); local item = turtle.getItemDetail(slot)
if item and item.name == itemName then if item and item.name == itemName then
return slot, item; return slot, item
end end
end end
return nil; return nil
end end
local function waitForInventory(message) local function waitForInventory(message)
if message then if message then
print(message); print(message)
end end
os.pullEvent('turtle_inventory'); os.pullEvent("turtle_inventory")
end end
local function hasFreeSlot() local function hasFreeSlot()
for slot = 1, 16 do for slot = 1, 16 do
if turtle.getItemCount(slot) == 0 then if turtle.getItemCount(slot) == 0 then
return true; return true
end end
end end
return false; return false
end end
local function ensureMiningSpace() local function ensureMiningSpace()
while not hasFreeSlot() do while not hasFreeSlot() do
waitForInventory('Inventory is full. Remove items to keep mining.'); waitForInventory("Inventory is full. Remove items to keep mining.")
end end
end end
local function getFuelLevel() local function getFuelLevel()
local fuelLevel = turtle.getFuelLevel(); local fuelLevel = turtle.getFuelLevel()
if fuelLevel == 'unlimited' then if fuelLevel == "unlimited" then
return nil; return nil
end end
return fuelLevel; return fuelLevel
end end
local function getRefuelTarget() local function getRefuelTarget()
local fuelLimit = turtle.getFuelLimit(); local fuelLimit = turtle.getFuelLimit()
if fuelLimit == 'unlimited' or fuelLimit >= REFUEL_THRESHOLD then if fuelLimit == "unlimited" or fuelLimit >= REFUEL_THRESHOLD then
return REFUEL_THRESHOLD; return REFUEL_THRESHOLD
end end
return fuelLimit; return fuelLimit
end end
local function findFuelSlot() local function findFuelSlot()
for i = 1, #FUEL_ITEMS do for i = 1, #FUEL_ITEMS do
local slot, item = findItemSlot(FUEL_ITEMS[i]); local slot, item = findItemSlot(FUEL_ITEMS[i])
if slot then if slot then
return slot, item; return slot, item
end end
end end
return nil; return nil
end end
local function refuelFromInventory() local function refuelFromInventory()
local fuelLevel = getFuelLevel(); local fuelLevel = getFuelLevel()
if not fuelLevel or fuelLevel >= getRefuelTarget() then if not fuelLevel or fuelLevel >= getRefuelTarget() then
return false; return false
end end
local previousSlot = turtle.getSelectedSlot(); local previousSlot = turtle.getSelectedSlot()
local consumedFuel = false; local consumedFuel = false
while fuelLevel < getRefuelTarget() do while fuelLevel < getRefuelTarget() do
local slot, item = findFuelSlot(); local slot, item = findFuelSlot()
if not slot then if not slot then
break; break
end end
turtle.select(slot); turtle.select(slot)
if turtle.refuel(1) then if turtle.refuel(1) then
consumedFuel = true; consumedFuel = true
print('Refueled with ' .. item.name .. '. Fuel: ' .. tostring(turtle.getFuelLevel())); print("Refueled with " .. item.name .. ". Fuel: " .. tostring(turtle.getFuelLevel()))
fuelLevel = getFuelLevel(); fuelLevel = getFuelLevel()
else else
print('Could not refuel with ' .. item.name .. '.'); print("Could not refuel with " .. item.name .. ".")
break; break
end end
end end
turtle.select(previousSlot); turtle.select(previousSlot)
return consumedFuel; return consumedFuel
end end
local function ensureStartFuel() local function ensureStartFuel()
while true do while true do
local fuelLevel = getFuelLevel(); local fuelLevel = getFuelLevel()
if not fuelLevel or fuelLevel >= MIN_START_FUEL then if not fuelLevel or fuelLevel >= MIN_START_FUEL then
return; return
end end
refuelFromInventory(); refuelFromInventory()
fuelLevel = getFuelLevel(); fuelLevel = getFuelLevel()
if not fuelLevel or fuelLevel >= MIN_START_FUEL then if not fuelLevel or fuelLevel >= MIN_START_FUEL then
return; return
end end
waitForInventory('Fuel is below ' .. tostring(MIN_START_FUEL) .. '. Insert fuel to start.'); waitForInventory("Fuel is below " .. tostring(MIN_START_FUEL) .. ". Insert fuel to start.")
end end
end end
local function ensureRuntimeFuel() local function ensureRuntimeFuel()
local fuelLevel = getFuelLevel(); local fuelLevel = getFuelLevel()
if not fuelLevel then if not fuelLevel then
return; return
end end
if fuelLevel < getRefuelTarget() then if fuelLevel < getRefuelTarget() then
refuelFromInventory(); refuelFromInventory()
end end
fuelLevel = getFuelLevel(); fuelLevel = getFuelLevel()
while fuelLevel and fuelLevel < MIN_START_FUEL do while fuelLevel and fuelLevel < MIN_START_FUEL do
waitForInventory('Fuel is below ' .. tostring(MIN_START_FUEL) .. '. Insert fuel to continue.'); waitForInventory("Fuel is below " .. tostring(MIN_START_FUEL) .. ". Insert fuel to continue.")
refuelFromInventory(); refuelFromInventory()
fuelLevel = getFuelLevel(); fuelLevel = getFuelLevel()
end end
end end
local function findFeedingSlot(gooTier) local function findFeedingSlot(gooTier)
local feedingItems = FEEDING_ITEMS_BY_TIER[gooTier] or {}; local feedingItems = FEEDING_ITEMS_BY_TIER[gooTier] or {}
for i = 1, #feedingItems do for i = 1, #feedingItems do
local slot, item = findItemSlot(feedingItems[i]); local slot, item = findItemSlot(feedingItems[i])
if slot then if slot then
return slot, item; return slot, item
end end
end end
return nil; return nil
end end
local function findEligibleProcessSlot(gooTier) local function findEligibleProcessSlot(gooTier)
for slot = 1, 16 do for slot = 1, 16 do
local item = turtle.getItemDetail(slot); local item = turtle.getItemDetail(slot)
local processItem = item and PROCESS_ITEMS[item.name]; local processItem = item and PROCESS_ITEMS[item.name]
if processItem then if processItem then
if processItem.tier <= gooTier then if processItem.tier <= gooTier then
return slot, item, processItem; return slot, item, processItem
end end
local logKey = item.name .. ':' .. tostring(gooTier); local logKey = item.name .. ":" .. tostring(gooTier)
if not loggedBlockedItems[logKey] then if not loggedBlockedItems[logKey] then
print(item.name .. ' requires goo tier ' .. tostring(processItem.tier) print(
.. ', current tier is ' .. tostring(gooTier)); item.name
loggedBlockedItems[logKey] = true; .. " requires goo tier "
.. tostring(processItem.tier)
.. ", current tier is "
.. tostring(gooTier)
)
loggedBlockedItems[logKey] = true
end end
end end
end end
return nil; return nil
end end
local function ensureGooAlive() local function ensureGooAlive()
while true do while true do
local gooTier, inspected = inspectGoo(); local gooTier, inspected = inspectGoo()
if inspected.state and inspected.state.alive == true then if inspected.state and inspected.state.alive == true then
return gooTier; return gooTier
end end
local slot, item = findFeedingSlot(gooTier); local slot, item = findFeedingSlot(gooTier)
if not slot then if not slot then
waitForInventory('Goo tier ' .. tostring(gooTier) .. ' is not alive. Waiting for feeding item...'); waitForInventory("Goo tier " .. tostring(gooTier) .. " is not alive. Waiting for feeding item...")
else else
turtle.select(slot); turtle.select(slot)
print('Goo tier ' .. tostring(gooTier) .. ' is not alive. Feeding with ' .. item.name .. '...'); print("Goo tier " .. tostring(gooTier) .. " is not alive. Feeding with " .. item.name .. "...")
if not turtle.placeUp() then if not turtle.placeUp() then
print('Could not feed the goo. Waiting before retry...'); print("Could not feed the goo. Waiting before retry...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
else else
os.sleep(1); os.sleep(1)
end end
end end
end end
end end
local function selectProcessItem(gooTier) local function selectProcessItem(gooTier)
local slot, item, processItem = findEligibleProcessSlot(gooTier); local slot, item, processItem = findEligibleProcessSlot(gooTier)
if not slot then if not slot then
return nil; return nil
end end
turtle.select(slot); turtle.select(slot)
return item, processItem; return item, processItem
end end
local function workTarget(inspectFn, digFn, placeFn, gooTier, targetName) local function workTarget(inspectFn, digFn, placeFn, gooTier, targetName)
local ok, inspected = inspectFn(); local ok, inspected = inspectFn()
if ok then if ok then
if isGooBlock(inspected.name) then if isGooBlock(inspected.name) then
return false; return false
end end
if isProcessBlock(inspected.name) then if isProcessBlock(inspected.name) then
return false; return false
end end
print('Mining processed ' .. targetName .. ' block: ' .. tostring(inspected.name)); print("Mining processed " .. targetName .. " block: " .. tostring(inspected.name))
ensureMiningSpace(); ensureMiningSpace()
return digFn(); return digFn()
end end
local item = selectProcessItem(gooTier); local item = selectProcessItem(gooTier)
if not item then if not item then
return false; return false
end end
print('Placing ' .. item.name .. ' on goo ' .. targetName); print("Placing " .. item.name .. " on goo " .. targetName)
return placeFn(); return placeFn()
end end
local function workHorizontalSide(gooTier, sideIndex) local function workHorizontalSide(gooTier, sideIndex)
if not turtle.forward() then if not turtle.forward() then
print('Cannot move to horizontal side ' .. tostring(sideIndex) .. '. Waiting...'); print("Cannot move to horizontal side " .. tostring(sideIndex) .. ". Waiting...")
return false; return false
end end
local changed = workTarget(turtle.inspectUp, turtle.digUp, turtle.placeUp, gooTier, 'side'); local changed = workTarget(turtle.inspectUp, turtle.digUp, turtle.placeUp, gooTier, "side")
if not turtle.back() then if not turtle.back() then
error('could not return below the goo'); error("could not return below the goo")
end end
return changed; return changed
end end
local function workHorizontalSides(gooTier) local function workHorizontalSides(gooTier)
local changed = false; local changed = false
for sideIndex = 1, 4 do for sideIndex = 1, 4 do
changed = workHorizontalSide(gooTier, sideIndex) or changed; changed = workHorizontalSide(gooTier, sideIndex) or changed
turtle.turnRight(); turtle.turnRight()
end end
return changed; return changed
end end
-- Returns `changed, reachedTop`. `reachedTop` is true only once we have actually -- Returns `changed, reachedTop`. `reachedTop` is true only once we have actually
@ -387,184 +392,184 @@ end
-- the remaining sides; it stays false when this side is blocked or unclimbable. -- the remaining sides; it stays false when this side is blocked or unclimbable.
local function workTopFromCurrentSide(gooTier) local function workTopFromCurrentSide(gooTier)
if not turtle.forward() then if not turtle.forward() then
return false, false; return false, false
end end
-- Only the presence of a block above matters here; ignore the inspect metadata. -- Only the presence of a block above matters here; ignore the inspect metadata.
local sideOccupied = turtle.inspectUp(); local sideOccupied = turtle.inspectUp()
local changed = false; local changed = false
local reachedTop = false; local reachedTop = false
if not sideOccupied then if not sideOccupied then
if turtle.up() then if turtle.up() then
if turtle.up() then if turtle.up() then
turnAround(); turnAround()
changed = workTarget(turtle.inspect, turtle.dig, turtle.place, gooTier, 'top'); changed = workTarget(turtle.inspect, turtle.dig, turtle.place, gooTier, "top")
reachedTop = true; reachedTop = true
turnAround(); turnAround()
if not turtle.down() then if not turtle.down() then
error('could not descend from top placement position'); error("could not descend from top placement position")
end end
else else
print('Cannot climb high enough to work on the top side.'); print("Cannot climb high enough to work on the top side.")
end end
if not turtle.down() then if not turtle.down() then
error('could not return to side floor position'); error("could not return to side floor position")
end end
else else
print('Cannot climb to work on the top side.'); print("Cannot climb to work on the top side.")
end end
end end
if not turtle.back() then if not turtle.back() then
error('could not return below the goo after top side check'); error("could not return below the goo after top side check")
end end
return changed, reachedTop; return changed, reachedTop
end end
local function workTop(gooTier) local function workTop(gooTier)
local changed = false; local changed = false
local done = false; local done = false
-- Iterate all four sides so the four turnRight calls net to zero (facing is -- Iterate all four sides so the four turnRight calls net to zero (facing is
-- restored), but skip the climb once the single top face has been handled. -- restored), but skip the climb once the single top face has been handled.
for _ = 1, 4 do for _ = 1, 4 do
if not done then if not done then
local sideChanged, reachedTop = workTopFromCurrentSide(gooTier); local sideChanged, reachedTop = workTopFromCurrentSide(gooTier)
changed = sideChanged or changed; changed = sideChanged or changed
done = sideChanged or reachedTop; done = sideChanged or reachedTop
end end
turtle.turnRight(); turtle.turnRight()
end end
return changed; return changed
end end
local function turnLeft(times) local function turnLeft(times)
for _ = 1, times do for _ = 1, times do
turtle.turnLeft(); turtle.turnLeft()
end end
end end
local function moveToSideFacingCenter() local function moveToSideFacingCenter()
for turns = 0, 3 do for turns = 0, 3 do
if turtle.forward() then if turtle.forward() then
turnAround(); turnAround()
return turns; return turns
end end
turtle.turnRight(); turtle.turnRight()
end end
return nil; return nil
end end
local function returnFromBottomSide(turns) local function returnFromBottomSide(turns)
if not turtle.forward() then if not turtle.forward() then
return false; return false
end end
turnAround(); turnAround()
turnLeft(turns); turnLeft(turns)
return true; return true
end end
local function workBottom() local function workBottom()
local gooTier = ensureGooAlive(); local gooTier = ensureGooAlive()
local turns = moveToSideFacingCenter(); local turns = moveToSideFacingCenter()
if not turns then if not turns then
print('Cannot move to a side position for bottom placement. Waiting...'); print("Cannot move to a side position for bottom placement. Waiting...")
return false; return false
end end
local changed = false; local changed = false
-- The bottom face is worked as one atomic round-trip (place -> wait -> mine -> -- The bottom face is worked as one atomic round-trip (place -> wait -> mine ->
-- return) rather than fire-and-forget like the other faces, because the turtle -- return) rather than fire-and-forget like the other faces, because the turtle
-- must vacate and then reclaim its home cell within a single pass. -- must vacate and then reclaim its home cell within a single pass.
while true do while true do
local ok, inspected = turtle.inspect(); local ok, inspected = turtle.inspect()
if not ok then if not ok then
if changed then if changed then
if returnFromBottomSide(turns) then if returnFromBottomSide(turns) then
return changed; return changed
end end
print('Center position is clear, but the turtle could not return. Waiting...'); print("Center position is clear, but the turtle could not return. Waiting...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
else else
local item = selectProcessItem(gooTier); local item = selectProcessItem(gooTier)
if item then if item then
print('Placing ' .. item.name .. ' on goo bottom'); print("Placing " .. item.name .. " on goo bottom")
if turtle.place() then if turtle.place() then
changed = true; changed = true
else else
print('Could not place bottom block.'); print("Could not place bottom block.")
end end
elseif returnFromBottomSide(turns) then elseif returnFromBottomSide(turns) then
return changed; return changed
else else
print('Center position is blocked. Waiting...'); print("Center position is blocked. Waiting...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
end end
end end
elseif isProcessBlock(inspected.name) then elseif isProcessBlock(inspected.name) then
print('Bottom block is still processing. Waiting outside...'); print("Bottom block is still processing. Waiting outside...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
elseif isGooBlock(inspected.name) then elseif isGooBlock(inspected.name) then
error('unexpected goo block in the turtle center position'); error("unexpected goo block in the turtle center position")
else else
print('Mining processed bottom block: ' .. tostring(inspected.name)); print("Mining processed bottom block: " .. tostring(inspected.name))
ensureMiningSpace(); ensureMiningSpace()
if turtle.dig() then if turtle.dig() then
changed = true; changed = true
else else
print('Could not mine bottom block. Waiting...'); print("Could not mine bottom block. Waiting...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
end end
end end
end end
end end
print('goo started. Place the turtle directly below the goo block.'); print("goo started. Place the turtle directly below the goo block.")
if not hasPickaxeEquipped() then if not hasPickaxeEquipped() then
error('goo requires a turtle with a pickaxe equipped'); error("goo requires a turtle with a pickaxe equipped")
end end
ensureStartFuel(); ensureStartFuel()
while true do while true do
ensureRuntimeFuel(); ensureRuntimeFuel()
local gooTier = ensureGooAlive(); local gooTier = ensureGooAlive()
-- Also relied on for its side effect: emits the "requires goo tier X" warnings -- Also relied on for its side effect: emits the "requires goo tier X" warnings
-- for blocks the current tier cannot process yet. -- for blocks the current tier cannot process yet.
local hasEligibleBlocks = findEligibleProcessSlot(gooTier) ~= nil; local hasEligibleBlocks = findEligibleProcessSlot(gooTier) ~= nil
local changed = false; local changed = false
changed = workTop(gooTier) or changed; changed = workTop(gooTier) or changed
changed = workHorizontalSides(gooTier) or changed; changed = workHorizontalSides(gooTier) or changed
changed = workBottom() or changed; changed = workBottom() or changed
if not changed then if not changed then
if hasEligibleBlocks then if hasEligibleBlocks then
print('No free goo side found. Waiting for processing...'); print("No free goo side found. Waiting for processing...")
os.sleep(WAIT_SECONDS); os.sleep(WAIT_SECONDS)
else else
waitForInventory('No eligible process block found. Waiting for inventory...'); waitForInventory("No eligible process block found. Waiting for inventory...")
end end
end end
end end