1017 lines
30 KiB
Lua
1017 lines
30 KiB
Lua
-- Advanced Autonomous Mining Turtle (FIXED v3)
|
|
-- Features: GPS tracking, auto-mining, fuel management, smart navigation
|
|
|
|
local CHANNEL_RECEIVE = 100
|
|
local CHANNEL_SEND = 101
|
|
local STATUS_CHANNEL = 102
|
|
|
|
-- State management
|
|
local state = {
|
|
mode = "idle", -- idle, manual, exploring, mining, returning
|
|
position = nil,
|
|
homePosition = nil,
|
|
fuel = 0,
|
|
inventory = {},
|
|
target = nil,
|
|
path = {},
|
|
lastStatusUpdate = 0,
|
|
stuckCounter = 0,
|
|
visitedPositions = {}, -- Track where we've been
|
|
lastPosition = nil,
|
|
stuckInHoleCounter = 0
|
|
}
|
|
|
|
-- Configuration
|
|
local config = {
|
|
minFuelToReturn = 500,
|
|
minFuelToExplore = 1000,
|
|
statusUpdateInterval = 5, -- seconds
|
|
maxStuckAttempts = 3,
|
|
maxDistanceFromHome = 200, -- blocks - stay within reasonable range
|
|
pauseIfNoPlayersNearby = false, -- set true if chunk loading is concern
|
|
valuableBlocks = {
|
|
["minecraft:coal_ore"] = 1,
|
|
["minecraft:iron_ore"] = 2,
|
|
["minecraft:gold_ore"] = 3,
|
|
["minecraft:diamond_ore"] = 5,
|
|
["minecraft:emerald_ore"] = 5,
|
|
["minecraft:redstone_ore"] = 2,
|
|
["minecraft:lapis_ore"] = 2,
|
|
["minecraft:deepslate_coal_ore"] = 1,
|
|
["minecraft:deepslate_iron_ore"] = 2,
|
|
["minecraft:deepslate_gold_ore"] = 3,
|
|
["minecraft:deepslate_diamond_ore"] = 5,
|
|
["minecraft:deepslate_emerald_ore"] = 5,
|
|
["minecraft:deepslate_redstone_ore"] = 2,
|
|
["minecraft:deepslate_lapis_ore"] = 2,
|
|
}
|
|
}
|
|
|
|
-- Check for modem
|
|
local modem = peripheral.find("modem")
|
|
if not modem then
|
|
error("No wireless modem found!")
|
|
end
|
|
modem.open(CHANNEL_RECEIVE)
|
|
|
|
print("Advanced Mining Turtle System")
|
|
print("ID: " .. os.getComputerID())
|
|
|
|
-- GPS Functions
|
|
local function updatePosition()
|
|
-- Quick GPS check with short timeout
|
|
local x, y, z = gps.locate(2) -- 2 second timeout
|
|
if x then
|
|
state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)}
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Sync home position with server (via wireless broadcast)
|
|
local function syncHomeWithServer()
|
|
-- Request home position from server via webbridge
|
|
print("Requesting home position from server...")
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "request_home",
|
|
turtleID = os.getComputerID()
|
|
})
|
|
|
|
-- Don't block - just request and continue
|
|
-- The response will be handled in the message processing loop
|
|
return true
|
|
end
|
|
|
|
-- Set home position (server-authoritative via wireless)
|
|
local function setHome()
|
|
print("Setting home position...")
|
|
|
|
-- If we don't have current position, try to get it
|
|
-- But don't block if we already have it
|
|
if not state.position then
|
|
if not updatePosition() then
|
|
print("Failed to get GPS position for home")
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Set locally immediately
|
|
state.homePosition = {
|
|
x = state.position.x,
|
|
y = state.position.y,
|
|
z = state.position.z
|
|
}
|
|
|
|
-- Send to server via wireless (fire and forget)
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "set_home",
|
|
turtleID = os.getComputerID(),
|
|
position = state.homePosition
|
|
})
|
|
|
|
print("✅ Home set at:", textutils.serialize(state.homePosition))
|
|
print(" (Syncing with server in background)")
|
|
return true
|
|
end
|
|
|
|
-- Movement tracking
|
|
local facing = 0 -- 0=North(Z-), 1=East(X+), 2=South(Z+), 3=West(X-)
|
|
|
|
local function updateFacingAfterTurn(right)
|
|
if right then
|
|
facing = (facing + 1) % 4
|
|
else
|
|
facing = (facing - 1) % 4
|
|
end
|
|
end
|
|
|
|
local function smartForward()
|
|
local success = turtle.forward()
|
|
if success and state.position then
|
|
if facing == 0 then state.position.z = state.position.z - 1
|
|
elseif facing == 1 then state.position.x = state.position.x + 1
|
|
elseif facing == 2 then state.position.z = state.position.z + 1
|
|
elseif facing == 3 then state.position.x = state.position.x - 1
|
|
end
|
|
end
|
|
return success
|
|
end
|
|
|
|
local function smartBack()
|
|
local success = turtle.back()
|
|
if success and state.position then
|
|
if facing == 0 then state.position.z = state.position.z + 1
|
|
elseif facing == 1 then state.position.x = state.position.x - 1
|
|
elseif facing == 2 then state.position.z = state.position.z - 1
|
|
elseif facing == 3 then state.position.x = state.position.x + 1
|
|
end
|
|
end
|
|
return success
|
|
end
|
|
|
|
local function smartUp()
|
|
local success = turtle.up()
|
|
if success and state.position then
|
|
state.position.y = state.position.y + 1
|
|
end
|
|
return success
|
|
end
|
|
|
|
local function smartDown()
|
|
local success = turtle.down()
|
|
if success and state.position then
|
|
state.position.y = state.position.y - 1
|
|
end
|
|
return success
|
|
end
|
|
|
|
local function smartTurnRight()
|
|
local success = turtle.turnRight()
|
|
if success then
|
|
updateFacingAfterTurn(true)
|
|
end
|
|
return success
|
|
end
|
|
|
|
local function smartTurnLeft()
|
|
local success = turtle.turnLeft()
|
|
if success then
|
|
updateFacingAfterTurn(false)
|
|
end
|
|
return success
|
|
end
|
|
|
|
-- Fuel management
|
|
local function updateFuel()
|
|
state.fuel = turtle.getFuelLevel()
|
|
return state.fuel
|
|
end
|
|
|
|
local function needsRefuel()
|
|
return state.fuel ~= "unlimited" and state.fuel < config.minFuelToReturn
|
|
end
|
|
|
|
local function tryRefuel()
|
|
for slot = 1, 16 do
|
|
turtle.select(slot)
|
|
if turtle.refuel(0) then -- Check if it's fuel
|
|
local count = turtle.getItemCount()
|
|
if count > 0 then
|
|
turtle.refuel(1)
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Inventory management
|
|
local function updateInventory()
|
|
state.inventory = {}
|
|
for slot = 1, 16 do
|
|
local item = turtle.getItemDetail(slot)
|
|
if item then
|
|
table.insert(state.inventory, {
|
|
slot = slot,
|
|
name = item.name,
|
|
count = item.count
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
local function inventoryFull()
|
|
for slot = 1, 16 do
|
|
if turtle.getItemCount(slot) == 0 then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function isValuableBlock(blockName)
|
|
return config.valuableBlocks[blockName] ~= nil
|
|
end
|
|
|
|
-- Check if block is unbreakable
|
|
local function isUnbreakable(blockName)
|
|
if not blockName then return false end
|
|
return string.find(blockName, "bedrock") or string.find(blockName, "barrier")
|
|
end
|
|
|
|
-- Mining functions
|
|
local function smartDig(direction)
|
|
direction = direction or "forward"
|
|
|
|
local digFunc, inspectFunc
|
|
if direction == "forward" then
|
|
digFunc = turtle.dig
|
|
inspectFunc = turtle.inspect
|
|
elseif direction == "up" then
|
|
digFunc = turtle.digUp
|
|
inspectFunc = turtle.inspectUp
|
|
elseif direction == "down" then
|
|
digFunc = turtle.digDown
|
|
inspectFunc = turtle.inspectDown
|
|
end
|
|
|
|
local hasBlock, data = inspectFunc()
|
|
if hasBlock then
|
|
if isUnbreakable(data.name) then
|
|
return false, false, data.name
|
|
end
|
|
local valuable = isValuableBlock(data.name)
|
|
digFunc()
|
|
return true, valuable, data.name
|
|
end
|
|
return false, false, nil
|
|
end
|
|
|
|
-- Pathfinding (improved with obstacle handling)
|
|
local function getDistance(pos1, pos2)
|
|
return math.abs(pos1.x - pos2.x) + math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z)
|
|
end
|
|
|
|
-- Check if turtle is too far from home
|
|
local function isTooFarFromHome()
|
|
if not state.position or not state.homePosition then
|
|
return false
|
|
end
|
|
local distance = getDistance(state.position, state.homePosition)
|
|
return distance > config.maxDistanceFromHome
|
|
end
|
|
|
|
-- Check if it's safe to continue operations (for chunk loading concerns)
|
|
local function shouldPauseOperations()
|
|
if not config.pauseIfNoPlayersNearby then
|
|
return false -- Feature disabled
|
|
end
|
|
|
|
-- You can add player detection logic here if using commands
|
|
-- For now, just check distance from home as a safety measure
|
|
return isTooFarFromHome()
|
|
end
|
|
|
|
-- Try to dig and move forward
|
|
local function forceForward()
|
|
for i = 1, 5 do
|
|
if smartForward() then
|
|
return true
|
|
end
|
|
smartDig("forward")
|
|
sleep(0.3)
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Try to dig and move up
|
|
local function forceUp()
|
|
for i = 1, 5 do
|
|
if smartUp() then
|
|
return true
|
|
end
|
|
smartDig("up")
|
|
sleep(0.3)
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Try to dig and move down
|
|
local function forceDown()
|
|
for i = 1, 5 do
|
|
if smartDown() then
|
|
return true
|
|
end
|
|
smartDig("down")
|
|
sleep(0.3)
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Advanced navigation with unstuck logic
|
|
local function navigateTowards(target)
|
|
if not state.position or not target then
|
|
return false
|
|
end
|
|
|
|
local dx = target.x - state.position.x
|
|
local dy = target.y - state.position.y
|
|
local dz = target.z - state.position.z
|
|
|
|
local lastPos = {x = state.position.x, y = state.position.y, z = state.position.z}
|
|
|
|
-- Prioritize vertical movement if needed
|
|
if math.abs(dy) > 3 then
|
|
if dy > 0 then
|
|
if forceUp() then
|
|
state.stuckCounter = 0
|
|
return false
|
|
end
|
|
else
|
|
if forceDown() then
|
|
state.stuckCounter = 0
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Horizontal movement
|
|
local targetFacing
|
|
if math.abs(dx) > math.abs(dz) then
|
|
targetFacing = dx > 0 and 1 or 3
|
|
else
|
|
targetFacing = dz > 0 and 2 or 0
|
|
end
|
|
|
|
-- Face the right direction
|
|
while facing ~= targetFacing do
|
|
smartTurnRight()
|
|
end
|
|
|
|
-- Try to move forward
|
|
if forceForward() then
|
|
state.stuckCounter = 0
|
|
else
|
|
-- Stuck! Try alternative routes
|
|
state.stuckCounter = state.stuckCounter + 1
|
|
print("Stuck counter: " .. state.stuckCounter)
|
|
|
|
if state.stuckCounter > config.maxStuckAttempts then
|
|
print("Trying alternative path...")
|
|
state.stuckCounter = 0
|
|
|
|
-- Try going up to go over obstacle
|
|
if forceUp() then
|
|
return false
|
|
end
|
|
|
|
-- Try going around
|
|
smartTurnRight()
|
|
if forceForward() then
|
|
return false
|
|
end
|
|
|
|
smartTurnLeft()
|
|
smartTurnLeft()
|
|
if forceForward() then
|
|
return false
|
|
end
|
|
|
|
-- Last resort: dig down and try lower path
|
|
smartTurnRight()
|
|
forceDown()
|
|
end
|
|
end
|
|
|
|
-- Check if we're close enough
|
|
return getDistance(state.position, target) < 2
|
|
end
|
|
|
|
-- Status broadcasting
|
|
function broadcastStatus()
|
|
updateFuel()
|
|
updateInventory()
|
|
-- Don't update position on every broadcast to avoid GPS delays
|
|
-- Position will be updated by movement functions
|
|
|
|
-- Scan surrounding blocks for map visualization
|
|
local surroundings = {}
|
|
local hasBlock, data
|
|
|
|
-- Check forward
|
|
hasBlock, data = turtle.inspect()
|
|
if hasBlock and data then
|
|
surroundings.forward = {name = data.name, metadata = data.metadata or 0}
|
|
end
|
|
|
|
-- Check up
|
|
hasBlock, data = turtle.inspectUp()
|
|
if hasBlock and data then
|
|
surroundings.up = {name = data.name, metadata = data.metadata or 0}
|
|
end
|
|
|
|
-- Check down
|
|
hasBlock, data = turtle.inspectDown()
|
|
if hasBlock and data then
|
|
surroundings.down = {name = data.name, metadata = data.metadata or 0}
|
|
end
|
|
|
|
modem.transmit(STATUS_CHANNEL, CHANNEL_RECEIVE, {
|
|
type = "status",
|
|
turtleID = os.getComputerID(),
|
|
mode = state.mode,
|
|
position = state.position,
|
|
homePosition = state.homePosition,
|
|
fuel = state.fuel,
|
|
inventoryCount = #state.inventory,
|
|
inventory = state.inventory,
|
|
facing = facing,
|
|
surroundings = surroundings
|
|
})
|
|
end
|
|
|
|
-- Return to home step (non-blocking)
|
|
local returnHomeAttempts = 0
|
|
local function returnHomeStep()
|
|
if not state.homePosition or not state.position then
|
|
print("No home or position set!")
|
|
state.mode = "idle"
|
|
broadcastStatus()
|
|
return true -- Done (failed)
|
|
end
|
|
|
|
if getDistance(state.position, state.homePosition) <= 1 then
|
|
print("Arrived at home!")
|
|
state.mode = "idle"
|
|
state.stuckCounter = 0
|
|
broadcastStatus()
|
|
return true -- Done (success)
|
|
end
|
|
|
|
if returnHomeAttempts >= 1000 then
|
|
print("Could not reach home - gave up after 1000 attempts")
|
|
state.mode = "idle"
|
|
broadcastStatus()
|
|
return true -- Done (failed)
|
|
end
|
|
|
|
updateFuel()
|
|
if needsRefuel() then
|
|
print("Low fuel, trying to refuel...")
|
|
if not tryRefuel() then
|
|
print("WARNING: Out of fuel!")
|
|
state.mode = "idle"
|
|
broadcastStatus()
|
|
return true -- Done (failed)
|
|
end
|
|
print("Refueled!")
|
|
end
|
|
|
|
navigateTowards(state.homePosition)
|
|
returnHomeAttempts = returnHomeAttempts + 1
|
|
|
|
-- Update GPS position every few steps
|
|
if returnHomeAttempts % 10 == 0 then
|
|
updatePosition()
|
|
print("Distance to home: " .. getDistance(state.position, state.homePosition))
|
|
end
|
|
|
|
return false -- Not done yet
|
|
end
|
|
|
|
-- Start return home (just initialize)
|
|
local function returnHome()
|
|
print("Returning home...")
|
|
state.mode = "returning"
|
|
state.stuckCounter = 0
|
|
returnHomeAttempts = 0
|
|
broadcastStatus()
|
|
end
|
|
|
|
-- Helper functions for intelligent exploration
|
|
local function positionKey(pos)
|
|
if not pos then return nil end
|
|
return string.format("%d,%d,%d", pos.x, pos.y, pos.z)
|
|
end
|
|
|
|
local function hasVisited(pos)
|
|
local key = positionKey(pos)
|
|
return state.visitedPositions[key] ~= nil
|
|
end
|
|
|
|
local function markVisited(pos)
|
|
local key = positionKey(pos)
|
|
state.visitedPositions[key] = os.epoch("utc")
|
|
end
|
|
|
|
local function isStuckInSamePosition()
|
|
if not state.lastPosition or not state.position then
|
|
return false
|
|
end
|
|
return state.lastPosition.x == state.position.x and
|
|
state.lastPosition.y == state.position.y and
|
|
state.lastPosition.z == state.position.z
|
|
end
|
|
|
|
local function canMoveInDirection(direction)
|
|
if direction == "forward" then
|
|
local hasBlock = turtle.inspect()
|
|
return not hasBlock
|
|
elseif direction == "up" then
|
|
local hasBlock = turtle.inspectUp()
|
|
return not hasBlock
|
|
elseif direction == "down" then
|
|
local hasBlock = turtle.inspectDown()
|
|
return not hasBlock
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function isInHole()
|
|
-- Check if turtle is in a hole (blocks above preventing escape)
|
|
local aboveBlocked = turtle.inspectUp()
|
|
local forwardBlocked = turtle.inspect()
|
|
|
|
-- If above is blocked and we can't move forward easily, we might be in a hole
|
|
return aboveBlocked and forwardBlocked
|
|
end
|
|
|
|
local function tryClimbOut()
|
|
print("Attempting to climb out of hole...")
|
|
|
|
-- Try to dig up and go up
|
|
for i = 1, 3 do
|
|
if canMoveInDirection("up") then
|
|
if smartUp() then
|
|
print("Climbed up 1 block")
|
|
state.stuckInHoleCounter = 0
|
|
return true
|
|
end
|
|
else
|
|
smartDig("up")
|
|
sleep(0.1)
|
|
end
|
|
end
|
|
|
|
-- Try all horizontal directions
|
|
for i = 1, 4 do
|
|
if canMoveInDirection("forward") then
|
|
if smartForward() then
|
|
print("Moved forward out of hole")
|
|
state.stuckInHoleCounter = 0
|
|
return true
|
|
end
|
|
else
|
|
smartDig("forward")
|
|
sleep(0.1)
|
|
if smartForward() then
|
|
print("Dug and moved forward")
|
|
state.stuckInHoleCounter = 0
|
|
return true
|
|
end
|
|
end
|
|
smartTurnRight()
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Exploration algorithm with intelligent navigation
|
|
local function exploreStep()
|
|
-- Mark current position as visited
|
|
if state.position then
|
|
markVisited(state.position)
|
|
end
|
|
|
|
-- Check if stuck in same position
|
|
if isStuckInSamePosition() then
|
|
state.stuckInHoleCounter = state.stuckInHoleCounter + 1
|
|
print("Stuck counter: " .. state.stuckInHoleCounter)
|
|
else
|
|
state.stuckInHoleCounter = 0
|
|
end
|
|
|
|
-- Save current position for next check
|
|
if state.position then
|
|
state.lastPosition = {
|
|
x = state.position.x,
|
|
y = state.position.y,
|
|
z = state.position.z
|
|
}
|
|
end
|
|
|
|
-- If stuck in hole for too long, try to climb out
|
|
if state.stuckInHoleCounter > 3 or isInHole() then
|
|
if tryClimbOut() then
|
|
return -- Successfully climbed out
|
|
end
|
|
end
|
|
|
|
-- Check all directions for valuable ores first (priority)
|
|
for _, direction in ipairs({"forward", "up", "down"}) do
|
|
local dug, valuable, blockName = smartDig(direction)
|
|
if valuable then
|
|
print("Found: " .. blockName)
|
|
broadcastStatus()
|
|
end
|
|
end
|
|
|
|
-- Smart movement decision making
|
|
local canGoUp = canMoveInDirection("up")
|
|
local canGoForward = canMoveInDirection("forward")
|
|
local canGoDown = canMoveInDirection("down")
|
|
|
|
-- If we're too deep (below y=0) or stuck, prioritize going up
|
|
if state.position and (state.position.y < 0 or state.stuckInHoleCounter > 1) then
|
|
if canGoUp then
|
|
if smartUp() then
|
|
print("Climbing up (too deep or stuck)")
|
|
return
|
|
end
|
|
else
|
|
smartDig("up")
|
|
if smartUp() then
|
|
print("Dug up and climbed")
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Intelligent direction selection based on visited positions
|
|
local forwardPos = nil
|
|
if state.position then
|
|
forwardPos = {x = state.position.x, y = state.position.y, z = state.position.z}
|
|
if facing == 0 then forwardPos.z = forwardPos.z - 1
|
|
elseif facing == 1 then forwardPos.x = forwardPos.x + 1
|
|
elseif facing == 2 then forwardPos.z = forwardPos.z + 1
|
|
elseif facing == 3 then forwardPos.x = forwardPos.x - 1
|
|
end
|
|
end
|
|
|
|
-- Prefer unvisited directions
|
|
local forwardVisited = hasVisited(forwardPos)
|
|
|
|
-- Decision tree for exploration
|
|
local r = math.random(1, 100)
|
|
|
|
if forwardVisited and r < 60 then
|
|
-- If forward is visited, turn to find new direction (60% chance)
|
|
local turnAttempts = 0
|
|
while turnAttempts < 4 do
|
|
if math.random() > 0.5 then
|
|
smartTurnRight()
|
|
else
|
|
smartTurnLeft()
|
|
end
|
|
|
|
-- Check new forward position
|
|
if state.position then
|
|
forwardPos = {x = state.position.x, y = state.position.y, z = state.position.z}
|
|
if facing == 0 then forwardPos.z = forwardPos.z - 1
|
|
elseif facing == 1 then forwardPos.x = forwardPos.x + 1
|
|
elseif facing == 2 then forwardPos.z = forwardPos.z + 1
|
|
elseif facing == 3 then forwardPos.x = forwardPos.x - 1
|
|
end
|
|
|
|
if not hasVisited(forwardPos) then
|
|
break -- Found unvisited direction
|
|
end
|
|
end
|
|
turnAttempts = turnAttempts + 1
|
|
end
|
|
elseif r < 20 and canGoUp then
|
|
-- Go up 20% of time to avoid getting too deep
|
|
if not smartUp() then
|
|
smartDig("up")
|
|
smartUp()
|
|
end
|
|
elseif r < 30 and canGoDown and state.position and state.position.y > 10 then
|
|
-- Go down only if not too deep (30% of time, above y=10)
|
|
if not smartDown() then
|
|
smartDig("down")
|
|
smartDown()
|
|
end
|
|
else
|
|
-- Try to move forward (main exploration)
|
|
if canGoForward then
|
|
if not smartForward() then
|
|
-- Blocked, try to dig
|
|
smartDig("forward")
|
|
if not smartForward() then
|
|
-- Still blocked, turn
|
|
if math.random() > 0.5 then
|
|
smartTurnRight()
|
|
else
|
|
smartTurnLeft()
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- Can't move forward, dig and try
|
|
smartDig("forward")
|
|
if not smartForward() then
|
|
-- Failed, turn to new direction
|
|
if math.random() > 0.5 then
|
|
smartTurnRight()
|
|
else
|
|
smartTurnLeft()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Command handling
|
|
local commands = {
|
|
forward = function() return smartForward(), "Moved forward" end,
|
|
back = function() return smartBack(), "Moved back" end,
|
|
up = function() return smartUp(), "Moved up" end,
|
|
down = function() return smartDown(), "Moved down" end,
|
|
turnLeft = function() return smartTurnLeft(), "Turned left" end,
|
|
turnRight = function() return smartTurnRight(), "Turned right" end,
|
|
dig = function()
|
|
local dug, val, name = smartDig("forward")
|
|
return dug, dug and "Dug: " .. (name or "block") or "Nothing to dig"
|
|
end,
|
|
digUp = function()
|
|
local dug, val, name = smartDig("up")
|
|
return dug, dug and "Dug up: " .. (name or "block") or "Nothing above"
|
|
end,
|
|
digDown = function()
|
|
local dug, val, name = smartDig("down")
|
|
return dug, dug and "Dug down: " .. (name or "block") or "Nothing below"
|
|
end,
|
|
place = function() return turtle.place(), "Placed" end,
|
|
select = function(slot) return turtle.select(slot), "Selected slot " .. slot end,
|
|
refuel = function()
|
|
local success = tryRefuel()
|
|
updateFuel()
|
|
broadcastStatus()
|
|
return success, success and ("Refueled! Now: " .. state.fuel) or "No fuel"
|
|
end,
|
|
status = function() broadcastStatus() return true, "Status sent" end,
|
|
|
|
-- Autonomous commands
|
|
setHome = function() return setHome(), "Home set" end,
|
|
|
|
explore = function()
|
|
print("Explore command received")
|
|
if not state.homePosition then
|
|
-- Auto-set home at current position if we have GPS
|
|
if state.position then
|
|
print("Auto-setting home at current position")
|
|
state.homePosition = {
|
|
x = state.position.x,
|
|
y = state.position.y,
|
|
z = state.position.z
|
|
}
|
|
-- Sync to server
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "set_home",
|
|
turtleID = os.getComputerID(),
|
|
position = state.homePosition
|
|
})
|
|
print("✅ Home auto-set at:", textutils.serialize(state.homePosition))
|
|
else
|
|
return false, "Home not set and no GPS available! Use setHome first"
|
|
end
|
|
end
|
|
if isTooFarFromHome() then
|
|
return false, "Already at max distance from home"
|
|
end
|
|
state.mode = "exploring"
|
|
state.stuckCounter = 0
|
|
broadcastStatus()
|
|
return true, "Starting exploration"
|
|
end,
|
|
|
|
mine = function()
|
|
print("Mine command received")
|
|
if not state.homePosition then
|
|
-- Auto-set home at current position if we have GPS
|
|
if state.position then
|
|
print("Auto-setting home at current position")
|
|
state.homePosition = {
|
|
x = state.position.x,
|
|
y = state.position.y,
|
|
z = state.position.z
|
|
}
|
|
-- Sync to server
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "set_home",
|
|
turtleID = os.getComputerID(),
|
|
position = state.homePosition
|
|
})
|
|
print("✅ Home auto-set at:", textutils.serialize(state.homePosition))
|
|
else
|
|
return false, "Home not set and no GPS available! Use setHome first"
|
|
end
|
|
end
|
|
if isTooFarFromHome() then
|
|
return false, "Already at max distance from home"
|
|
end
|
|
state.mode = "exploring" -- Use exploring mode for mining
|
|
state.stuckCounter = 0
|
|
broadcastStatus()
|
|
return true, "Starting mining"
|
|
end,
|
|
|
|
returnHome = function()
|
|
print("Return home command received")
|
|
state.mode = "returning"
|
|
state.stuckCounter = 0
|
|
broadcastStatus()
|
|
return true, "Returning home"
|
|
end,
|
|
|
|
stop = function()
|
|
print("STOP command received")
|
|
state.mode = "idle"
|
|
state.stuckCounter = 0
|
|
broadcastStatus()
|
|
return true, "Stopped"
|
|
end,
|
|
|
|
manual = function()
|
|
state.mode = "manual"
|
|
broadcastStatus()
|
|
return true, "Manual control"
|
|
end,
|
|
}
|
|
|
|
-- Process incoming messages
|
|
local function processMessage(message)
|
|
if type(message) ~= "table" then
|
|
return
|
|
end
|
|
|
|
-- Check if message is targeted to this turtle
|
|
local myID = os.getComputerID()
|
|
if message.target and message.target ~= myID then
|
|
-- This command is for a different turtle, ignore it
|
|
return
|
|
end
|
|
|
|
print("Received command message")
|
|
print(" Target: " .. tostring(message.target))
|
|
print(" My ID: " .. myID)
|
|
|
|
if message.command then
|
|
print(" Command: " .. message.command)
|
|
|
|
local handler = commands[message.command]
|
|
if handler then
|
|
print(" Executing command...")
|
|
local success, result = handler(message.param)
|
|
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
status = success and "ok" or "error",
|
|
message = result or "",
|
|
turtleID = myID
|
|
})
|
|
|
|
broadcastStatus()
|
|
else
|
|
print(" No handler found for command: " .. message.command)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Main loop
|
|
print("Initializing...")
|
|
print("Checking for wireless modem...")
|
|
if not modem then
|
|
print("ERROR: Wireless modem not found!")
|
|
else
|
|
print("Wireless modem: OK")
|
|
end
|
|
|
|
print("Attempting quick GPS check...")
|
|
-- Single quick GPS attempt (5 second timeout instead of 10)
|
|
local x, y, z = gps.locate(5)
|
|
if x then
|
|
state.position = {x = math.floor(x), y = math.floor(y), z = math.floor(z)}
|
|
print("GPS: OK - Position acquired!")
|
|
else
|
|
print("GPS: Not available - will retry in background")
|
|
print("Position tracking disabled for now.")
|
|
end
|
|
|
|
-- Sync state with server
|
|
print("Syncing with server...")
|
|
if syncHomeWithServer() then
|
|
print("✅ Server sync complete")
|
|
else
|
|
print("⚠️ Server sync failed, using local state")
|
|
end
|
|
|
|
updateFuel()
|
|
|
|
print("Ready! Waiting for commands...")
|
|
print("Broadcasting initial status...")
|
|
broadcastStatus()
|
|
print("Turtle " .. os.getComputerID() .. " is online!")
|
|
|
|
-- Main execution loop
|
|
parallel.waitForAny(
|
|
function()
|
|
-- Status broadcast loop
|
|
while true do
|
|
sleep(config.statusUpdateInterval)
|
|
broadcastStatus()
|
|
end
|
|
end,
|
|
|
|
function()
|
|
-- Command processing loop
|
|
while true do
|
|
local event, side, channel, replyChannel, message = os.pullEvent()
|
|
|
|
if event == "modem_message" then
|
|
print("Modem message on channel " .. channel)
|
|
if channel == CHANNEL_RECEIVE then
|
|
-- Handle home position sync responses
|
|
if type(message) == "table" then
|
|
if message.type == "home_position" and message.turtleID == os.getComputerID() then
|
|
if message.homePosition then
|
|
state.homePosition = message.homePosition
|
|
print("📍 Synced home from server:", textutils.serialize(message.homePosition))
|
|
else
|
|
print("No home position on server")
|
|
end
|
|
elseif message.type == "home_set_confirm" and message.turtleID == os.getComputerID() then
|
|
if message.homePosition then
|
|
state.homePosition = message.homePosition
|
|
print("✅ Server confirmed home:", textutils.serialize(message.homePosition))
|
|
end
|
|
else
|
|
-- Regular command processing
|
|
processMessage(message)
|
|
end
|
|
else
|
|
processMessage(message)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
|
|
function()
|
|
-- Autonomous execution loop
|
|
while true do
|
|
if state.mode == "exploring" then
|
|
updateFuel()
|
|
|
|
-- Safety check: too far from home
|
|
if isTooFarFromHome() then
|
|
print("Too far from home! Returning...")
|
|
returnHome() -- Initialize return home
|
|
-- Check if we need to pause (chunk loading concern)
|
|
elseif shouldPauseOperations() then
|
|
print("Pausing operations (safety limit)")
|
|
state.mode = "idle"
|
|
broadcastStatus()
|
|
-- Check if we should stop
|
|
elseif needsRefuel() or inventoryFull() then
|
|
print("Need to return home")
|
|
returnHome() -- Initialize return home
|
|
else
|
|
exploreStep()
|
|
sleep(0.2)
|
|
end
|
|
|
|
elseif state.mode == "returning" then
|
|
-- Non-blocking return home step
|
|
local done = returnHomeStep()
|
|
if not done then
|
|
sleep(0.1)
|
|
end
|
|
|
|
else
|
|
-- Idle or manual mode
|
|
sleep(0.5)
|
|
end
|
|
end
|
|
end
|
|
)
|