680 lines
20 KiB
Lua
680 lines
20 KiB
Lua
-- Touch-Enabled Command Center for Pocket Computer (FIXED)
|
|
-- Monitor and control autonomous mining turtles
|
|
|
|
local CHANNEL_SEND = 100
|
|
local CHANNEL_RECEIVE = 101
|
|
local STATUS_CHANNEL = 102
|
|
|
|
local modem = peripheral.find("modem")
|
|
if not modem then
|
|
error("No wireless modem found!")
|
|
end
|
|
|
|
if pocket then
|
|
pocket.equipBack()
|
|
modem = peripheral.find("modem")
|
|
end
|
|
|
|
modem.open(CHANNEL_RECEIVE)
|
|
modem.open(STATUS_CHANNEL)
|
|
|
|
local w, h = term.getSize()
|
|
|
|
-- Tracked turtles
|
|
local turtles = {}
|
|
local selectedTurtle = nil
|
|
local viewMode = "overview" -- overview, detail, manual, modes
|
|
|
|
-- Button system
|
|
local buttons = {}
|
|
|
|
local function clearButtons()
|
|
buttons = {}
|
|
end
|
|
|
|
local function addButton(x, y, width, height, label, action, color)
|
|
table.insert(buttons, {
|
|
x = x,
|
|
y = y,
|
|
width = width,
|
|
height = height,
|
|
label = label,
|
|
action = action,
|
|
color = color or colors.gray
|
|
})
|
|
end
|
|
|
|
local function drawButton(btn, highlight)
|
|
term.setCursorPos(btn.x, btn.y)
|
|
|
|
if highlight then
|
|
term.setBackgroundColor(colors.white)
|
|
term.setTextColor(colors.black)
|
|
else
|
|
term.setBackgroundColor(btn.color)
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local text = btn.label
|
|
local padding = math.floor((btn.width - #text) / 2)
|
|
|
|
for dy = 0, btn.height - 1 do
|
|
term.setCursorPos(btn.x, btn.y + dy)
|
|
if dy == math.floor(btn.height / 2) then
|
|
term.write(string.rep(" ", padding) .. text .. string.rep(" ", btn.width - padding - #text))
|
|
else
|
|
term.write(string.rep(" ", btn.width))
|
|
end
|
|
end
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local function checkButton(x, y)
|
|
for _, btn in ipairs(buttons) do
|
|
if x >= btn.x and x < btn.x + btn.width and
|
|
y >= btn.y and y < btn.y + btn.height then
|
|
return btn
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Send command to turtle
|
|
local function sendCommand(turtleID, command, param)
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
command = command,
|
|
param = param,
|
|
target = turtleID
|
|
})
|
|
end
|
|
|
|
-- Send state change command to turtle (new protocol)
|
|
local function sendStateCommand(turtleID, stateName, stateData)
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "state_change",
|
|
state = stateName,
|
|
data = stateData or {},
|
|
target = turtleID
|
|
})
|
|
end
|
|
|
|
-- Helper function to format fuel display
|
|
local function formatFuel(fuel)
|
|
if not fuel then
|
|
return "?"
|
|
elseif fuel == "unlimited" then
|
|
return "INF"
|
|
else
|
|
return tostring(fuel)
|
|
end
|
|
end
|
|
|
|
-- Draw UI modes
|
|
local function drawOverview()
|
|
clearButtons()
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
term.setTextColor(colors.yellow)
|
|
print("=MINING CMD CENTER=")
|
|
term.setTextColor(colors.white)
|
|
|
|
local startY = 3
|
|
|
|
if #turtles == 0 then
|
|
term.setCursorPos(1, startY)
|
|
print("No turtles online")
|
|
print("Waiting...")
|
|
else
|
|
for i, turtle in ipairs(turtles) do
|
|
if i > 3 then break end -- Max 3 turtles on screen
|
|
|
|
local y = startY + (i - 1) * 4
|
|
local selected = (selectedTurtle == i)
|
|
|
|
-- Turtle info box
|
|
term.setCursorPos(1, y)
|
|
term.setTextColor(selected and colors.lime or colors.white)
|
|
print(string.format("T-%d [%s]", turtle.turtleID or 0, turtle.state or turtle.mode or "idle"))
|
|
|
|
if turtle.position then
|
|
print(string.format(" %d,%d,%d",
|
|
turtle.position.x or 0,
|
|
turtle.position.y or 0,
|
|
turtle.position.z or 0))
|
|
else
|
|
print(" No GPS")
|
|
end
|
|
|
|
print(string.format(" F:%s I:%d",
|
|
formatFuel(turtle.fuel),
|
|
turtle.inventoryCount or 0))
|
|
|
|
-- Select button
|
|
addButton(20, y, 6, 2, "SEL", function()
|
|
selectedTurtle = i
|
|
viewMode = "detail"
|
|
end, selected and colors.lime or colors.blue)
|
|
|
|
drawButton(buttons[#buttons])
|
|
end
|
|
end
|
|
|
|
-- Bottom buttons
|
|
local btnY = h - 3
|
|
addButton(1, btnY, 8, 2, "EXPLORE", function()
|
|
if selectedTurtle and turtles[selectedTurtle] then
|
|
sendCommand(turtles[selectedTurtle].turtleID, "explore")
|
|
end
|
|
end, colors.green)
|
|
|
|
addButton(10, btnY, 8, 2, "HOME", function()
|
|
if selectedTurtle and turtles[selectedTurtle] then
|
|
sendCommand(turtles[selectedTurtle].turtleID, "returnHome")
|
|
end
|
|
end, colors.orange)
|
|
|
|
addButton(19, btnY, 7, 2, "STOP", function()
|
|
if selectedTurtle and turtles[selectedTurtle] then
|
|
sendCommand(turtles[selectedTurtle].turtleID, "stop")
|
|
end
|
|
end, colors.red)
|
|
|
|
for _, btn in ipairs(buttons) do
|
|
drawButton(btn)
|
|
end
|
|
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local function drawDetail()
|
|
if not selectedTurtle or not turtles[selectedTurtle] then
|
|
viewMode = "overview"
|
|
drawOverview()
|
|
return
|
|
end
|
|
|
|
clearButtons()
|
|
local turtle = turtles[selectedTurtle]
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
term.setTextColor(colors.yellow)
|
|
print("=TURTLE " .. (turtle.turtleID or "?") .. "=")
|
|
term.setTextColor(colors.white)
|
|
|
|
print("")
|
|
print("State: " .. (turtle.state or turtle.mode or "idle"))
|
|
print("Fuel: " .. formatFuel(turtle.fuel))
|
|
|
|
if turtle.position then
|
|
print(string.format("Pos: %d,%d,%d",
|
|
turtle.position.x or 0,
|
|
turtle.position.y or 0,
|
|
turtle.position.z or 0))
|
|
else
|
|
print("Pos: No GPS")
|
|
end
|
|
|
|
if turtle.homePosition and turtle.position then
|
|
local dist = math.abs((turtle.position.x or 0) - (turtle.homePosition.x or 0)) +
|
|
math.abs((turtle.position.y or 0) - (turtle.homePosition.y or 0)) +
|
|
math.abs((turtle.position.z or 0) - (turtle.homePosition.z or 0))
|
|
print("Home: " .. dist .. " blocks")
|
|
elseif turtle.homePosition then
|
|
print("Home: Set")
|
|
else
|
|
print("Home: Not set")
|
|
end
|
|
|
|
print("")
|
|
print("Inventory:")
|
|
if turtle.inventory and #turtle.inventory > 0 then
|
|
for i, item in ipairs(turtle.inventory) do
|
|
if i <= 4 then
|
|
local name = item.name:match("minecraft:(.+)") or item.name
|
|
if #name > 18 then name = name:sub(1, 15) .. "..." end
|
|
print(string.format("%d:%s x%d", item.slot or 0, name, item.count or 0))
|
|
end
|
|
end
|
|
else
|
|
print(" Empty")
|
|
end
|
|
|
|
-- Action buttons (row 1: explore/home/stop)
|
|
local btnY = h - 10
|
|
addButton(1, btnY, 8, 2, "EXPLORE", function()
|
|
sendCommand(turtle.turtleID, "explore")
|
|
end, colors.green)
|
|
|
|
addButton(10, btnY, 8, 2, "HOME", function()
|
|
sendCommand(turtle.turtleID, "returnHome")
|
|
end, colors.orange)
|
|
|
|
addButton(19, btnY, 7, 2, "STOP", function()
|
|
sendCommand(turtle.turtleID, "stop")
|
|
end, colors.red)
|
|
|
|
-- Row 2: manual/modes/setHome
|
|
btnY = h - 7
|
|
addButton(1, btnY, 8, 2, "MANUAL", function()
|
|
viewMode = "manual"
|
|
sendCommand(turtle.turtleID, "manual")
|
|
end, colors.purple)
|
|
|
|
addButton(10, btnY, 8, 2, "MODES", function()
|
|
viewMode = "modes"
|
|
end, colors.cyan)
|
|
|
|
addButton(19, btnY, 7, 2, "HOME*", function()
|
|
sendCommand(turtle.turtleID, "setHome")
|
|
end, colors.blue)
|
|
|
|
-- Row 3: equip/rename
|
|
btnY = h - 4
|
|
addButton(1, btnY, 8, 2, "EQUIP L", function()
|
|
-- Send eval to equip left
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "eval",
|
|
uuid = tostring(math.random(100000, 999999)),
|
|
code = "return turtle.equipLeft()",
|
|
target = turtle.turtleID
|
|
})
|
|
end, colors.purple)
|
|
|
|
addButton(10, btnY, 8, 2, "EQUIP R", function()
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "eval",
|
|
uuid = tostring(math.random(100000, 999999)),
|
|
code = "return turtle.equipRight()",
|
|
target = turtle.turtleID
|
|
})
|
|
end, colors.purple)
|
|
|
|
addButton(19, btnY, 7, 2, "RENAME", function()
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
term.setTextColor(colors.yellow)
|
|
print("Enter new name:")
|
|
term.setTextColor(colors.white)
|
|
local name = read()
|
|
if name and #name > 0 then
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "rename",
|
|
name = name,
|
|
target = turtle.turtleID
|
|
})
|
|
end
|
|
draw()
|
|
end, colors.lightBlue)
|
|
|
|
addButton(1, h - 1, 12, 2, "< BACK", function()
|
|
viewMode = "overview"
|
|
end, colors.gray)
|
|
|
|
for _, btn in ipairs(buttons) do
|
|
drawButton(btn)
|
|
end
|
|
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local function drawManual()
|
|
if not selectedTurtle or not turtles[selectedTurtle] then
|
|
viewMode = "overview"
|
|
return
|
|
end
|
|
|
|
clearButtons()
|
|
local turtle = turtles[selectedTurtle]
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
term.setTextColor(colors.red)
|
|
print("=MANUAL CONTROL=")
|
|
term.setTextColor(colors.white)
|
|
|
|
print(string.format("T-%d Fuel:%s",
|
|
turtle.turtleID or 0,
|
|
formatFuel(turtle.fuel)))
|
|
|
|
-- Movement buttons
|
|
local centerX = 6
|
|
local centerY = 5
|
|
|
|
-- Forward
|
|
addButton(centerX, centerY, 3, 2, "W", function()
|
|
sendCommand(turtle.turtleID, "forward")
|
|
end, colors.blue)
|
|
|
|
-- Left
|
|
addButton(centerX - 4, centerY + 2, 3, 2, "A", function()
|
|
sendCommand(turtle.turtleID, "turnLeft")
|
|
end, colors.blue)
|
|
|
|
-- Back
|
|
addButton(centerX, centerY + 2, 3, 2, "S", function()
|
|
sendCommand(turtle.turtleID, "back")
|
|
end, colors.blue)
|
|
|
|
-- Right
|
|
addButton(centerX + 4, centerY + 2, 3, 2, "D", function()
|
|
sendCommand(turtle.turtleID, "turnRight")
|
|
end, colors.blue)
|
|
|
|
-- Up/Down
|
|
addButton(centerX + 9, centerY, 4, 2, "UP", function()
|
|
sendCommand(turtle.turtleID, "up")
|
|
end, colors.cyan)
|
|
|
|
addButton(centerX + 9, centerY + 2, 4, 2, "DN", function()
|
|
sendCommand(turtle.turtleID, "down")
|
|
end, colors.cyan)
|
|
|
|
-- Action buttons
|
|
local actY = centerY + 5
|
|
addButton(1, actY, 6, 2, "DIG", function()
|
|
sendCommand(turtle.turtleID, "dig")
|
|
end, colors.orange)
|
|
|
|
addButton(8, actY, 6, 2, "PLACE", function()
|
|
sendCommand(turtle.turtleID, "place")
|
|
end, colors.green)
|
|
|
|
addButton(15, actY, 6, 2, "DIGUP", function()
|
|
sendCommand(turtle.turtleID, "digUp")
|
|
end, colors.orange)
|
|
|
|
addButton(22, actY, 5, 2, "DIGD", function()
|
|
sendCommand(turtle.turtleID, "digDown")
|
|
end, colors.orange)
|
|
|
|
-- Bottom buttons
|
|
addButton(1, h - 3, 8, 2, "HOME", function()
|
|
sendCommand(turtle.turtleID, "returnHome")
|
|
end, colors.orange)
|
|
|
|
addButton(10, h - 3, 8, 2, "REFUEL", function()
|
|
sendCommand(turtle.turtleID, "refuel")
|
|
end, colors.lime)
|
|
|
|
addButton(19, h - 3, 7, 2, "SORT", function()
|
|
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
|
type = "eval",
|
|
uuid = tostring(math.random(100000, 999999)),
|
|
code = [[
|
|
local moved = 0
|
|
for slot = 1, 16 do
|
|
local item = turtle.getItemDetail(slot)
|
|
if item then
|
|
for target = 1, slot - 1 do
|
|
local ti = turtle.getItemDetail(target)
|
|
if not ti then
|
|
turtle.select(slot)
|
|
turtle.transferTo(target)
|
|
moved = moved + 1
|
|
break
|
|
elseif ti.name == item.name and ti.count < 64 then
|
|
turtle.select(slot)
|
|
turtle.transferTo(target)
|
|
moved = moved + 1
|
|
if turtle.getItemCount(slot) == 0 then break end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
turtle.select(1)
|
|
return moved
|
|
]],
|
|
target = turtle.turtleID
|
|
})
|
|
end, colors.cyan)
|
|
|
|
addButton(1, h, 12, 1, "< BACK", function()
|
|
viewMode = "detail"
|
|
end, colors.gray)
|
|
|
|
addButton(14, h, 13, 1, "AUTO MODE", function()
|
|
sendCommand(turtle.turtleID, "explore")
|
|
viewMode = "detail"
|
|
end, colors.purple)
|
|
|
|
for _, btn in ipairs(buttons) do
|
|
drawButton(btn)
|
|
end
|
|
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local function drawModes()
|
|
if not selectedTurtle or not turtles[selectedTurtle] then
|
|
viewMode = "overview"
|
|
return
|
|
end
|
|
|
|
clearButtons()
|
|
local turtle = turtles[selectedTurtle]
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
term.setTextColor(colors.cyan)
|
|
print("=STATE MACHINE=")
|
|
term.setTextColor(colors.white)
|
|
print(string.format("T-%d [%s]", turtle.turtleID or 0, turtle.state or turtle.mode or "idle"))
|
|
|
|
-- State buttons
|
|
local btnY = 4
|
|
local btnW = 12
|
|
|
|
addButton(1, btnY, btnW, 2, "IDLE", function()
|
|
sendStateCommand(turtle.turtleID, "idle")
|
|
sendCommand(turtle.turtleID, "stop")
|
|
end, colors.gray)
|
|
|
|
addButton(14, btnY, btnW, 2, "EXPLORE", function()
|
|
sendStateCommand(turtle.turtleID, "exploring")
|
|
sendCommand(turtle.turtleID, "explore")
|
|
end, colors.green)
|
|
|
|
btnY = btnY + 3
|
|
addButton(1, btnY, btnW, 2, "MINE", function()
|
|
sendStateCommand(turtle.turtleID, "mining")
|
|
sendCommand(turtle.turtleID, "explore")
|
|
end, colors.orange)
|
|
|
|
addButton(14, btnY, btnW, 2, "FARM", function()
|
|
sendStateCommand(turtle.turtleID, "farming")
|
|
end, colors.lime)
|
|
|
|
btnY = btnY + 3
|
|
addButton(1, btnY, btnW, 2, "GO HOME", function()
|
|
sendStateCommand(turtle.turtleID, "goHome")
|
|
sendCommand(turtle.turtleID, "returnHome")
|
|
end, colors.yellow)
|
|
|
|
addButton(14, btnY, btnW, 2, "REFUEL", function()
|
|
sendStateCommand(turtle.turtleID, "refueling")
|
|
sendCommand(turtle.turtleID, "refuel")
|
|
end, colors.red)
|
|
|
|
btnY = btnY + 3
|
|
addButton(1, btnY, btnW, 2, "DUMP INV", function()
|
|
sendStateCommand(turtle.turtleID, "dumpInventory")
|
|
end, colors.brown)
|
|
|
|
addButton(14, btnY, btnW, 2, "MOVE TO", function()
|
|
-- Could prompt for coordinates, for now just sends moving state
|
|
sendStateCommand(turtle.turtleID, "moving")
|
|
end, colors.lightBlue)
|
|
|
|
btnY = btnY + 3
|
|
addButton(1, btnY, btnW, 2, "SCAN", function()
|
|
sendStateCommand(turtle.turtleID, "scan")
|
|
end, colors.purple)
|
|
|
|
addButton(14, btnY, btnW, 2, "EXTRACT", function()
|
|
sendStateCommand(turtle.turtleID, "extraction")
|
|
end, colors.magenta)
|
|
|
|
btnY = btnY + 3
|
|
addButton(1, btnY, btnW, 2, "BUILD", function()
|
|
sendStateCommand(turtle.turtleID, "building")
|
|
end, colors.lightBlue)
|
|
|
|
addButton(14, btnY, btnW, 2, "AUTOCRAFT", function()
|
|
sendStateCommand(turtle.turtleID, "autocraft")
|
|
end, colors.pink)
|
|
|
|
-- Back button
|
|
addButton(1, h - 1, 12, 2, "< BACK", function()
|
|
viewMode = "detail"
|
|
end, colors.gray)
|
|
|
|
for _, btn in ipairs(buttons) do
|
|
drawButton(btn)
|
|
end
|
|
|
|
term.setTextColor(colors.white)
|
|
end
|
|
|
|
local function draw()
|
|
if viewMode == "overview" then
|
|
drawOverview()
|
|
elseif viewMode == "detail" then
|
|
drawDetail()
|
|
elseif viewMode == "manual" then
|
|
drawManual()
|
|
elseif viewMode == "modes" then
|
|
drawModes()
|
|
end
|
|
end
|
|
|
|
-- Handle keyboard input (still works alongside touch)
|
|
local function handleKey(key)
|
|
if viewMode == "manual" then
|
|
local turtle = turtles[selectedTurtle]
|
|
if not turtle then return end
|
|
|
|
local cmd = nil
|
|
if key == keys.w then cmd = "forward"
|
|
elseif key == keys.s then cmd = "back"
|
|
elseif key == keys.a then cmd = "turnLeft"
|
|
elseif key == keys.d then cmd = "turnRight"
|
|
elseif key == keys.space then cmd = "up"
|
|
elseif key == keys.leftShift or key == keys.rightShift then cmd = "down"
|
|
elseif key == keys.e then cmd = "dig"
|
|
elseif key == keys.q then cmd = "place"
|
|
end
|
|
|
|
if cmd then
|
|
sendCommand(turtle.turtleID, cmd)
|
|
end
|
|
elseif viewMode == "overview" then
|
|
if key >= keys.one and key <= keys.nine then
|
|
local index = key - keys.one + 1
|
|
if turtles[index] then
|
|
selectedTurtle = index
|
|
viewMode = "detail"
|
|
draw()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Handle touch
|
|
local function handleTouch(x, y)
|
|
local btn = checkButton(x, y)
|
|
if btn and btn.action then
|
|
-- Visual feedback
|
|
drawButton(btn, true)
|
|
sleep(0.1)
|
|
drawButton(btn, false)
|
|
|
|
-- Execute action
|
|
btn.action()
|
|
draw()
|
|
end
|
|
end
|
|
|
|
-- Main loop
|
|
print("Initializing...")
|
|
sleep(0.5)
|
|
draw()
|
|
|
|
parallel.waitForAny(
|
|
function()
|
|
-- Touch/click handler
|
|
while true do
|
|
local event, p1, p2, p3 = os.pullEvent()
|
|
if event == "mouse_click" or event == "monitor_touch" then
|
|
handleTouch(p2, p3)
|
|
end
|
|
end
|
|
end,
|
|
function()
|
|
-- Keyboard handler
|
|
while true do
|
|
local event, key = os.pullEvent("key")
|
|
handleKey(key)
|
|
end
|
|
end,
|
|
function()
|
|
-- Status receiver
|
|
while true do
|
|
local event, side, channel, replyChannel, message = os.pullEvent("modem_message")
|
|
|
|
if channel == STATUS_CHANNEL and type(message) == "table" and message.type == "status" then
|
|
-- Update or add turtle
|
|
local found = false
|
|
for i, t in ipairs(turtles) do
|
|
if t.turtleID == message.turtleID then
|
|
-- Preserve state if not in message
|
|
if not message.state then
|
|
message.state = t.state or message.mode or "idle"
|
|
end
|
|
turtles[i] = message
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not found then
|
|
if not message.state then
|
|
message.state = message.mode or "idle"
|
|
end
|
|
table.insert(turtles, message)
|
|
if not selectedTurtle then
|
|
selectedTurtle = 1
|
|
end
|
|
end
|
|
|
|
draw()
|
|
elseif channel == CHANNEL_RECEIVE and type(message) == "table" then
|
|
-- State change confirmation or other response
|
|
if message.type == "state_changed" and message.turtleID then
|
|
for i, t in ipairs(turtles) do
|
|
if t.turtleID == message.turtleID then
|
|
turtles[i].state = message.state
|
|
break
|
|
end
|
|
end
|
|
end
|
|
draw()
|
|
end
|
|
end
|
|
end,
|
|
function()
|
|
-- Refresh display
|
|
while true do
|
|
sleep(2)
|
|
draw()
|
|
end
|
|
end
|
|
)
|