meigrafd

Turtle_Miner:Wireless

Jan 18th, 2026 (edited)
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.11 KB | None | 0 0
  1. -- Turtle Miner (CraftOS / CC:Tweaked)
  2.  
  3. local DEFAULTS = {
  4.     stateFile = "disk:miner.state",
  5.     homeDir = 0,
  6.     startEscapeSteps = 6,
  7.     save = { stepInterval = 12, statusInterval = 1.0, suckCycles = 4, suckSleep = 0.03, moveSleep = 0.02 },
  8.     fuel = { returnReserve = 120, refuelMin = 800 },
  9.     fluids = { blockSlot = 14 },
  10.     scanner = { enable = true, radiusDefault = 8 },
  11.     status = { protocol = "miner" },
  12.     stairs = { steps = 24, width = 3, headroom = 3, torches = true, torchSlot = 15, torchInterval = 6, torchRescanInterval = 12 },
  13.     mine = { roomLen = 16, roomRows = 5, layers = 6, layerDrop = 1, torchInterval = 10, torchRescanInterval = 24 }
  14. }
  15.  
  16. local function scanPeripherals(want)
  17.     local found = {}
  18.     for _, name in ipairs(peripheral.getNames()) do
  19.         local pType = peripheral.getType(name)
  20.         local p = peripheral.wrap(name)
  21.         if (want.modem and not found.modem and pType == "modem") then found.modem = p; found.modemName = name end
  22.         if (want.scanner and not found.scanner and type(p.scan) == "function") then found.scanner = p; found.scannerName = name; found.scannerType = pType end
  23.     end
  24.     return found
  25. end
  26.  
  27. local function parseArgs(args)
  28.     local mode = "both"
  29.     local resume = false
  30.     local goHome = false
  31.     local radius = nil
  32.     for i = 1, #args do
  33.         local a = tostring(args[i] or "")
  34.         local al = string.lower(a)
  35.         if (al == "mine" or al == "stairs" or al == "both") then mode = al end
  36.         if (al == "resume") then resume = true end
  37.         if (al == "home") then goHome = true; resume = true end
  38.         if (radius == nil) then local n = tonumber(a); if (n ~= nil) then radius = n end end
  39.     end
  40.     return mode, radius, resume, goHome
  41. end
  42.  
  43. local args = { ... }
  44. local mode, radius, resume, goHome = parseArgs(args)
  45.  
  46. local STATE_FILE = DEFAULTS.stateFile
  47. local HOME_DIR = DEFAULTS.homeDir
  48. local START_ESCAPE_STEPS = DEFAULTS.startEscapeSteps
  49.  
  50. local SAVE_STEP_INTERVAL = DEFAULTS.save.stepInterval
  51. local STATUS_INTERVAL = DEFAULTS.save.statusInterval
  52. local SUCK_CYCLES = DEFAULTS.save.suckCycles
  53. local SUCK_SLEEP = DEFAULTS.save.suckSleep
  54. local MOVE_SLEEP = DEFAULTS.save.moveSleep
  55.  
  56. local STAIR_ENABLE = (mode == "stairs" or mode == "both")
  57. local STAIR_STEPS = DEFAULTS.stairs.steps
  58. local STAIR_WIDTH = DEFAULTS.stairs.width
  59. local STAIR_HEADROOM = DEFAULTS.stairs.headroom
  60. local STAIR_TORCHES = DEFAULTS.stairs.torches
  61. local TORCH_SLOT_PREF = DEFAULTS.stairs.torchSlot
  62. local STAIR_TORCH_INTERVAL = DEFAULTS.stairs.torchInterval
  63. local STAIR_TORCH_RESCAN_INTERVAL = DEFAULTS.stairs.torchRescanInterval
  64.  
  65. local FLUID_BLOCK_SLOT = DEFAULTS.fluids.blockSlot
  66.  
  67. local MINE_ENABLE = (mode == "mine" or mode == "both")
  68. local MINE_ROOM_LEN = DEFAULTS.mine.roomLen
  69. local MINE_ROOM_ROWS = DEFAULTS.mine.roomRows
  70. local MINE_LAYERS = DEFAULTS.mine.layers
  71. local MINE_LAYER_DROP = DEFAULTS.mine.layerDrop
  72. local MINE_TORCH_INTERVAL = DEFAULTS.mine.torchInterval
  73. local MINE_TORCH_RESCAN_INTERVAL = DEFAULTS.mine.torchRescanInterval
  74.  
  75. local USE_SCANNER_IF_AVAILABLE = DEFAULTS.scanner.enable
  76. local SCAN_RADIUS_DEFAULT = DEFAULTS.scanner.radiusDefault
  77. local STATUS_PROTOCOL = DEFAULTS.status.protocol
  78. local FUEL_RETURN_RESERVE = DEFAULTS.fuel.returnReserve
  79. local FUEL_REFUEL_MIN = DEFAULTS.fuel.refuelMin
  80.  
  81. radius = tonumber(radius or SCAN_RADIUS_DEFAULT) or SCAN_RADIUS_DEFAULT
  82. if (radius < 1) then radius = 1 end
  83. if (radius > 32) then radius = 32 end
  84.  
  85. local P = scanPeripherals({ modem = true, scanner = USE_SCANNER_IF_AVAILABLE })
  86. local MODEM_SIDE = P.modemName
  87. local scanner, scannerSide, scannerType = P.scanner, P.scannerName, P.scannerType
  88.  
  89. local TORCH_NAME_LIST = { "minecraft:wall_torch", "minecraft:torch" }
  90. local function isTorchName(n)
  91.     if (not n) then return false end
  92.     for i = 1, #TORCH_NAME_LIST do if (TORCH_NAME_LIST[i] == n) then return true end end
  93.     return false
  94. end
  95.  
  96. local ALLOWED_PREFIX = { "xycraft_world:", "supplementaries:" }
  97. local function startsWith(s, p) return s and p and string.sub(s, 1, #p) == p end
  98.  
  99. local ALLOWED_EXACT = (function()
  100.     local t = {}
  101.     local function add(list) for i = 1, #list do t[list[i]] = true end end
  102.     add({
  103.         "minecraft:cobblestone",
  104.         "minecraft:dirt",
  105.         "minecraft:grass_block",
  106.         "minecraft:stone",
  107.         "minecraft:deepslate",
  108.         "minecraft:granite",
  109.         "minecraft:diorite",
  110.         "minecraft:andesite",
  111.         "minecraft:tuff",
  112.         "minecraft:gravel",
  113.         "minecraft:calcite",
  114.         "minecraft:smooth_basalt",
  115.         "minecraft:dripstone_block",
  116.         "minecraft:moss_block",
  117.         "minecraft:clay",
  118.         "minecraft:pointed_dripstone",
  119.         "minecraft:wall_torch",
  120.         "supplementaries:barnacles",
  121.         "xycraft_world:kiwi"
  122.     })
  123.     return t
  124. end)()
  125.  
  126. local home = { x = 0, y = 0, z = 0 }
  127. local staging = { x = 0, y = 0, z = 0 }
  128. local pos = { x = 0, y = 0, z = 0 }
  129. local dir = HOME_DIR
  130. local stepCount = 0
  131. local path = {}
  132.  
  133. local modemReady = false
  134. local lastStatusClock = 0
  135. local dirtySteps = 0
  136.  
  137. local torchSlot = 0
  138. local lastTorchScanStep = 0
  139.  
  140. local function rednetInit()
  141.     if (modemReady) then return true end
  142.     if (not MODEM_SIDE) then return false end
  143.     if (peripheral.getType(MODEM_SIDE) ~= "modem") then return false end
  144.     if (not rednet.isOpen(MODEM_SIDE)) then rednet.open(MODEM_SIDE) end
  145.     modemReady = true
  146.     return true
  147. end
  148.  
  149. local function invUsed()
  150.     local c = 0
  151.     for i = 1, 16 do if (turtle.getItemCount(i) > 0) then c = c + 1 end end
  152.     return c
  153. end
  154.  
  155. local function statusSend(task, force)
  156.     if (not rednetInit()) then return end
  157.     local now = os.clock()
  158.     if (not force and STATUS_INTERVAL > 0 and (now - lastStatusClock) < STATUS_INTERVAL) then return end
  159.     lastStatusClock = now
  160.     rednet.broadcast({ task = task, pos = { x = pos.x, y = pos.y, z = pos.z }, fuel = turtle.getFuelLevel(), inv = invUsed() }, STATUS_PROTOCOL)
  161. end
  162.  
  163. local function saveState(force)
  164.     if (not force) then
  165.         if (SAVE_STEP_INTERVAL <= 0) then return end
  166.         if (dirtySteps < SAVE_STEP_INTERVAL) then return end
  167.     end
  168.     local h = fs.open(STATE_FILE, "w")
  169.     h.write(textutils.serialize({
  170.         home = home, staging = staging, pos = pos, dir = dir, stepCount = stepCount, path = path,
  171.         torchSlot = torchSlot, lastTorchScanStep = lastTorchScanStep
  172.     }))
  173.     h.close()
  174.     dirtySteps = 0
  175. end
  176.  
  177. local function loadState()
  178.     if (not fs.exists(STATE_FILE)) then return false end
  179.     local h = fs.open(STATE_FILE, "r")
  180.     local s = textutils.unserialize(h.readAll())
  181.     h.close()
  182.     if (s and s.home and s.staging and s.pos and s.dir ~= nil and type(s.path) == "table") then
  183.         home = s.home; staging = s.staging; pos = s.pos; dir = s.dir; path = s.path
  184.         stepCount = tonumber(s.stepCount or 0) or 0
  185.         torchSlot = tonumber(s.torchSlot or 0) or 0
  186.         lastTorchScanStep = tonumber(s.lastTorchScanStep or 0) or 0
  187.         dirtySteps = 0
  188.         return true
  189.     end
  190.     return false
  191. end
  192.  
  193. local function resetNavToStart()
  194.     home = { x = 0, y = 0, z = 0 }
  195.     staging = { x = 0, y = 0, z = 0 }
  196.     pos = { x = 0, y = 0, z = 0 }
  197.     dir = HOME_DIR
  198.     stepCount = 0
  199.     path = {}
  200.     torchSlot = 0
  201.     lastTorchScanStep = 0
  202.     dirtySteps = 0
  203.     saveState(true)
  204. end
  205.  
  206. local function turnLeft(rec)
  207.     turtle.turnLeft()
  208.     dir = (dir + 3) % 4
  209.     if (rec) then path[#path + 1] = "L" end
  210.     dirtySteps = dirtySteps + 1; saveState(false)
  211. end
  212.  
  213. local function turnRight(rec)
  214.     turtle.turnRight()
  215.     dir = (dir + 1) % 4
  216.     if (rec) then path[#path + 1] = "R" end
  217.     dirtySteps = dirtySteps + 1; saveState(false)
  218. end
  219.  
  220. local function face(d)
  221.     local diff = (d - dir) % 4
  222.     if (diff == 0) then return end
  223.     if (diff == 1) then turnRight(false); return end
  224.     if (diff == 2) then turnRight(false); turnRight(false); return end
  225.     turnLeft(false)
  226. end
  227.  
  228. local function updateForward()
  229.     if (dir == 0) then pos.z = pos.z - 1 elseif (dir == 1) then pos.x = pos.x + 1 elseif (dir == 2) then pos.z = pos.z + 1 else pos.x = pos.x - 1 end
  230. end
  231.  
  232. local function updateBack()
  233.     if (dir == 0) then pos.z = pos.z + 1 elseif (dir == 1) then pos.x = pos.x - 1 elseif (dir == 2) then pos.z = pos.z - 1 else pos.x = pos.x + 1 end
  234. end
  235.  
  236. local function isOreName(name) return name and string.find(name, "ore", 1, true) ~= nil end
  237. local function isFluidName(name) if (not name) then return false end return (string.find(name, "water", 1, true) ~= nil) or (string.find(name, "lava", 1, true) ~= nil) end
  238. local function allowedBlockName(name)
  239.     if (not name) then return false end
  240.     if (isOreName(name)) then return true end
  241.     if (ALLOWED_EXACT[name] == true) then return true end
  242.     for i = 1, #ALLOWED_PREFIX do if (startsWith(name, ALLOWED_PREFIX[i])) then return true end end
  243.     return false
  244. end
  245. local function atHome() return (pos.x == home.x and pos.y == home.y and pos.z == home.z) end
  246. local function protectedZone() return atHome() end
  247.  
  248. local function suckAllAround()
  249.     for _ = 1, SUCK_CYCLES do turtle.suck(); turtle.suckUp(); turtle.suckDown(); if (SUCK_SLEEP > 0) then sleep(SUCK_SLEEP) end end
  250. end
  251.  
  252. local function tryFillFluid(where)
  253.     if (FLUID_BLOCK_SLOT < 1 or FLUID_BLOCK_SLOT > 16) then return false end
  254.     if (turtle.getItemCount(FLUID_BLOCK_SLOT) <= 0) then return false end
  255.     local sel = turtle.getSelectedSlot()
  256.     turtle.select(FLUID_BLOCK_SLOT)
  257.     local ok = false
  258.     if (where == "front") then ok = turtle.place() elseif (where == "down") then ok = turtle.placeDown() elseif (where == "up") then ok = turtle.placeUp() end
  259.     turtle.select(sel)
  260.     return ok
  261. end
  262.  
  263. local function tryChestSuck(count)
  264.     local startDir = dir
  265.     for _ = 1, 4 do if (turtle.suck(count)) then return true end; turnRight(false) end
  266.     while (dir ~= startDir) do turnRight(false) end
  267.     return false
  268. end
  269.  
  270. local function tryChestDrop()
  271.     local startDir = dir
  272.     for _ = 1, 4 do if (turtle.drop()) then return true end; turnRight(false) end
  273.     while (dir ~= startDir) do turnRight(false) end
  274.     return false
  275. end
  276.  
  277. local function estimateHomeDistance()
  278.     local dx = math.abs(pos.x - home.x)
  279.     local dy = math.abs(pos.y - home.y)
  280.     local dz = math.abs(pos.z - home.z)
  281.     return dx + dy + dz
  282. end
  283.  
  284. local function fuelOkForReturn()
  285.     local f = turtle.getFuelLevel()
  286.     if (f == "unlimited") then return true end
  287.     return (f >= (estimateHomeDistance() + FUEL_RETURN_RESERVE))
  288. end
  289.  
  290. local function refuelFromInventory(min)
  291.     local lvl = turtle.getFuelLevel()
  292.     if (lvl == "unlimited" or lvl >= min) then return true end
  293.     for slot = 1, 16 do
  294.         lvl = turtle.getFuelLevel()
  295.         if (lvl == "unlimited" or lvl >= min) then turtle.select(1); return true end
  296.         if (turtle.getItemCount(slot) > 0) then turtle.select(slot); if (turtle.refuel(0)) then turtle.refuel() end end
  297.     end
  298.     turtle.select(1)
  299.     lvl = turtle.getFuelLevel()
  300.     return (lvl == "unlimited" or lvl >= min)
  301. end
  302.  
  303. local function abortHome(msg)
  304.     print(msg)
  305.     returnHomeFast(msg)
  306.     saveState(true)
  307.     error(msg)
  308. end
  309.  
  310. local function moveStep(kind, miningAllowed, record)
  311.     if (kind == "F") then
  312.         while (not turtle.forward()) do
  313.             if (not miningAllowed or protectedZone()) then abortHome("Blocked at protected zone (won't dig).") end
  314.             local ok, data = turtle.inspect()
  315.             local n = (data and data.name) or "unknown"
  316.             if (ok and data and data.name and isFluidName(data.name)) then
  317.                 if (FLUID_BLOCK_SLOT ~= 0 and tryFillFluid("front")) then else abortHome("Front blocked by fluid: " .. n) end
  318.             elseif (ok and data and data.name and allowedBlockName(data.name)) then
  319.                 turtle.dig(); suckAllAround()
  320.             else
  321.                 abortHome("Blocked by non-allowed block in front: " .. n)
  322.             end
  323.             if (MOVE_SLEEP > 0) then sleep(MOVE_SLEEP) end
  324.         end
  325.         updateForward(); stepCount = stepCount + 1
  326.         if (record) then path[#path + 1] = "F" end
  327.         dirtySteps = dirtySteps + 1; saveState(false)
  328.         return true
  329.     end
  330.     if (kind == "D") then
  331.         while (not turtle.down()) do
  332.             if (not miningAllowed or protectedZone()) then abortHome("Blocked at protected zone (won't dig).") end
  333.             local ok, data = turtle.inspectDown()
  334.             local n = (data and data.name) or "unknown"
  335.             if (ok and data and data.name and isFluidName(data.name)) then
  336.                 if (FLUID_BLOCK_SLOT ~= 0 and tryFillFluid("down")) then else abortHome("Down blocked by fluid: " .. n) end
  337.             elseif (ok and data and data.name and allowedBlockName(data.name)) then
  338.                 turtle.digDown(); suckAllAround()
  339.             else
  340.                 abortHome("Blocked by non-allowed block below: " .. n)
  341.             end
  342.             if (MOVE_SLEEP > 0) then sleep(MOVE_SLEEP) end
  343.         end
  344.         pos.y = pos.y - 1; stepCount = stepCount + 1
  345.         if (record) then path[#path + 1] = "D" end
  346.         dirtySteps = dirtySteps + 1; saveState(false)
  347.         return true
  348.     end
  349.     if (kind == "U") then
  350.         while (not turtle.up()) do
  351.             if (protectedZone()) then abortHome("Blocked at protected zone (won't dig).") end
  352.             local ok, data = turtle.inspectUp()
  353.             local n = (data and data.name) or "unknown"
  354.             if (ok and data and data.name and isFluidName(data.name)) then
  355.                 if (FLUID_BLOCK_SLOT ~= 0 and tryFillFluid("up")) then else abortHome("Up blocked by fluid: " .. n) end
  356.             elseif (ok and data and data.name and allowedBlockName(data.name)) then
  357.                 turtle.digUp(); suckAllAround()
  358.             else
  359.                 abortHome("Up blocked by non-allowed block: " .. n)
  360.             end
  361.             if (MOVE_SLEEP > 0) then sleep(MOVE_SLEEP) end
  362.         end
  363.         pos.y = pos.y + 1
  364.         dirtySteps = dirtySteps + 1; saveState(false)
  365.         return true
  366.     end
  367.     if (kind == "B") then
  368.         while (not turtle.back()) do if (MOVE_SLEEP > 0) then sleep(MOVE_SLEEP) end end
  369.         updateBack()
  370.         dirtySteps = dirtySteps + 1; saveState(false)
  371.         return true
  372.     end
  373.     return false
  374. end
  375.  
  376. local function oppositeStep(s)
  377.     if (s == "F") then return "B" end
  378.     if (s == "B") then return "F" end
  379.     if (s == "U") then return "D" end
  380.     if (s == "D") then return "U" end
  381.     if (s == "L") then return "R" end
  382.     if (s == "R") then return "L" end
  383.     return nil
  384. end
  385.  
  386. local function tryMoveNoDig(kind)
  387.     if (kind == "F") then if (turtle.forward()) then updateForward(); return true end; local ok, data = turtle.inspect(); error("Home path blocked in front: " .. (((data and data.name) or "unknown"))) end
  388.     if (kind == "B") then if (turtle.back()) then updateBack(); return true end; turnRight(false); turnRight(false); local ok, data = turtle.inspect(); turnRight(false); turnRight(false); error("Home path blocked behind: " .. (((data and data.name) or "unknown"))) end
  389.     if (kind == "U") then if (turtle.up()) then pos.y = pos.y + 1; return true end; local ok, data = turtle.inspectUp(); error("Home path blocked above: " .. (((data and data.name) or "unknown"))) end
  390.     if (kind == "D") then if (turtle.down()) then pos.y = pos.y - 1; return true end; local ok, data = turtle.inspectDown(); error("Home path blocked below: " .. (((data and data.name) or "unknown"))) end
  391.     if (kind == "L") then turnLeft(false); return true end
  392.     if (kind == "R") then turnRight(false); return true end
  393.     return false
  394. end
  395.  
  396. local function returnViaPathNoDig(reason)
  397.     statusSend("return-path: " .. tostring(reason or ""), true)
  398.     if (type(path) ~= "table" or #path == 0) then error("No recorded path. Refusing to dig home.") end
  399.     for i = #path, 1, -1 do
  400.         local opp = oppositeStep(path[i])
  401.         if (opp) then tryMoveNoDig(opp) end
  402.         if ((i % 16) == 0) then saveState(false) end
  403.     end
  404.     path = {}
  405.     dir = HOME_DIR
  406.     saveState(true)
  407. end
  408.  
  409. function returnHomeFast(reason)
  410.     statusSend("return-fast: " .. tostring(reason or ""), true)
  411.     while (pos.x ~= home.x) do ensureFuel(); face((pos.x < home.x) and 1 or 3); moveStep("F", true, false) end
  412.     while (pos.z ~= home.z) do ensureFuel(); face((pos.z < home.z) and 2 or 0); moveStep("F", true, false) end
  413.     while (pos.y ~= home.y) do ensureFuel(); moveStep((pos.y < home.y) and "U" or "D", true, false) end
  414.     path = {}
  415.     dir = HOME_DIR
  416.     dirtySteps = dirtySteps + 1
  417.     saveState(true)
  418. end
  419.  
  420. local function refuelFromChest(min)
  421.     local lvl = turtle.getFuelLevel()
  422.     if (lvl == "unlimited" or lvl >= min) then return end
  423.     if (not atHome()) then returnHomeFast("refuel") end
  424.     turtle.select(16)
  425.     for _ = 1, 64 do
  426.         lvl = turtle.getFuelLevel()
  427.         if (lvl == "unlimited" or lvl >= min) then break end
  428.         if (turtle.getItemCount(16) == 0) then if (not tryChestSuck(64)) then abortHome("No accessible chest adjacent at home (fuel).") end end
  429.         if (turtle.getItemCount(16) > 0 and turtle.refuel(0)) then turtle.refuel() else if (not refuelFromInventory(min)) then break end end
  430.         if (MOVE_SLEEP > 0) then sleep(MOVE_SLEEP) end
  431.     end
  432.     turtle.select(1)
  433.     lvl = turtle.getFuelLevel()
  434.     if (lvl ~= "unlimited" and lvl < min) then abortHome("Not enough fuel. Put coal/charcoal into adjacent chest at home.") end
  435. end
  436.  
  437. function ensureFuel()
  438.     local f = turtle.getFuelLevel()
  439.     if (f == "unlimited") then return end
  440.     if (fuelOkForReturn()) then return end
  441.     statusSend("fuel low -> return", true)
  442.     returnHomeFast("fuel")
  443.     refuelFromChest(FUEL_REFUEL_MIN)
  444.     statusSend("resume", true)
  445. end
  446.  
  447. local function scanTorchSlot()
  448.     if (TORCH_SLOT_PREF >= 1 and TORCH_SLOT_PREF <= 16 and turtle.getItemCount(TORCH_SLOT_PREF) > 0) then
  449.         local d = turtle.getItemDetail(TORCH_SLOT_PREF)
  450.         if (d and isTorchName(d.name)) then torchSlot = TORCH_SLOT_PREF; return torchSlot end
  451.     end
  452.     for i = 1, 16 do
  453.         if (turtle.getItemCount(i) > 0) then
  454.             local d = turtle.getItemDetail(i)
  455.             if (d and isTorchName(d.name)) then torchSlot = i; return torchSlot end
  456.         end
  457.     end
  458.     torchSlot = 0
  459.     return 0
  460. end
  461.  
  462. local function ensureTorchSlot(rescanEvery)
  463.     if (torchSlot >= 1 and torchSlot <= 16 and turtle.getItemCount(torchSlot) > 0) then
  464.         local d = turtle.getItemDetail(torchSlot)
  465.         if (d and isTorchName(d.name)) then return end
  466.     end
  467.     if (rescanEvery and rescanEvery > 0 and (stepCount - lastTorchScanStep) < rescanEvery) then return end
  468.     lastTorchScanStep = stepCount
  469.     scanTorchSlot()
  470.     dirtySteps = dirtySteps + 1; saveState(false)
  471. end
  472.  
  473. local function placeTorchBackWall()
  474.     if (torchSlot < 1 or torchSlot > 16 or turtle.getItemCount(torchSlot) <= 0) then return false end
  475.     local sel = turtle.getSelectedSlot()
  476.     turtle.select(torchSlot)
  477.     turnRight(false); turnRight(false)
  478.     local ok = false
  479.     if (turtle.detect()) then ok = turtle.place() end
  480.     turnRight(false); turnRight(false)
  481.     turtle.select(sel)
  482.     return ok
  483. end
  484.  
  485. local function stairsDigOrAbort(label, inspectFn, digFn, fluidWhere, allowFluids)
  486.     local ok, data = inspectFn()
  487.     if (not ok) then return true end
  488.     local n = (data and data.name) or "unknown"
  489.     if (isTorchName(n)) then return true end
  490.     if (isFluidName(n)) then
  491.         if (FLUID_BLOCK_SLOT ~= 0 and tryFillFluid(fluidWhere)) then return true end
  492.         if (allowFluids) then return true end
  493.         abortHome("Stairs blocked by fluid (" .. label .. "): " .. n)
  494.         return false
  495.     end
  496.     if (allowedBlockName(n)) then digFn(); suckAllAround(); return true end
  497.     abortHome("Stairs blocked by non-allowed block (" .. label .. "): " .. n)
  498.     return false
  499. end
  500.  
  501. local function clearCeilHere(allowFluids)
  502.     if (STAIR_HEADROOM <= 1) then return end
  503.     stairsDigOrAbort("up", turtle.inspectUp, turtle.digUp, "up", allowFluids)
  504.     if (STAIR_HEADROOM <= 2) then return end
  505.     for _ = 2, STAIR_HEADROOM - 1 do moveStep("U", true, false); stairsDigOrAbort("up", turtle.inspectUp, turtle.digUp, "up", allowFluids) end
  506.     for _ = 1, STAIR_HEADROOM - 2 do moveStep("D", true, false) end
  507. end
  508.  
  509. local function shiftToLeft()
  510.     turnLeft(false); moveStep("F", true, false); turnRight(false)
  511. end
  512.  
  513. local function shiftToRight()
  514.     turnRight(false); moveStep("F", true, false); turnLeft(false)
  515. end
  516.  
  517. local function shiftBackFromLeft()
  518.     turnLeft(false); moveStep("B", true, false); turnRight(false)
  519. end
  520.  
  521. local function shiftBackFromRight()
  522.     turnRight(false); moveStep("B", true, false); turnLeft(false)
  523. end
  524.  
  525. local function sweep3Wide(fn)
  526.     fn(0)
  527.     if (STAIR_WIDTH < 3) then return end
  528.     shiftToLeft(); fn(-1); shiftBackFromLeft()
  529.     shiftToRight(); fn(1); shiftBackFromRight()
  530. end
  531.  
  532. local function clearFrontHere(allowFluids)
  533.     stairsDigOrAbort("front", turtle.inspect, turtle.dig, "front", allowFluids)
  534. end
  535.  
  536. local function digDownHere(allowFluids)
  537.     stairsDigOrAbort("down", turtle.inspectDown, turtle.digDown, "down", allowFluids)
  538. end
  539.  
  540. local function safeForward(miningAllowed, record)
  541.     ensureFuel()
  542.     moveStep("F", miningAllowed, record)
  543. end
  544.  
  545. local function safeDown(miningAllowed, record)
  546.     ensureFuel()
  547.     moveStep("D", miningAllowed, record)
  548. end
  549.  
  550. local function tryPlaceTorchStairs()
  551.     if (not STAIR_TORCHES or STAIR_TORCH_INTERVAL <= 0) then return end
  552.     if ((stepCount % STAIR_TORCH_INTERVAL) ~= 0) then return end
  553.     ensureTorchSlot(STAIR_TORCH_RESCAN_INTERVAL)
  554.     if (torchSlot <= 0) then return end
  555.     placeTorchBackWall()
  556. end
  557.  
  558. local function digStairsDown(steps)
  559.     for i = 1, steps do
  560.         statusSend("stairs " .. i .. "/" .. steps, false)
  561.  
  562.         sweep3Wide(function() clearFrontHere(true); clearCeilHere(true) end)
  563.         safeForward(true, true)
  564.  
  565.         sweep3Wide(function() clearCeilHere(true) end)
  566.         sweep3Wide(function() digDownHere(true) end)
  567.         safeDown(true, true)
  568.  
  569.         sweep3Wide(function() clearCeilHere(true) end)
  570.         tryPlaceTorchStairs()
  571.     end
  572. end
  573.  
  574. local function scanForOres()
  575.     if (not scanner) then return {} end
  576.     local ok, res = pcall(function() return scanner.scan(radius) end)
  577.     if (not ok or type(res) ~= "table") then return {} end
  578.     local ores = {}
  579.     for i = 1, #res do local b = res[i]; if (b and b.name and string.find(b.name, "ore", 1, true) ~= nil) then ores[#ores + 1] = b end end
  580.     return ores
  581. end
  582.  
  583. local function mineRowForward(len)
  584.     for _ = 1, len do
  585.         local ok, data = turtle.inspect()
  586.         if (ok and data and data.name and isFluidName(data.name)) then
  587.             local n = data.name
  588.             if (FLUID_BLOCK_SLOT ~= 0 and tryFillFluid("front")) then else abortHome("Front blocked by fluid: " .. n) end
  589.         elseif (ok and data and data.name and allowedBlockName(data.name)) then
  590.             turtle.dig(); suckAllAround()
  591.         elseif (ok and data and data.name) then
  592.             abortHome("Blocked by non-allowed block in front: " .. data.name)
  593.         end
  594.         safeForward(true, true)
  595.     end
  596.     return true
  597. end
  598.  
  599. local function mineRoomLayer()
  600.     local rowLen = math.max(8, MINE_ROOM_LEN)
  601.     for r = 1, MINE_ROOM_ROWS do
  602.         statusSend("mine row " .. r .. "/" .. MINE_ROOM_ROWS, false)
  603.         local _ = scanForOres()
  604.         if (not mineRowForward(rowLen)) then return false end
  605.         if (r < MINE_ROOM_ROWS) then
  606.             if ((r % 2) == 1) then turnRight(true); safeForward(true, true); turnRight(true) else turnLeft(true); safeForward(true, true); turnLeft(true) end
  607.         end
  608.     end
  609.     return true
  610. end
  611.  
  612. local function mineLayers(layers)
  613.     for l = 1, layers do
  614.         statusSend("mine layer " .. l .. "/" .. layers, false)
  615.         if (not mineRoomLayer()) then return false end
  616.         if (l < layers) then for _ = 1, MINE_LAYER_DROP do safeDown(true, true) end end
  617.     end
  618.     return true
  619. end
  620.  
  621. local function dropAllToChestAtHome()
  622.     if (pos.x ~= home.x or pos.y ~= home.y or pos.z ~= home.z) then returnHomeFast("dump") end
  623.     for slot = 1, 15 do
  624.         turtle.select(slot)
  625.         if (turtle.getItemCount(slot) > 0) then
  626.             local startDir = dir
  627.             local dropped = false
  628.             for _ = 1, 4 do if (turtle.drop()) then dropped = true; break end; turnRight(false) end
  629.             while (dir ~= startDir) do turnRight(false) end
  630.             if (not dropped) then abortHome("No accessible chest adjacent at home (dump).") end
  631.         end
  632.     end
  633.     turtle.select(1)
  634. end
  635.  
  636. local function escapeFromBaseForwardNoDig()
  637.     if (pos.x ~= home.x or pos.y ~= home.y or pos.z ~= home.z) then return end
  638.     statusSend("escape", true)
  639.     turnRight(true); turnRight(true)
  640.     for i = 1, START_ESCAPE_STEPS do
  641.         while (not turtle.forward()) do abortHome("Escape blocked (won't dig at home).") end
  642.         updateForward()
  643.         path[#path + 1] = "F"
  644.         stepCount = stepCount + 1
  645.         dirtySteps = dirtySteps + 1
  646.         if (i == 1) then staging = { x = pos.x, y = pos.y, z = pos.z } end
  647.         saveState(false)
  648.     end
  649.     saveState(true)
  650. end
  651.  
  652. if (resume) then if (not loadState()) then resetNavToStart() end else resetNavToStart() end
  653.  
  654. print("Mode:", mode)
  655. print("Radius:", radius)
  656. if (scanner) then print("Scanner:", scannerSide, scannerType) else print("Scanner: none (fallback mining only)") end
  657. if (MODEM_SIDE) then print("Modem:", MODEM_SIDE) else print("Modem: none") end
  658.  
  659. statusSend("starting", true)
  660. refuelFromChest(FUEL_REFUEL_MIN)
  661. ensureTorchSlot(0)
  662.  
  663. if (goHome) then
  664.     if (not fs.exists(STATE_FILE) or not loadState()) then print("No state found. Can't go home safely."); return end
  665.     if (type(path) == "table" and #path > 0) then returnViaPathNoDig("manual-home") else print("No recorded path. Refusing to dig home."); return end
  666.     statusSend("idle", true)
  667.     print("Done.")
  668.     return
  669. end
  670.  
  671. if (pos.x == home.x and pos.y == home.y and pos.z == home.z) then escapeFromBaseForwardNoDig() end
  672. if (STAIR_ENABLE and STAIR_STEPS > 0) then digStairsDown(STAIR_STEPS) end
  673. if (MINE_ENABLE) then if (mode == "both") then mineRoomLayer() else mineLayers(MINE_LAYERS) end end
  674.  
  675. statusSend("return", true)
  676. returnHomeFast("finished")
  677. dropAllToChestAtHome()
  678. statusSend("idle", true)
  679. print("Done.")
  680.  
Advertisement
Add Comment
Please, Sign In to add comment