tClearChunky.lua
331 lines · 9.3 KB
{program="tClearChunky",version="1.0",date="2024-10-22"}
Copy & run
wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/tClear/tClearChunky.lua
| 1 | --{program="tClearChunky",version="1.0",date="2024-10-22"} |
| 2 | --------------------------------------- |
| 3 | -- tClearChunky by Kaikaku |
| 4 | -- 2024-10-22, v1.0 chunky wireless turtle |
| 5 | --------------------------------------- |
| 6 | |
| 7 | --------------------------------------- |
| 8 | ---- DESCRIPTION ---------------------- |
| 9 | --------------------------------------- |
| 10 | -- Chunky wireless turtle that pairs with a main mining turtle |
| 11 | -- to keep chunks loaded and prevent the main turtle from breaking |
| 12 | -- due to chunk unloading. Follows the main turtle's movements. |
| 13 | |
| 14 | --------------------------------------- |
| 15 | ---- ASSUMPTIONS ---------------------- |
| 16 | --------------------------------------- |
| 17 | -- Requires a wireless modem for communication |
| 18 | -- Should be placed one block to the right of the main turtle |
| 19 | |
| 20 | --------------------------------------- |
| 21 | ---- VARIABLES: template -------------- |
| 22 | --------------------------------------- |
| 23 | local cVersion ="v1.0" |
| 24 | local cPrgName ="tClearChunky" |
| 25 | local blnDebugPrint = true |
| 26 | |
| 27 | --------------------------------------- |
| 28 | ---- VARIABLES: specific -------------- |
| 29 | --------------------------------------- |
| 30 | local masterTurtleId = nil |
| 31 | local chunkLoadingInterval = 2 -- seconds between chunk loading signals |
| 32 | local position = {x = 0, y = 0, z = -1, facing = 0} -- relative to master (to the left) |
| 33 | local isActive = false |
| 34 | |
| 35 | --------------------------------------- |
| 36 | ---- Communication functions ----------- |
| 37 | --------------------------------------- |
| 38 | local function findModem() |
| 39 | for _, p in pairs(rs.getSides()) do |
| 40 | if peripheral.isPresent(p) and peripheral.getType(p) == "modem" then |
| 41 | return p |
| 42 | end |
| 43 | end |
| 44 | error("No wireless modem attached to this turtle.") |
| 45 | end |
| 46 | |
| 47 | local function sendChunkLoad() |
| 48 | if masterTurtleId then |
| 49 | rednet.send(masterTurtleId, { |
| 50 | type = "chunk_load", |
| 51 | id = os.getComputerID(), |
| 52 | position = position, |
| 53 | timestamp = os.time() |
| 54 | }, "tclear-chunky") |
| 55 | end |
| 56 | end |
| 57 | |
| 58 | local function sendStatus(status, data) |
| 59 | if masterTurtleId then |
| 60 | rednet.send(masterTurtleId, { |
| 61 | type = "status", |
| 62 | status = status, |
| 63 | id = os.getComputerID(), |
| 64 | data = data or {}, |
| 65 | timestamp = os.time() |
| 66 | }, "tclear-chunky") |
| 67 | end |
| 68 | end |
| 69 | |
| 70 | --------------------------------------- |
| 71 | ---- Movement functions --------------- |
| 72 | --------------------------------------- |
| 73 | local function moveTo(targetX, targetY, targetZ, targetFacing) |
| 74 | -- Calculate relative movement needed from current position to target |
| 75 | local dx = targetX - position.x |
| 76 | local dy = targetY - position.y |
| 77 | local dz = targetZ - position.z |
| 78 | local dfacing = (targetFacing - position.facing) % 4 |
| 79 | |
| 80 | debugPrint("Moving from (" .. position.x .. "," .. position.y .. "," .. position.z .. ") to (" .. targetX .. "," .. targetY .. "," .. targetZ .. ")") |
| 81 | debugPrint("Delta: dx=" .. dx .. " dy=" .. dy .. " dz=" .. dz .. " dfacing=" .. dfacing) |
| 82 | |
| 83 | -- Turn to correct facing first |
| 84 | if dfacing == 1 then |
| 85 | turtle.turnRight() |
| 86 | position.facing = (position.facing + 1) % 4 |
| 87 | elseif dfacing == 2 then |
| 88 | turtle.turnRight() |
| 89 | turtle.turnRight() |
| 90 | position.facing = (position.facing + 2) % 4 |
| 91 | elseif dfacing == 3 then |
| 92 | turtle.turnLeft() |
| 93 | position.facing = (position.facing - 1) % 4 |
| 94 | end |
| 95 | |
| 96 | -- Move vertically first |
| 97 | while dy > 0 do |
| 98 | if turtle.up() then |
| 99 | dy = dy - 1 |
| 100 | position.y = position.y + 1 |
| 101 | debugPrint("Moved up to y=" .. position.y) |
| 102 | else |
| 103 | debugPrint("Cannot move up, blocked") |
| 104 | break |
| 105 | end |
| 106 | end |
| 107 | while dy < 0 do |
| 108 | if turtle.down() then |
| 109 | dy = dy + 1 |
| 110 | position.y = position.y - 1 |
| 111 | debugPrint("Moved down to y=" .. position.y) |
| 112 | else |
| 113 | debugPrint("Cannot move down, blocked") |
| 114 | break |
| 115 | end |
| 116 | end |
| 117 | |
| 118 | -- Move horizontally - handle X movement (forward/backward relative to facing) |
| 119 | while dx > 0 do |
| 120 | -- Try to dig if blocked, but don't get stuck |
| 121 | if not turtle.forward() then |
| 122 | debugPrint("Blocked, trying to dig forward") |
| 123 | turtle.dig() |
| 124 | sleep(0.1) -- Brief pause after digging |
| 125 | if turtle.forward() then |
| 126 | dx = dx - 1 |
| 127 | position.x = position.x + 1 |
| 128 | debugPrint("Moved forward to x=" .. position.x) |
| 129 | else |
| 130 | debugPrint("Still blocked after digging, giving up") |
| 131 | break |
| 132 | end |
| 133 | else |
| 134 | dx = dx - 1 |
| 135 | position.x = position.x + 1 |
| 136 | debugPrint("Moved forward to x=" .. position.x) |
| 137 | end |
| 138 | end |
| 139 | while dx < 0 do |
| 140 | -- Turn around to move backward |
| 141 | turtle.turnLeft() |
| 142 | turtle.turnLeft() |
| 143 | if not turtle.forward() then |
| 144 | debugPrint("Blocked, trying to dig backward") |
| 145 | turtle.dig() |
| 146 | sleep(0.1) |
| 147 | if turtle.forward() then |
| 148 | dx = dx + 1 |
| 149 | position.x = position.x - 1 |
| 150 | debugPrint("Moved backward to x=" .. position.x) |
| 151 | else |
| 152 | debugPrint("Still blocked after digging backward, giving up") |
| 153 | end |
| 154 | else |
| 155 | dx = dx + 1 |
| 156 | position.x = position.x - 1 |
| 157 | debugPrint("Moved backward to x=" .. position.x) |
| 158 | end |
| 159 | turtle.turnLeft() |
| 160 | turtle.turnLeft() |
| 161 | if dx < 0 then break end -- If still can't move, give up |
| 162 | end |
| 163 | |
| 164 | -- Move sideways - handle Z movement (left/right relative to facing) |
| 165 | while dz > 0 do |
| 166 | turtle.turnRight() |
| 167 | if not turtle.forward() then |
| 168 | debugPrint("Blocked, trying to dig right") |
| 169 | turtle.dig() |
| 170 | sleep(0.1) |
| 171 | if turtle.forward() then |
| 172 | dz = dz - 1 |
| 173 | position.z = position.z + 1 |
| 174 | debugPrint("Moved right to z=" .. position.z) |
| 175 | else |
| 176 | debugPrint("Still blocked after digging right") |
| 177 | end |
| 178 | else |
| 179 | dz = dz - 1 |
| 180 | position.z = position.z + 1 |
| 181 | debugPrint("Moved right to z=" .. position.z) |
| 182 | end |
| 183 | turtle.turnLeft() |
| 184 | if dz > 0 then break end -- If still can't move, give up |
| 185 | end |
| 186 | while dz < 0 do |
| 187 | turtle.turnLeft() |
| 188 | if not turtle.forward() then |
| 189 | debugPrint("Blocked, trying to dig left") |
| 190 | turtle.dig() |
| 191 | sleep(0.1) |
| 192 | if turtle.forward() then |
| 193 | dz = dz + 1 |
| 194 | position.z = position.z - 1 |
| 195 | debugPrint("Moved left to z=" .. position.z) |
| 196 | else |
| 197 | debugPrint("Still blocked after digging left") |
| 198 | end |
| 199 | else |
| 200 | dz = dz + 1 |
| 201 | position.z = position.z - 1 |
| 202 | debugPrint("Moved left to z=" .. position.z) |
| 203 | end |
| 204 | turtle.turnRight() |
| 205 | if dz < 0 then break end -- If still can't move, give up |
| 206 | end |
| 207 | |
| 208 | -- Update final facing |
| 209 | position.facing = targetFacing |
| 210 | debugPrint("Final position: (" .. position.x .. "," .. position.y .. "," .. position.z .. ") facing=" .. position.facing) |
| 211 | end |
| 212 | |
| 213 | --------------------------------------- |
| 214 | ---- Main functions -------------------- |
| 215 | --------------------------------------- |
| 216 | local function debugPrint(str) |
| 217 | if blnDebugPrint then |
| 218 | print("[Chunky] " .. str) |
| 219 | end |
| 220 | end |
| 221 | |
| 222 | local function processMessage(message) |
| 223 | if message.type == "find_chunky" then |
| 224 | -- Master turtle is looking for chunky turtles |
| 225 | print("Master turtle " .. (message.masterId or "unknown") .. " is looking for chunky turtles") |
| 226 | -- Send response |
| 227 | rednet.send(message.masterId, { |
| 228 | type = "chunky_available", |
| 229 | id = os.getComputerID(), |
| 230 | timestamp = os.time() |
| 231 | }, "tclear-chunky") |
| 232 | print("Sent response to master turtle") |
| 233 | return true |
| 234 | |
| 235 | elseif message.type == "pair" then |
| 236 | masterTurtleId = message.masterId |
| 237 | isActive = true |
| 238 | print("SUCCESS: Paired with master turtle " .. masterTurtleId) |
| 239 | print("Chunky turtle is now active and ready to follow!") |
| 240 | sendStatus("paired", {chunkyId = os.getComputerID()}) |
| 241 | return true |
| 242 | |
| 243 | elseif message.type == "move" then |
| 244 | if isActive and message.target then |
| 245 | debugPrint("Moving to " .. message.target.x .. "," .. message.target.y .. "," .. message.target.z) |
| 246 | moveTo(message.target.x, message.target.y, message.target.z, message.target.facing or 0) |
| 247 | sendStatus("moved", {position = position}) |
| 248 | return true |
| 249 | end |
| 250 | |
| 251 | elseif message.type == "stop" then |
| 252 | isActive = false |
| 253 | debugPrint("Stopped by master turtle") |
| 254 | sendStatus("stopped", {}) |
| 255 | return true |
| 256 | |
| 257 | elseif message.type == "ping" then |
| 258 | sendStatus("alive", {position = position}) |
| 259 | return true |
| 260 | end |
| 261 | |
| 262 | return false |
| 263 | end |
| 264 | |
| 265 | --------------------------------------- |
| 266 | ---- Main program ---------------------- |
| 267 | --------------------------------------- |
| 268 | |
| 269 | -- Initialize |
| 270 | local modemSide = findModem() |
| 271 | rednet.open(modemSide) |
| 272 | |
| 273 | local thisId = os.getComputerID() |
| 274 | print("tClearChunky v" .. cVersion .. " starting...") |
| 275 | print("Chunky turtle ID: " .. thisId) |
| 276 | print("Waiting for pairing with master turtle...") |
| 277 | |
| 278 | -- Send initial broadcast to find master |
| 279 | rednet.broadcast({ |
| 280 | type = "chunky_available", |
| 281 | id = thisId, |
| 282 | timestamp = os.time() |
| 283 | }, "tclear-chunky") |
| 284 | |
| 285 | print("Sent initial broadcast - waiting for master turtle...") |
| 286 | |
| 287 | -- Main loop |
| 288 | local lastChunkLoad = 0 |
| 289 | local lastBroadcast = 0 |
| 290 | local broadcastInterval = 5 -- Send broadcast every 5 seconds |
| 291 | |
| 292 | while true do |
| 293 | local timer = os.startTimer(0.1) -- Check for messages every 0.1 seconds |
| 294 | |
| 295 | -- Handle rednet messages |
| 296 | local senderId, message, protocol = rednet.receive(0.1) |
| 297 | if senderId then |
| 298 | -- Accept messages on multiple protocols |
| 299 | if protocol == "tclear-chunky" or protocol == "tclear-run" or protocol == "tclear" or protocol == nil then |
| 300 | local handled = processMessage(message) |
| 301 | if handled then |
| 302 | print("Processed message from " .. senderId) |
| 303 | end |
| 304 | end |
| 305 | end |
| 306 | |
| 307 | -- Send chunk loading signal periodically |
| 308 | local currentTime = os.time() |
| 309 | if isActive and (currentTime - lastChunkLoad) >= chunkLoadingInterval then |
| 310 | sendChunkLoad() |
| 311 | lastChunkLoad = currentTime |
| 312 | end |
| 313 | |
| 314 | -- Send periodic broadcasts if not paired yet |
| 315 | if not isActive and (currentTime - lastBroadcast) >= broadcastInterval then |
| 316 | rednet.broadcast({ |
| 317 | type = "chunky_available", |
| 318 | id = thisId, |
| 319 | timestamp = currentTime |
| 320 | }, "tclear-chunky") |
| 321 | print("Sent broadcast - still waiting for master turtle...") |
| 322 | lastBroadcast = currentTime |
| 323 | end |
| 324 | |
| 325 | -- Handle timer events (cleanup) |
| 326 | local event, timerId = os.pullEvent("timer") |
| 327 | if timerId == timer then |
| 328 | -- Timer expired, continue loop |
| 329 | end |
| 330 | end |
| 331 |