dome_tunnels.lua
633 lines · 18.4 KB
Dome Tunnels by Silvamord (based on epicmining_turtle.lua style) Place the turtle at the bottom-left corner of the tunnel cross-section, facing forward (along the tunnel direction). The script will carve a dome profile slice-by-slice and advance. Parameters: - Width (min 2): number of blocks across (left to right) - Side height: height of the extreme left and right columns - Center height: height of the inner columns (2..width-1) - Length: tunnel length in blocks (number of slices) - Corner radius: rounds the top corners by reducing height near the sides (radius 0 keeps sharp edges; radius 1+ trims the uppermost layers on sides) Example matching the request: - Width = 5 - Side height = 3 - Center height = 4 - Length = your choice - Corner radius = 0..2 (try 1 for a slight dome rounding) ]]-- local version = "1.1" -- UI helpers (minimal) local function ask_number_default(prompt, default_value, min_val, max_val) while true do term.clear() term.setCursorPos(1,1) write("Dome Tunnels v"..version.."\n\n")
Copy & run
wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/dome_tunnels/dome_tunnels.lua
| 1 | --[[ |
| 2 | Dome Tunnels by Silvamord (based on epicmining_turtle.lua style) |
| 3 | Place the turtle at the bottom-left corner of the tunnel cross-section, |
| 4 | facing forward (along the tunnel direction). The script will carve a dome |
| 5 | profile slice-by-slice and advance. |
| 6 | |
| 7 | Parameters: |
| 8 | - Width (min 2): number of blocks across (left to right) |
| 9 | - Side height: height of the extreme left and right columns |
| 10 | - Center height: height of the inner columns (2..width-1) |
| 11 | - Length: tunnel length in blocks (number of slices) |
| 12 | - Corner radius: rounds the top corners by reducing height near the sides |
| 13 | (radius 0 keeps sharp edges; radius 1+ trims the uppermost layers on sides) |
| 14 | |
| 15 | Example matching the request: |
| 16 | - Width = 5 |
| 17 | - Side height = 3 |
| 18 | - Center height = 4 |
| 19 | - Length = your choice |
| 20 | - Corner radius = 0..2 (try 1 for a slight dome rounding) |
| 21 | ]]-- |
| 22 | |
| 23 | local version = "1.1" |
| 24 | |
| 25 | -- UI helpers (minimal) |
| 26 | local function ask_number_default(prompt, default_value, min_val, max_val) |
| 27 | while true do |
| 28 | term.clear() |
| 29 | term.setCursorPos(1,1) |
| 30 | write("Dome Tunnels v"..version.."\n\n") |
| 31 | write(string.format("%s [default: %s] ", prompt, tostring(default_value))) |
| 32 | local s = read() |
| 33 | if s == nil or s == "" then |
| 34 | return default_value |
| 35 | end |
| 36 | local n = tonumber(s) |
| 37 | if n and (not min_val or n >= min_val) and (not max_val or n <= max_val) then |
| 38 | return n |
| 39 | end |
| 40 | write("\nInvalid input. Press Enter to try again.") |
| 41 | read() |
| 42 | end |
| 43 | end |
| 44 | |
| 45 | local function ask_yes_no(prompt, default_yes) |
| 46 | while true do |
| 47 | term.clear() |
| 48 | term.setCursorPos(1,1) |
| 49 | write("Dome Tunnels v"..version.."\n\n") |
| 50 | local def = default_yes and "Y" or "N" |
| 51 | write(string.format("%s (y/n) [default: %s] ", prompt, def)) |
| 52 | local s = read() |
| 53 | s = s and string.lower(s) or "" |
| 54 | if s == "" then return default_yes end |
| 55 | if s == "y" or s == "yes" then return true end |
| 56 | if s == "n" or s == "no" then return false end |
| 57 | write("\nInvalid input. Press Enter to try again.") |
| 58 | read() |
| 59 | end |
| 60 | end |
| 61 | |
| 62 | local function ask_choice(prompt, default_value, choices) |
| 63 | while true do |
| 64 | term.clear() |
| 65 | term.setCursorPos(1,1) |
| 66 | write("Dome Tunnels v"..version.."\n\n") |
| 67 | write(string.format("%s %s [default: %s] ", prompt, table.concat(choices, "/"), tostring(default_value))) |
| 68 | local s = read() |
| 69 | if s == nil or s == "" then return default_value end |
| 70 | s = string.lower(s) |
| 71 | for _, c in ipairs(choices) do |
| 72 | if s == c then return s end |
| 73 | end |
| 74 | write("\nInvalid input. Press Enter to try again.") |
| 75 | read() |
| 76 | end |
| 77 | end |
| 78 | |
| 79 | -- Movement helpers |
| 80 | local function ensure_fuel(threshold) |
| 81 | if turtle.getFuelLevel() == "unlimited" then return end |
| 82 | if turtle.getFuelLevel() >= threshold then return end |
| 83 | for i = 1, 16 do |
| 84 | turtle.select(i) |
| 85 | if turtle.refuel(0) then |
| 86 | while turtle.getFuelLevel() < threshold and turtle.refuel(1) do end |
| 87 | if turtle.getFuelLevel() >= threshold then |
| 88 | return |
| 89 | end |
| 90 | end |
| 91 | end |
| 92 | term.clear() |
| 93 | term.setCursorPos(1,1) |
| 94 | print("Out of fuel. Put fuel in inventory and press Enter.") |
| 95 | read() |
| 96 | return ensure_fuel(threshold) |
| 97 | end |
| 98 | |
| 99 | local function dig_forward() |
| 100 | while turtle.detect() do |
| 101 | if not turtle.dig() then |
| 102 | -- try to attack entities if any |
| 103 | turtle.attack() |
| 104 | sleep(0.2) |
| 105 | end |
| 106 | end |
| 107 | end |
| 108 | |
| 109 | local function dig_upwards() |
| 110 | while turtle.detectUp() do |
| 111 | if not turtle.digUp() then |
| 112 | turtle.attackUp() |
| 113 | sleep(0.2) |
| 114 | end |
| 115 | end |
| 116 | end |
| 117 | |
| 118 | local function dig_downwards() |
| 119 | while turtle.detectDown() do |
| 120 | if not turtle.digDown() then |
| 121 | turtle.attackDown() |
| 122 | sleep(0.2) |
| 123 | end |
| 124 | end |
| 125 | end |
| 126 | |
| 127 | local function safe_forward() |
| 128 | ensure_fuel(100) |
| 129 | while not turtle.forward() do |
| 130 | dig_forward() |
| 131 | -- if still blocked, wait a bit (gravel/sand falling) |
| 132 | sleep(0.05) |
| 133 | end |
| 134 | end |
| 135 | |
| 136 | local function safe_up() |
| 137 | ensure_fuel(100) |
| 138 | while not turtle.up() do |
| 139 | dig_upwards() |
| 140 | sleep(0.05) |
| 141 | end |
| 142 | end |
| 143 | |
| 144 | local function safe_down() |
| 145 | ensure_fuel(100) |
| 146 | while not turtle.down() do |
| 147 | dig_downwards() |
| 148 | sleep(0.05) |
| 149 | end |
| 150 | end |
| 151 | |
| 152 | local function turn_left() |
| 153 | turtle.turnLeft() |
| 154 | end |
| 155 | |
| 156 | local function turn_right() |
| 157 | turtle.turnRight() |
| 158 | end |
| 159 | |
| 160 | -- Lateral move: step right relative to forward |
| 161 | local function step_right() |
| 162 | turn_right() |
| 163 | dig_forward() |
| 164 | safe_forward() |
| 165 | turn_left() |
| 166 | end |
| 167 | |
| 168 | -- Lateral move: step left relative to forward |
| 169 | local function step_left() |
| 170 | turn_left() |
| 171 | dig_forward() |
| 172 | safe_forward() |
| 173 | turn_right() |
| 174 | end |
| 175 | |
| 176 | -- Compute the effective height for a given column x (0-based from left) |
| 177 | local function column_height(x, width, side_h, center_h, radius) |
| 178 | -- If radius == 0, use stepped profile (edges at side_h, inner at center_h) |
| 179 | if (radius or 0) <= 0 then |
| 180 | if x == 0 or x == width - 1 then return side_h else return center_h end |
| 181 | end |
| 182 | -- Rounded dome via cosine interpolation from edge (u=0) to center (u=1) |
| 183 | local u |
| 184 | if width <= 1 then |
| 185 | u = 1 |
| 186 | else |
| 187 | u = 1 - (math.abs(2*x - (width - 1)) / (width - 1)) |
| 188 | end |
| 189 | -- Curvature control: larger radius -> rounder corners (faster rise near edges) |
| 190 | local exponent = 1 / (1 + radius) |
| 191 | local u2 = u ^ exponent |
| 192 | local f = (1 - math.cos(math.pi * u2)) / 2 -- 0 at edges, 1 at center |
| 193 | local h = math.floor(0.5 + (side_h + (center_h - side_h) * f)) |
| 194 | if h < side_h then h = side_h end |
| 195 | if h > center_h then h = center_h end |
| 196 | return h |
| 197 | end |
| 198 | |
| 199 | local function compute_heights(width, side_h, center_h, radius) |
| 200 | local heights = {} |
| 201 | for x = 0, width - 1 do |
| 202 | heights[x+1] = column_height(x, width, side_h, center_h, radius) |
| 203 | end |
| 204 | return heights |
| 205 | end |
| 206 | |
| 207 | -- Carve the slice in front of the turtle for a single forward step |
| 208 | -- starting at bottom edge; if start_at_left is true, begin at leftmost, |
| 209 | -- otherwise begin at rightmost. Uses serpentine per-row to reduce moves. |
| 210 | -- Returns boolean: are we at left edge after finishing the slice? |
| 211 | local function carve_slice(width, side_h, center_h, radius, start_at_left, current_level) |
| 212 | local heights = compute_heights(width, side_h, center_h, radius) |
| 213 | local need_upper = false |
| 214 | for i = 1, #heights do if (heights[i] or 0) >= 4 then need_upper = true break end end |
| 215 | |
| 216 | local function move_to_level(target) |
| 217 | while current_level < target do safe_up(); current_level = current_level + 1 end |
| 218 | while current_level > target do safe_down(); current_level = current_level - 1 end |
| 219 | end |
| 220 | |
| 221 | -- Enter slice once at start, then operate mostly at level 2 |
| 222 | dig_forward(); safe_forward() |
| 223 | if (heights[start_at_left and 1 or width] or 0) >= 2 then |
| 224 | move_to_level(2) |
| 225 | else |
| 226 | current_level = 1 |
| 227 | end |
| 228 | |
| 229 | local end_at_left |
| 230 | if start_at_left then |
| 231 | for x = 1, width do |
| 232 | local h = heights[x] or 0 |
| 233 | if current_level < 2 and h >= 2 then move_to_level(2) end |
| 234 | if current_level > 2 and h < 3 then move_to_level(2) end |
| 235 | if h >= 1 then turtle.digDown() end |
| 236 | if h >= 3 then dig_upwards() end |
| 237 | if x < width then step_right() end |
| 238 | end |
| 239 | end_at_left = false |
| 240 | else |
| 241 | for x = width, 1, -1 do |
| 242 | local h = heights[x] or 0 |
| 243 | if current_level < 2 and h >= 2 then move_to_level(2) end |
| 244 | if current_level > 2 and h < 3 then move_to_level(2) end |
| 245 | if h >= 1 then turtle.digDown() end |
| 246 | if h >= 3 then dig_upwards() end |
| 247 | if x > 1 then step_left() end |
| 248 | end |
| 249 | end_at_left = true |
| 250 | end |
| 251 | |
| 252 | -- Upper pass only if needed (h==4) |
| 253 | if need_upper then |
| 254 | move_to_level(3) |
| 255 | if end_at_left then |
| 256 | for x = 1, width do |
| 257 | if (heights[x] or 0) >= 4 then dig_upwards() end |
| 258 | if x < width then step_right() end |
| 259 | end |
| 260 | end_at_left = false |
| 261 | else |
| 262 | for x = width, 1, -1 do |
| 263 | if (heights[x] or 0) >= 4 then dig_upwards() end |
| 264 | if x > 1 then step_left() end |
| 265 | end |
| 266 | end_at_left = true |
| 267 | end |
| 268 | -- settle to level 2 to start next slice efficiently |
| 269 | move_to_level(2) |
| 270 | end |
| 271 | |
| 272 | return end_at_left, current_level |
| 273 | end |
| 274 | |
| 275 | -- Estimate forward/up/down moves required to carve a slice and advance by one |
| 276 | local function estimate_moves_per_slice(width, side_h, center_h, radius) |
| 277 | local heights = compute_heights(width, side_h, center_h, radius) |
| 278 | local need_upper = false |
| 279 | for i = 1, #heights do if (heights[i] or 0) >= 4 then need_upper = true break end end |
| 280 | local lateral = (width - 1) |
| 281 | local vertical = 1 -- adjust to level 2 |
| 282 | if need_upper then |
| 283 | vertical = vertical + 2 -- up to level 3 and back to 2 |
| 284 | lateral = lateral + (width - 1) |
| 285 | end |
| 286 | local advance = 1 -- entering slice |
| 287 | return vertical + lateral + advance |
| 288 | end |
| 289 | |
| 290 | local function turn_around() |
| 291 | turn_right(); turn_right() |
| 292 | end |
| 293 | |
| 294 | local function move_forward_n(n) |
| 295 | for i = 1, n do |
| 296 | -- path should be clear; still be safe |
| 297 | safe_forward() |
| 298 | end |
| 299 | end |
| 300 | |
| 301 | local function ensure_fuel_or_prompt(threshold) |
| 302 | ensure_fuel(threshold) |
| 303 | if turtle.getFuelLevel() == "unlimited" then return end |
| 304 | if turtle.getFuelLevel() >= threshold then return end |
| 305 | term.clear() |
| 306 | term.setCursorPos(1,1) |
| 307 | print("Fuel still low. Put fuel in inventory and press Enter.") |
| 308 | read() |
| 309 | ensure_fuel(threshold) |
| 310 | end |
| 311 | |
| 312 | local function attempt_return_for_refuel(depth, reserve_needed) |
| 313 | -- Ensure we can get back to start and forth again |
| 314 | local minimal = depth * 2 + 4 |
| 315 | if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() < minimal then |
| 316 | ensure_fuel_or_prompt(minimal) |
| 317 | end |
| 318 | turn_around() |
| 319 | move_forward_n(depth) |
| 320 | term.clear(); term.setCursorPos(1,1) |
| 321 | print("At start. Add fuel now, then press Enter to resume.") |
| 322 | read() |
| 323 | ensure_fuel_or_prompt(reserve_needed) |
| 324 | turn_around() |
| 325 | move_forward_n(depth) |
| 326 | end |
| 327 | |
| 328 | -- Favorites storage helpers |
| 329 | local function favorite_path() |
| 330 | local dir = ".dome_tunnels" |
| 331 | if not fs.exists(dir) then fs.makeDir(dir) end |
| 332 | return fs.combine(dir, "dome_tunnel_favorite") |
| 333 | end |
| 334 | |
| 335 | local function load_favorite() |
| 336 | local path = favorite_path() |
| 337 | -- Backward compatibility: migrate old root-level file if present |
| 338 | if not fs.exists(path) and fs.exists("dome_favorite") then |
| 339 | local hOld = fs.open("dome_favorite", "r") |
| 340 | if hOld then |
| 341 | local d = hOld.readAll(); hOld.close() |
| 342 | local okOld, tblOld = pcall(textutils.unserialize, d) |
| 343 | if okOld and type(tblOld) == "table" then |
| 344 | local hNew = fs.open(path, "w") |
| 345 | if hNew then hNew.write(textutils.serialize(tblOld)); hNew.close() end |
| 346 | -- Optionally remove old file |
| 347 | pcall(fs.delete, "dome_favorite") |
| 348 | end |
| 349 | end |
| 350 | end |
| 351 | if not fs.exists(path) then return nil end |
| 352 | local h = fs.open(path, "r") |
| 353 | if not h then return nil end |
| 354 | local data = h.readAll() |
| 355 | h.close() |
| 356 | local ok, tbl = pcall(textutils.unserialize, data) |
| 357 | if ok and type(tbl) == "table" then return tbl end |
| 358 | return nil |
| 359 | end |
| 360 | |
| 361 | local function save_favorite(fav) |
| 362 | local path = favorite_path() |
| 363 | local h = fs.open(path, "w") |
| 364 | if not h then return end |
| 365 | h.write(textutils.serialize(fav)) |
| 366 | h.close() |
| 367 | end |
| 368 | |
| 369 | -- Inventory helpers |
| 370 | local function count_empty_slots() |
| 371 | local empty = 0 |
| 372 | for i = 1, 16 do |
| 373 | if turtle.getItemCount(i) == 0 then empty = empty + 1 end |
| 374 | end |
| 375 | return empty |
| 376 | end |
| 377 | |
| 378 | local function any_item_matches_slot(slot_idx) |
| 379 | if turtle.getItemCount(slot_idx) == 0 then return false end |
| 380 | for i = 1, 16 do |
| 381 | if i ~= slot_idx and turtle.getItemCount(i) > 0 then |
| 382 | turtle.select(i) |
| 383 | if turtle.compareTo(slot_idx) then return true end |
| 384 | end |
| 385 | end |
| 386 | return false |
| 387 | end |
| 388 | |
| 389 | local function drop_matching_front(slot_idx) |
| 390 | for i = 1, 16 do |
| 391 | if i ~= slot_idx and turtle.getItemCount(i) > 0 then |
| 392 | turtle.select(i) |
| 393 | if turtle.compareTo(slot_idx) then |
| 394 | turtle.drop() |
| 395 | end |
| 396 | end |
| 397 | end |
| 398 | end |
| 399 | |
| 400 | local function turn_to_side(side) |
| 401 | if side == "left" then turn_left() else turn_right() end |
| 402 | end |
| 403 | |
| 404 | local function place_chest_in_wall(chest_slot, side) |
| 405 | if turtle.getItemCount(chest_slot) == 0 then return false end |
| 406 | turn_to_side(side) |
| 407 | dig_forward() |
| 408 | turtle.select(chest_slot) |
| 409 | local ok = turtle.place() |
| 410 | if not ok then |
| 411 | -- try clearing again and placing |
| 412 | dig_forward() |
| 413 | ok = turtle.place() |
| 414 | end |
| 415 | if not ok then |
| 416 | -- fallback: place below |
| 417 | ok = turtle.placeDown() |
| 418 | end |
| 419 | if not ok then |
| 420 | -- failed to place; face forward again |
| 421 | turn_to_side(side == "left" and "right" or "left") |
| 422 | return false |
| 423 | end |
| 424 | -- face back forward |
| 425 | turn_to_side(side == "left" and "right" or "left") |
| 426 | return true |
| 427 | end |
| 428 | |
| 429 | local function deposit_into_front(chest_slot, torch_slot, throw_slot) |
| 430 | for i = 1, 16 do |
| 431 | if i ~= chest_slot and i ~= torch_slot and i ~= throw_slot and turtle.getItemCount(i) > 0 then |
| 432 | turtle.select(i) |
| 433 | if turtle.refuel(0) then |
| 434 | -- skip fuel |
| 435 | else |
| 436 | turtle.drop() |
| 437 | end |
| 438 | end |
| 439 | end |
| 440 | -- handle throw slot: keep one |
| 441 | if throw_slot and throw_slot >= 1 and throw_slot <= 16 then |
| 442 | local cnt = turtle.getItemCount(throw_slot) |
| 443 | if cnt > 1 then |
| 444 | turtle.select(throw_slot) |
| 445 | turtle.drop(cnt - 1) |
| 446 | end |
| 447 | end |
| 448 | end |
| 449 | |
| 450 | local function ensure_inventory_capacity(cfg, at_left_edge) |
| 451 | local empty = count_empty_slots() |
| 452 | if empty > 0 then return end |
| 453 | if cfg.use_throw and turtle.getItemCount(cfg.throw_slot) > 0 and any_item_matches_slot(cfg.throw_slot) then |
| 454 | drop_matching_front(cfg.throw_slot) |
| 455 | if count_empty_slots() > 0 then return end |
| 456 | end |
| 457 | if cfg.use_chests then |
| 458 | local side = at_left_edge and "left" or "right" |
| 459 | if place_chest_in_wall(cfg.chest_slot, side) then |
| 460 | deposit_into_front(cfg.chest_slot, cfg.torch_slot, cfg.throw_slot) |
| 461 | end |
| 462 | end |
| 463 | end |
| 464 | |
| 465 | local function place_torch_if_needed(step_idx, cfg, width, side_h, center_h, radius, at_left_edge) |
| 466 | if not cfg.use_torches then return end |
| 467 | if cfg.torch_spacing <= 0 then return end |
| 468 | if step_idx % cfg.torch_spacing ~= 0 then return end |
| 469 | if turtle.getItemCount(cfg.torch_slot) == 0 then return end |
| 470 | local function place_on_side(side) |
| 471 | local heights = compute_heights(width, side_h, center_h, radius) |
| 472 | local h_edge = heights[(side == "left") and 1 or width] or 1 |
| 473 | local climbed = 0 |
| 474 | if h_edge >= 2 then |
| 475 | safe_up() |
| 476 | climbed = climbed + 1 |
| 477 | end |
| 478 | turn_to_side(side) |
| 479 | turtle.select(cfg.torch_slot) |
| 480 | local ok = turtle.place() |
| 481 | if not ok then |
| 482 | -- fallback to floor torch |
| 483 | turn_to_side(side == "left" and "right" or "left") |
| 484 | for i = 1, climbed do safe_down() end |
| 485 | turtle.select(cfg.torch_slot) |
| 486 | ok = turtle.placeDown() |
| 487 | return |
| 488 | end |
| 489 | turn_to_side(side == "left" and "right" or "left") |
| 490 | for i = 1, climbed do safe_down() end |
| 491 | end |
| 492 | |
| 493 | if cfg.torch_side == "both" then |
| 494 | -- Place on both sides, starting with the current edge for efficiency |
| 495 | if at_left_edge then |
| 496 | place_on_side("left") |
| 497 | place_on_side("right") |
| 498 | else |
| 499 | place_on_side("right") |
| 500 | place_on_side("left") |
| 501 | end |
| 502 | else |
| 503 | -- Single side: honor user's side; if set to left/right, use that side |
| 504 | place_on_side(cfg.torch_side) |
| 505 | end |
| 506 | end |
| 507 | |
| 508 | -- Main |
| 509 | local function main() |
| 510 | term.clear() |
| 511 | term.setCursorPos(1,1) |
| 512 | print("Dome Tunnels v"..version) |
| 513 | print("Place turtle at bottom-left corner, facing forward.") |
| 514 | |
| 515 | local fav = load_favorite() |
| 516 | local use_fav = false |
| 517 | if fav then |
| 518 | use_fav = ask_yes_no("Use favorite saved config?", true) |
| 519 | end |
| 520 | |
| 521 | local width, side_h, center_h, length, radius, auto_return |
| 522 | local use_torches, torch_spacing, torch_side, torch_slot |
| 523 | local use_chests, chest_slot |
| 524 | local use_throw, throw_slot |
| 525 | |
| 526 | if use_fav then |
| 527 | width = fav.width; side_h = fav.side_h; center_h = fav.center_h; length = fav.length; radius = fav.radius |
| 528 | auto_return = fav.auto_return |
| 529 | use_torches = fav.use_torches; torch_spacing = fav.torch_spacing or 9; torch_side = fav.torch_side or "right"; torch_slot = fav.torch_slot or 1 |
| 530 | use_chests = fav.use_chests; chest_slot = fav.chest_slot or 2 |
| 531 | use_throw = fav.use_throw; throw_slot = fav.throw_slot or 4 |
| 532 | else |
| 533 | -- Defaults tailored to the requested shape: 5-wide, sides 3 high, center 4 high |
| 534 | width = ask_number_default("Width (min 2):", 5, 2, 64) |
| 535 | side_h = ask_number_default("Side height (>=1):", 3, 1, 64) |
| 536 | center_h = ask_number_default("Center height (>=1):", 4, 1, 64) |
| 537 | length = ask_number_default("Length (>=1):", 32, 1, 100000) |
| 538 | radius = ask_number_default("Corner radius (0 = stepped, higher = rounder):", 0, 0, 32) |
| 539 | auto_return = ask_yes_no("Auto-return to start to refuel when needed?", true) |
| 540 | use_torches = ask_yes_no("Place torches?", true) |
| 541 | if use_torches then |
| 542 | torch_spacing = ask_number_default("Torch spacing (blocks):", 9, 1, 64) |
| 543 | torch_side = ask_choice("Torch side:", "both", {"left", "right", "both"}) |
| 544 | torch_slot = ask_number_default("Torch slot (1-16):", 1, 1, 16) |
| 545 | else |
| 546 | torch_spacing = 0; torch_side = "right"; torch_slot = 1 |
| 547 | end |
| 548 | use_chests = ask_yes_no("Place chests to dump items when full?", true) |
| 549 | if use_chests then |
| 550 | chest_slot = ask_number_default("Chest slot (1-16):", 2, 1, 16) |
| 551 | else |
| 552 | chest_slot = 2 |
| 553 | end |
| 554 | use_throw = ask_yes_no("Throw items matching a sample when full?", false) |
| 555 | if use_throw then |
| 556 | throw_slot = ask_number_default("Sample slot to throw (1-16):", 4, 1, 16) |
| 557 | else |
| 558 | throw_slot = 4 |
| 559 | end |
| 560 | local savefav = ask_yes_no("Save these settings as favorite?", true) |
| 561 | if savefav then |
| 562 | local tosave = { |
| 563 | width = width, side_h = side_h, center_h = center_h, length = length, radius = radius, |
| 564 | auto_return = auto_return, |
| 565 | use_torches = use_torches, torch_spacing = torch_spacing, torch_side = torch_side, torch_slot = torch_slot, |
| 566 | use_chests = use_chests, chest_slot = chest_slot, |
| 567 | use_throw = use_throw, throw_slot = throw_slot |
| 568 | } |
| 569 | save_favorite(tosave) |
| 570 | end |
| 571 | end |
| 572 | |
| 573 | local cfg = { |
| 574 | use_torches = use_torches, torch_spacing = torch_spacing, torch_side = torch_side, torch_slot = torch_slot, |
| 575 | use_chests = use_chests, chest_slot = chest_slot, |
| 576 | use_throw = use_throw, throw_slot = throw_slot, |
| 577 | auto_return = auto_return |
| 578 | } |
| 579 | |
| 580 | term.clear() |
| 581 | term.setCursorPos(1,1) |
| 582 | print("Dome Tunnels v"..version) |
| 583 | print("Width:\t\t"..width) |
| 584 | print("Side height:\t"..side_h) |
| 585 | print("Center height:\t"..center_h) |
| 586 | print("Length:\t\t"..length) |
| 587 | print("Corner radius:\t"..radius) |
| 588 | print("Auto-return for fuel:\t"..(auto_return and "Yes" or "No")) |
| 589 | print("Torches:\t\t"..(cfg.use_torches and ("Yes (every "..cfg.torch_spacing.." blocks)") or "No")) |
| 590 | if cfg.use_torches then |
| 591 | print("Torch side:\t"..cfg.torch_side.." (top corners; no extra wall digging)") |
| 592 | print("Torch slot:\t"..cfg.torch_slot) |
| 593 | end |
| 594 | print("Chests:\t\t"..(cfg.use_chests and ("Yes (slot "..cfg.chest_slot..")") or "No")) |
| 595 | print("Throw items:\t"..(cfg.use_throw and ("Yes (sample slot "..cfg.throw_slot..")") or "No")) |
| 596 | print("") |
| 597 | print("Put torches in slot "..cfg.torch_slot..", chests in slot "..cfg.chest_slot..", sample item in slot "..cfg.throw_slot.." if throwing is enabled.") |
| 598 | print("") |
| 599 | print("Press Enter to start...") |
| 600 | read() |
| 601 | |
| 602 | local per_slice_moves = estimate_moves_per_slice(width, side_h, center_h, radius) |
| 603 | local depth = 0 |
| 604 | local at_left_edge = true -- start at bottom-left by convention |
| 605 | local current_level = 1 |
| 606 | |
| 607 | -- Carve tunnel |
| 608 | for step = 1, length do |
| 609 | ensure_inventory_capacity(cfg, at_left_edge) |
| 610 | -- Fuel check: we want enough for this slice plus the trip back to start |
| 611 | local reserve_needed = per_slice_moves + depth + 10 |
| 612 | if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() < reserve_needed then |
| 613 | ensure_fuel_or_prompt(reserve_needed) |
| 614 | if turtle.getFuelLevel() < reserve_needed and cfg.auto_return then |
| 615 | attempt_return_for_refuel(depth, reserve_needed) |
| 616 | end |
| 617 | end |
| 618 | |
| 619 | at_left_edge, current_level = carve_slice(width, side_h, center_h, radius, at_left_edge, current_level) |
| 620 | place_torch_if_needed(step, cfg, width, side_h, center_h, radius, at_left_edge) |
| 621 | ensure_inventory_capacity(cfg, at_left_edge) |
| 622 | depth = depth + 1 |
| 623 | end |
| 624 | |
| 625 | term.clear() |
| 626 | term.setCursorPos(1,1) |
| 627 | print("Done. Dome tunnel completed.") |
| 628 | end |
| 629 | |
| 630 | main() |
| 631 | |
| 632 | |
| 633 |