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