local M = {} local logger = require("libs.logger") -- Optional external status setter (set via M.setStatusCallback) local statusCallback = nil function M.setStatusCallback(fn) statusCallback = fn end -- Position tracking state for manual mode local manualPos = { x = 0, y = 0, z = 0 } local directions = { "north", "east", "south", "west" } local facing = 1 -- GPS attempt local function tryGPS() local x, y, z = gps.locate(2) -- 2 second timeout if x and y and z then return { x = math.floor(x), y = math.floor(y), z = math.floor(z) } end return nil end -- Manual position update helpers local function updatePositionOnMove(direction) if direction == "forward" then if directions[facing] == "north" then manualPos.z = manualPos.z - 1 elseif directions[facing] == "south" then manualPos.z = manualPos.z + 1 elseif directions[facing] == "east" then manualPos.x = manualPos.x + 1 elseif directions[facing] == "west" then manualPos.x = manualPos.x - 1 end elseif direction == "back" then if directions[facing] == "north" then manualPos.z = manualPos.z + 1 elseif directions[facing] == "south" then manualPos.z = manualPos.z - 1 elseif directions[facing] == "east" then manualPos.x = manualPos.x - 1 elseif directions[facing] == "west" then manualPos.x = manualPos.x + 1 end elseif direction == "up" then manualPos.y = manualPos.y + 1 elseif direction == "down" then manualPos.y = manualPos.y - 1 end end local function updateFacingLeft() facing = facing - 1 if facing < 1 then facing = 4 end end local function updateFacingRight() facing = facing + 1 if facing > 4 then facing = 1 end end -- Public function to get current position (GPS or manual) function M.getPosition() local gpsPos = tryGPS() if gpsPos then return gpsPos.x, gpsPos.y, gpsPos.z, true else return manualPos.x, manualPos.y, manualPos.z, false end end -- Minimum fuel required to proceed with one movement local MIN_FUEL = 1 -- Attempt to refuel from inventory local function refuelFromInventory(minRequired) minRequired = minRequired or MIN_FUEL local gained = 0 for slot = 1, 16 do turtle.select(slot) if turtle.getItemCount() > 0 and turtle.refuel(0) then local before = turtle.getFuelLevel() if turtle.refuel(1) then local after = turtle.getFuelLevel() local added = after - before gained = gained + added logger.log(string.format("Refueled +%d from slot %d", added, slot)) if after >= minRequired then break end else logger.log(string.format("Refuel failed from slot %d", slot)) end end end return gained end -- Ensure fuel before doing anything local function refuelIfNeeded() local fuel = turtle.getFuelLevel() if fuel == "unlimited" then return true end if fuel < MIN_FUEL then logger.log("Low fuel, attempting to refuel...") local added = refuelFromInventory(MIN_FUEL) fuel = turtle.getFuelLevel() end while fuel < MIN_FUEL do if statusCallback then statusCallback("out_of_fuel") end local x, y, z, gpsActive = M.getPosition() local posText = gpsActive and string.format("GPS (%d,%d,%d)", x, y, z) or string.format("Manual (%d,%d,%d)", x, y, z) logger.log("No fuel. Waiting for fuel... Location: " .. posText) sleep(5) local added = refuelFromInventory(MIN_FUEL) fuel = turtle.getFuelLevel() end return true end -- Retry a movement or action with fuel awareness local function try(actionFn, times, name) times = times or 5 for i = 1, times do if not refuelIfNeeded() then sleep(2) elseif actionFn() then if name then logger.log("Action succeeded: " .. name) end return true end if name then logger.log("Retrying: " .. name) end sleep(0.5) end if name then logger.log("Gave up after retries: " .. name) end return false end -- Movement wrappers that update manual position and use try() function M.forward() local function action() return turtle.forward() end local result = try(action, 5, "forward") if result then updatePositionOnMove("forward") end return result end function M.back() local function action() return turtle.back() end local result = try(action, 5, "back") if result then updatePositionOnMove("back") end return result end function M.up() local function action() return turtle.up() end local result = try(action, 5, "up") if result then updatePositionOnMove("up") end return result end function M.down() local function action() return turtle.down() end local result = try(action, 5, "down") if result then updatePositionOnMove("down") end return result end -- Digging functions (not wrapped in try, but you can add if you want) function M.digForward() while turtle.detect() do turtle.dig() sleep(0.3) logger.log("Dug block in front") end end function M.digUp() while turtle.detectUp() do turtle.digUp() sleep(0.3) logger.log("Dug block above") end end function M.digDown() while turtle.detectDown() do turtle.digDown() sleep(0.3) logger.log("Dug block below") end end -- Move forward clearing blocks as needed function M.moveUntilClearForward() logger.log("Attempting to move forward (auto-clear)") while not M.forward() do if turtle.detect() then M.digForward() else sleep(0.5) end end logger.log("Moved forward") end -- Inspection function M.inspectForward() local success, data = turtle.inspect() if success then return data else return nil end end function M.inspectUp() local success, data = turtle.inspectUp() if success then return data else return nil end end function M.inspectDown() local success, data = turtle.inspectDown() if success then return data else return nil end end -- Turning functions that update facing function M.turnLeft() turtle.turnLeft() updateFacingLeft() logger.log("Turned left") end function M.turnRight() turtle.turnRight() updateFacingRight() logger.log("Turned right") end function M.turnAround() turtle.turnLeft() turtle.turnLeft() updateFacingLeft() updateFacingLeft() logger.log("Turned around") end return M