SHOW:
|
|
- or go back to the newest paste.
| 1 | --[[ | |
| 2 | Do whatever you want with the script, but give credit to people who wrote it and keep this license. Simple, right? | |
| 3 | Writers: | |
| 4 | access_denied | |
| 5 | ]] | |
| 6 | ||
| 7 | --[[ | |
| 8 | OreDig version 1.4.1 | |
| 9 | ]] | |
| 10 | ||
| 11 | --[[ | |
| 12 | CONSTANTS | |
| 13 | ]] | |
| 14 | - | BEDROCK_LEVEL = 5 --Level at which the bedrock is |
| 14 | + | BEDROCK_LEVEL = 2 --Level at which the bedrock is |
| 15 | PERSISTANCE_FILE_PATH = "digpersist" | |
| 16 | MOVEMENT_DELAY = 0.0 | |
| 17 | ROTATION_DELAY = 0.0 | |
| 18 | TRY_LIMIT = 10 | |
| 19 | STARTUP_SCRIPT_STRING = [[shell.run("%s", "resume")]]
| |
| 20 | ||
| 21 | DIRS = { --Directions are numbers for easy manipulation.
| |
| 22 | NORTH = 0, | |
| 23 | EAST = 1, | |
| 24 | SOUTH = 2, | |
| 25 | WEST = 3, | |
| 26 | } | |
| 27 | ||
| 28 | TASKS = { --Different task descriptions
| |
| 29 | SHAFT = "digging shaft", | |
| 30 | MOVING_TO_DOCK = "moving to dock", | |
| 31 | MOVING_TO_SHAFT = "moving to shaft", | |
| 32 | REFUELING = "refueling", | |
| 33 | DUMPING = "dumping all items", | |
| 34 | WAITING_FOR_FUEL = "waiting for fuel", | |
| 35 | FINISHED = "finished" | |
| 36 | } | |
| 37 | ||
| 38 | STARTUP_SCRIPT_INSTALL_STATUS = {
| |
| 39 | NOT_INSTALLED = 1, | |
| 40 | DIFFERENT = 2, | |
| 41 | INSTALLED = 0 | |
| 42 | } | |
| 43 | --[[ | |
| 44 | Code from http://wiki.interfaceware.com/112.html | |
| 45 | Allows me to serialize tables. | |
| 46 | ]] | |
| 47 | function SaveTable(Table) | |
| 48 | local savedTables = {} -- used to record tables that have been saved, so that we do not go into an infinite recursion
| |
| 49 | local outFuncs = {
| |
| 50 | ['string'] = function(value) return string.format("%q",value) end;
| |
| 51 | ['boolean'] = function(value) if (value) then return 'true' else return 'false' end end; | |
| 52 | ['number'] = function(value) return string.format('%f',value) end;
| |
| 53 | } | |
| 54 | local outFuncsMeta = {
| |
| 55 | __index = function(t,k) error('Invalid Type For SaveTable: '..k) end
| |
| 56 | } | |
| 57 | setmetatable(outFuncs,outFuncsMeta) | |
| 58 | local tableOut = function(value) | |
| 59 | if (savedTables[value]) then | |
| 60 | error('There is a cyclical reference (table value referencing another table value) in this set.');
| |
| 61 | end | |
| 62 | local outValue = function(value) return outFuncs[type(value)](value) end | |
| 63 | local out = '{'
| |
| 64 | for i,v in pairs(value) do out = out..'['..outValue(i)..']='..outValue(v)..';' end | |
| 65 | savedTables[value] = true; --record that it has already been saved | |
| 66 | return out..'}' | |
| 67 | end | |
| 68 | outFuncs['table'] = tableOut; | |
| 69 | return tableOut(Table); | |
| 70 | end | |
| 71 | ||
| 72 | function LoadTable(Input) | |
| 73 | -- note that this does not enforce anything, for simplicity | |
| 74 | return assert(loadstring('return '..Input))()
| |
| 75 | end | |
| 76 | ||
| 77 | --[[ | |
| 78 | Now my stuff | |
| 79 | ]] | |
| 80 | ||
| 81 | data = { --Just a table that I have created for convenience. It will be written to a file every move. Keeps track of many different variables.
| |
| 82 | lenx = 4, --Width of quarry | |
| 83 | lenz = 4, --Depth of quarry | |
| 84 | height = 6, --Height of quarry | |
| 85 | shaftsDone = 0, --How many shafts are already done | |
| 86 | shaftsToDo = 0, --How many shafts left | |
| 87 | dir = DIRS.NORTH, --Relative direction in which the turtle is currently facing. North will always be the direction in which the turtle was facing when it was started. | |
| 88 | reservedSlots = 2, --How many slots are reserved for items that aren't to be mined and fuel items. | |
| 89 | fuelPerItem = 80, --How much fuel is given per fuel item | |
| 90 | currentlyDoing = TASKS.WAITING_FOR_FUEL, --What task is currently being done | |
| 91 | currentShaftProgress = 0, --How many blocks are done in the current shaft | |
| 92 | coords = {}, --Current coordinates relative to the dock
| |
| 93 | shafts = {}, --Array of shaft coordinates left to do
| |
| 94 | fuelItemsRequired = 0, | |
| 95 | fuelItemsUsed = 0, | |
| 96 | debug = 0, | |
| 97 | shaftsGenerated = false | |
| 98 | } | |
| 99 | data.coords = {x = 0, y = data.height, z = 0} --We start at the dock.
| |
| 100 | --[[ | |
| 101 | Helper methods | |
| 102 | ]] | |
| 103 | function xor(a, b) return (a~=b) and (a or b) end --Fake XOR snipplet | |
| 104 | function cleanup() | |
| 105 | term.clear() | |
| 106 | term.setCursorPos(1, 1) | |
| 107 | end | |
| 108 | --[[ | |
| 109 | Persistance | |
| 110 | ]] | |
| 111 | function dumpToFile() --Dump the state to file | |
| 112 | local file = io.open(PERSISTANCE_FILE_PATH, "w") | |
| 113 | file:write(SaveTable(data)) | |
| 114 | file:close() | |
| 115 | end | |
| 116 | ||
| 117 | function readFromFile() --Read the state from file | |
| 118 | local file = io.open(PERSISTANCE_FILE_PATH, "r") | |
| 119 | if file then | |
| 120 | local tableStr = file:read("*a")
| |
| 121 | data = LoadTable(tableStr) | |
| 122 | file:close() | |
| 123 | end | |
| 124 | end | |
| 125 | ||
| 126 | function periodicSave() | |
| 127 | while 1 do | |
| 128 | dumpToFile() | |
| 129 | sleep(0.5) | |
| 130 | end | |
| 131 | end | |
| 132 | --[[ | |
| 133 | Mining methods | |
| 134 | ]] | |
| 135 | do | |
| 136 | function setStatus(status) | |
| 137 | data.currentlyDoing = status | |
| 138 | end | |
| 139 | local function getRequiredFuelToGoBack(x, y, z) --Not tested, but should return the fuel required to go back to dock | |
| 140 | return data.height - y + x + z + 2 | |
| 141 | end | |
| 142 | local function getRequiredFuelToProcessShaft(x, z) --Calculates how much fuel is required to go to the shaft, process it and come back. | |
| 143 | return (data.height - BEDROCK_LEVEL + x + z + 2) * 2 | |
| 144 | end | |
| 145 | local function hasRequiredFuelToGoBack(x, y, z) | |
| 146 | return getRequiredFuelToGoBack(x, y, z) <= turtle.getFuelLevel() | |
| 147 | end | |
| 148 | local function hasRequiredFuelToProcessShaft(x, z) | |
| 149 | return getRequiredFuelToProcessShaft(x, z) <= turtle.getFuelLevel() | |
| 150 | end | |
| 151 | local function hasRequiredFuelAndSpace(x, y, z) | |
| 152 | return hasRequiredFuelToGoBack(x, y, z) and turtle.getItemCount(16) == 0 -- We need enough fuel to go up after going down and return to the dock. We also need space to put the items into. | |
| 153 | end | |
| 154 | function setDir(todir) --Turn to the required direction optimaly | |
| 155 | if(data.dir == todir) then return end --No need to turn! | |
| 156 | local way = math.abs(data.dir-todir) <= (math.min(data.dir, todir) + 4 - math.max(data.dir,todir)) -- true if we don't need to go "out of bounds", e.g. <-0 or 3-> | |
| 157 | local rotdir = (xor(way, (todir > data.dir))) and -1 or 1 --Direction in which we will spin | |
| 158 | while data.dir ~= todir do | |
| 159 | data.dir = (data.dir + rotdir) % 4 --Enforce 0<=dir<=3 | |
| 160 | dumpToFile() --Each turn, we have to dump to file, otherwise we might have a problem with sync of virtual state and real state of the turtle. | |
| 161 | turtle[rotdir > 0 and "turnRight" or "turnLeft"]() | |
| 162 | end | |
| 163 | sleep(ROTATION_DELAY) | |
| 164 | end | |
| 165 | function dump() --Dump all mined materials | |
| 166 | setStatus(TASKS.DUMPING) | |
| 167 | for i = data.reservedSlots + 2, 16 do --Dump all mined materials | |
| 168 | if turtle.getItemCount(i)~=0 then --DAMN, I NEED MY CONTINUE!!! Well, we don't want to waste time on empty slots... | |
| 169 | turtle.select(i) | |
| 170 | turtle.drop(turtle.getItemCount(i)) | |
| 171 | end | |
| 172 | end | |
| 173 | end | |
| 174 | function refuelAndDump(x, z) --Refuel and dump | |
| 175 | setDir(DIRS.SOUTH) | |
| 176 | setStatus(TASKS.REFUELING) | |
| 177 | turtle.select(1) | |
| 178 | if not hasRequiredFuelToProcessShaft(x, z) and turtle.getItemCount(1)>1 then --Use coal that we've mined | |
| 179 | turtle.refuel(math.min(turtle.getItemCount(1)-1, math.ceil((getRequiredFuelToProcessShaft(x, z)-turtle.getFuelLevel()) / data.fuelPerItem))) | |
| 180 | end | |
| 181 | while not hasRequiredFuelToProcessShaft(x, z) do | |
| 182 | turtle.select(1) | |
| 183 | data.fuelItemsRequired = math.ceil((getRequiredFuelToProcessShaft(x, z) - turtle.getFuelLevel()) / data.fuelPerItem) --We require this much fuel items | |
| 184 | data.debug = getRequiredFuelToProcessShaft(x, z).." "..turtle.getFuelLevel() | |
| 185 | turtle.suckUp() | |
| 186 | local willConsume = math.min(turtle.getItemCount(1)-1, data.fuelItemsRequired) | |
| 187 | if willConsume > 0 and turtle.refuel(willConsume) then --Successfully consumed | |
| 188 | setStatus(TASKS.REFUELING) | |
| 189 | data.fuelItemsUsed = data.fuelItemsUsed + willConsume | |
| 190 | else --Could not consume | |
| 191 | setStatus(TASKS.WAITING_FOR_FUEL) | |
| 192 | sleep(5) | |
| 193 | end | |
| 194 | end | |
| 195 | data.fuelItemsRequired = 0 | |
| 196 | if turtle.getItemCount(1)>1 then turtle.dropUp(turtle.getItemCount(1)-1) end --Drop off the fuel we didn't use | |
| 197 | dump() | |
| 198 | end | |
| 199 | local function tryEmptyChest(postfix) | |
| 200 | while (turtle.select(1) or true) and turtle["suck"..postfix]() do | |
| 201 | checkShaftStep() | |
| 202 | end | |
| 203 | end | |
| 204 | local function move(forward, postfix, add, remove, dir) | |
| 205 | local tryCount = 0 | |
| 206 | while turtle["detect"..postfix]() do | |
| 207 | tryEmptyChest(postfix) | |
| 208 | if not turtle["dig"..postfix]() then tryCount = tryCount + 1 end | |
| 209 | if tryCount>TRY_LIMIT then return false end | |
| 210 | sleep(0.2) | |
| 211 | end | |
| 212 | while (add(dir) or true) and not turtle[forward]() do | |
| 213 | remove(dir) | |
| 214 | if not turtle["attack"..postfix]() then tryCount = tryCount + 1 end | |
| 215 | if tryCount>TRY_LIMIT then return false end | |
| 216 | sleep(0.2) | |
| 217 | end | |
| 218 | return true | |
| 219 | end | |
| 220 | function moveX(dir) --X+ = E, X- = W | |
| 221 | setDir(DIRS.WEST - (dir + 1)) --Direction arithmetic magic :D | |
| 222 | local add = function (dir) | |
| 223 | data.coords.x = data.coords.x + dir | |
| 224 | dumpToFile() | |
| 225 | return true | |
| 226 | end | |
| 227 | local remove = function (dir) | |
| 228 | data.coords.x = data.coords.x - dir | |
| 229 | dumpToFile() | |
| 230 | return true | |
| 231 | end | |
| 232 | if not move("forward", "", add, remove, dir) then return false end
| |
| 233 | sleep(MOVEMENT_DELAY) | |
| 234 | return true | |
| 235 | end | |
| 236 | function moveY(dir) --Y+ = U, Y- = D | |
| 237 | local add = function (dir) | |
| 238 | data.coords.y = data.coords.y + dir | |
| 239 | dumpToFile() | |
| 240 | return true | |
| 241 | end | |
| 242 | local remove = function (dir) | |
| 243 | data.coords.y = data.coords.y - dir | |
| 244 | dumpToFile() | |
| 245 | return true | |
| 246 | end | |
| 247 | if not move(dir > 0 and "up" or "down", dir > 0 and "Up" or "Down", add, remove, dir) then return false end | |
| 248 | dumpToFile() | |
| 249 | sleep(MOVEMENT_DELAY) | |
| 250 | return true | |
| 251 | end | |
| 252 | function moveZ(dir) --Z+ = N, Z- = S | |
| 253 | setDir(DIRS.NORTH - (dir - 1)) --Direction arithmetic magic :D | |
| 254 | local add = function (dir) | |
| 255 | data.coords.z = data.coords.z + dir | |
| 256 | dumpToFile() | |
| 257 | return true | |
| 258 | end | |
| 259 | local remove = function (dir) | |
| 260 | data.coords.z = data.coords.z - dir | |
| 261 | dumpToFile() | |
| 262 | return true | |
| 263 | end | |
| 264 | if not move("forward", "", add, remove, dir) then return false end
| |
| 265 | dumpToFile() | |
| 266 | sleep(MOVEMENT_DELAY) | |
| 267 | return true | |
| 268 | end | |
| 269 | function dock(x, z) --Go back to dock | |
| 270 | setStatus(TASKS.MOVING_TO_DOCK) | |
| 271 | moveVerticalTo(data.height) | |
| 272 | moveHorizontalTo(0, 0) | |
| 273 | refuelAndDump(x, z) | |
| 274 | end | |
| 275 | function moveHorizontalTo(x, z) | |
| 276 | while data.coords.x ~= x do while not moveX(x-data.coords.x>0 and 1 or -1) do sleep(0.2) end end | |
| 277 | while data.coords.z ~= z do while not moveZ(z-data.coords.z>0 and 1 or -1) do sleep(0.2) end end | |
| 278 | end | |
| 279 | function moveVerticalTo(y) | |
| 280 | while data.coords.y ~= y do while not moveY(y-data.coords.y>0 and 1 or -1) do end end | |
| 281 | end | |
| 282 | function processBlockInFront() --Determine if we are interested in the block in front. If we are, then mine it. | |
| 283 | local flag = true | |
| 284 | for i = 2, data.reservedSlots + 1 do | |
| 285 | turtle.select(i) | |
| 286 | flag = flag and not turtle.compare(i) | |
| 287 | end | |
| 288 | turtle.select(1) | |
| 289 | if flag then | |
| 290 | tryEmptyChest("")
| |
| 291 | turtle.dig() | |
| 292 | end | |
| 293 | end | |
| 294 | function checkShaftStep() | |
| 295 | --The next if should never happen, but whatever, let it live, might come in useful later on. | |
| 296 | if not hasRequiredFuelToGoBack(data.coords.x, data.coords.y, data.coords.z) and turtle.getItemCount(1)>1 then --Use coal that we've mined to refuel to cut down on docking time | |
| 297 | setStatus(TASKS.REFUELING) | |
| 298 | turtle.select(1) | |
| 299 | local consumeItems = math.ceil((getRequiredFuelToGoBack(data.coords.x, data.coords.y, data.coords.z) - turtle.getFuelLevel()) / data.fuelPerItem) | |
| 300 | turtle.refuel(math.min(turtle.getItemCount(1)-1, consumeItems)) | |
| 301 | end | |
| 302 | if not hasRequiredFuelAndSpace(data.coords.x, data.coords.y, data.coords.z) then --If we don't have enough coal to refuel or we don't have enough space, we need to dock and come back to the spot | |
| 303 | local backupDir = data.dir | |
| 304 | local backupX = data.coords.x | |
| 305 | local backupY = data.coords.y | |
| 306 | local backupZ = data.coords.z | |
| 307 | dock(data.coords.x, data.coords.z) | |
| 308 | setStatus(TASKS.MOVING_TO_SHAFT) | |
| 309 | moveHorizontalTo(backupX, backupZ) | |
| 310 | moveVerticalTo(backupY) | |
| 311 | setDir(backupDir) | |
| 312 | end | |
| 313 | end | |
| 314 | function processShaft(x, z) | |
| 315 | dock(x, z) | |
| 316 | setStatus(TASKS.MOVING_TO_SHAFT) | |
| 317 | moveHorizontalTo(x, z) | |
| 318 | data.currentShaftProgress = 0 | |
| 319 | while data.coords.y > BEDROCK_LEVEL + 1 do | |
| 320 | --Digging the shaft | |
| 321 | setStatus(TASKS.SHAFT) | |
| 322 | checkShaftStep() | |
| 323 | turtle.select(1) | |
| 324 | if not moveY(-1) then return end --We hit bedrock, nothing to do here... | |
| 325 | for i = 0, 3 do | |
| 326 | checkShaftStep() | |
| 327 | turtle.select(1) | |
| 328 | processBlockInFront() | |
| 329 | setDir((data.dir + (i~=3 and 1 or 0)) % 4) | |
| 330 | end | |
| 331 | data.currentShaftProgress = data.currentShaftProgress + 1 | |
| 332 | end | |
| 333 | data.shaftsDone = data.shaftsDone + 1 | |
| 334 | end | |
| 335 | function excavate() | |
| 336 | if not data.shaftsGenerated then | |
| 337 | for x = 0, data.lenx - 1 do | |
| 338 | for z = 0, data.lenz - 1 do | |
| 339 | if (z-(x*2))%5==0 then table.insert(data.shafts, {x = x, z = z + 1}) end
| |
| 340 | end | |
| 341 | end | |
| 342 | data.shaftsToDo = #data.shafts | |
| 343 | data.shaftsGenerated = true | |
| 344 | end | |
| 345 | while #data.shafts > 0 do | |
| 346 | local shaft = data.shafts[1] | |
| 347 | processShaft(shaft.x, shaft.z) | |
| 348 | table.remove(data.shafts, 1) | |
| 349 | end | |
| 350 | moveVerticalTo(data.height) | |
| 351 | moveHorizontalTo(0, 0) | |
| 352 | dump() | |
| 353 | setDir(DIRS.NORTH) | |
| 354 | setStatus(TASKS.FINISHED) | |
| 355 | fs.delete(PERSISTANCE_FILE_PATH) | |
| 356 | end | |
| 357 | end | |
| 358 | --[[ | |
| 359 | Check if the startup script is right | |
| 360 | ]] | |
| 361 | function getStartupScriptStatus() | |
| 362 | if not fs.exists("startup") then return STARTUP_SCRIPT_INSTALL_STATUS.NOT_INSTALLED end
| |
| 363 | local file = io.open("startup", "r")
| |
| 364 | if not file then return STARTUP_SCRIPT_INSTALL_STATUS.NOT_INSTALLED end | |
| 365 | local str = file:read("*a")
| |
| 366 | file:close() | |
| 367 | return str == string.format(STARTUP_SCRIPT_STRING, shell.getRunningProgram()) and STARTUP_SCRIPT_INSTALL_STATUS.INSTALLED or STARTUP_SCRIPT_INSTALL_STATUS.DIFFERENT | |
| 368 | end | |
| 369 | ||
| 370 | function installStartupScript() | |
| 371 | fs.delete("startup")
| |
| 372 | local file = io.open("startup", "w")
| |
| 373 | file:write(string.format(STARTUP_SCRIPT_STRING, shell.getRunningProgram())) | |
| 374 | file:close() | |
| 375 | end | |
| 376 | ||
| 377 | --[[ | |
| 378 | GUI | |
| 379 | ]] | |
| 380 | ||
| 381 | function splitIntoLines(str) | |
| 382 | local w, h = term.getSize() | |
| 383 | local lines = {}
| |
| 384 | for line in str:gmatch("[^\r\n]+") do
| |
| 385 | local currentLine = "" | |
| 386 | for token in line:gmatch("[^%s]+") do
| |
| 387 | if #(currentLine .. token) > w-3 then | |
| 388 | table.insert(lines, currentLine) | |
| 389 | currentLine = token | |
| 390 | else | |
| 391 | currentLine = (currentLine~="" and currentLine .. " " or "") .. token | |
| 392 | end | |
| 393 | end | |
| 394 | if currentLine~="" then table.insert(lines, currentLine) end | |
| 395 | end | |
| 396 | return lines | |
| 397 | end | |
| 398 | ||
| 399 | function showProgress(finished) --Progress | |
| 400 | local progressBarSpinChars = {"/", "-", "\\", "|"}
| |
| 401 | local curChar = 0 | |
| 402 | local cur = 1 | |
| 403 | local w, h = term.getSize() | |
| 404 | local exit = false | |
| 405 | local running = true | |
| 406 | local function cycleProgressBar() | |
| 407 | curChar = (curChar + 1) % 4 --Cycle through the progress bar chars for an animation effect | |
| 408 | end | |
| 409 | ||
| 410 | local function clear() | |
| 411 | term.clear() | |
| 412 | cur = 0 | |
| 413 | end | |
| 414 | ||
| 415 | local function allocateLine() | |
| 416 | cur = cur + 1 | |
| 417 | end | |
| 418 | ||
| 419 | local function renderProgressBar(fraction) | |
| 420 | allocateLine() | |
| 421 | term.setCursorPos(1, cur) | |
| 422 | local numOfDone = math.floor((w-3)*fraction) | |
| 423 | local numOfLeft = math.ceil((w-3)*(1-fraction)) | |
| 424 | term.write("[")
| |
| 425 | if numOfDone>0 then term.write(string.rep("=", numOfDone)) end
| |
| 426 | term.write(fraction == 1 and "=" or progressBarSpinChars[curChar + 1]) | |
| 427 | if numOfLeft>0 then term.write(string.rep(" ", numOfLeft)) end
| |
| 428 | term.write("]")
| |
| 429 | end | |
| 430 | ||
| 431 | local function writeLine(desc, value) | |
| 432 | if type(value) ~= "string" then value = tostring(value) end | |
| 433 | allocateLine() | |
| 434 | term.setCursorPos(1, cur) | |
| 435 | term.write(desc .. (value~="" and ":" or "")) | |
| 436 | term.setCursorPos(w - #value + 1, cur) | |
| 437 | term.write(value) | |
| 438 | end | |
| 439 | ||
| 440 | local function normalProgressRenderer() | |
| 441 | writeLine("Current task", data.currentlyDoing)
| |
| 442 | writeLine("Shafts done", data.shaftsDone)
| |
| 443 | writeLine("Total shafts", data.shaftsToDo)
| |
| 444 | local localProgress = data.currentShaftProgress / (data.height - BEDROCK_LEVEL - 1) | |
| 445 | if localProgress==0/0 then localProgress = 0 end | |
| 446 | writeLine("Current shaft progress", math.floor(localProgress*100).."%")
| |
| 447 | renderProgressBar(localProgress) | |
| 448 | local totalProgress = (data.shaftsDone + localProgress % 1)/data.shaftsToDo | |
| 449 | if totalProgress==0/0 then totalProgress = 0 end | |
| 450 | writeLine("Whole progress", math.floor(totalProgress*100).."%")
| |
| 451 | renderProgressBar(totalProgress) | |
| 452 | writeLine("Coordinates", data.coords.x..", "..data.coords.y..", "..data.coords.z)
| |
| 453 | writeLine("Fuel used so far", data.fuelItemsUsed)
| |
| 454 | if data.currentlyDoing == TASKS.WAITING_FOR_FUEL then writeLine("Fuel items needed", data.fuelItemsRequired)
| |
| 455 | else writeLine("Sufficient fuel for now.", "") cycleProgressBar() end
| |
| 456 | term.setCursorPos(1, h) term.write("Press Enter to exit...")
| |
| 457 | end | |
| 458 | ||
| 459 | local function finishedProgressRenderer() | |
| 460 | writeLine("Finished.", "")
| |
| 461 | writeLine("Shafts done", data.shaftsDone)
| |
| 462 | writeLine("Fuel used", data.fuelItemsUsed)
| |
| 463 | term.setCursorPos(1, 4) | |
| 464 | end | |
| 465 | ||
| 466 | local function renderProgress() | |
| 467 | clear() | |
| 468 | if finished then | |
| 469 | finishedProgressRenderer() | |
| 470 | else | |
| 471 | normalProgressRenderer() | |
| 472 | end | |
| 473 | end | |
| 474 | ||
| 475 | local function displayProgress() | |
| 476 | while data.currentlyDoing~=TASKS.FINISHED and running do | |
| 477 | --Update information | |
| 478 | renderProgress(false) | |
| 479 | sleep(0.1) | |
| 480 | end | |
| 481 | running = false | |
| 482 | end | |
| 483 | ||
| 484 | local function waitForKey() | |
| 485 | while running do | |
| 486 | local sEvent, param = os.pullEvent("key")
| |
| 487 | if sEvent == "key" then | |
| 488 | if param == 28 then | |
| 489 | exit = true | |
| 490 | running = false | |
| 491 | dumpToFile() | |
| 492 | end | |
| 493 | end | |
| 494 | end | |
| 495 | end | |
| 496 | if finished then renderProgress() else parallel.waitForAny(function() parallel.waitForAll(displayProgress, waitForKey) end, excavate, periodicSave) cleanup() end | |
| 497 | return exit | |
| 498 | end | |
| 499 | function showConfig() --Config | |
| 500 | local currentlySelected = 1 | |
| 501 | local running = true | |
| 502 | local exit = false | |
| 503 | local configOptions = setmetatable({
| |
| 504 | {key = "Width", keyBlank = "Width (required!)", value = "8", varType = "number", transferName = "lenx"},
| |
| 505 | {key = "Length", keyBlank = "Length (required!)", value = "8", varType = "number", transferName = "lenz"},
| |
| 506 | {key = "Height", keyBlank = "Height (required!)", value = "8", varType = "number", transferName = "height"},
| |
| 507 | {key = "Number of excluded blocks", keyBlank = "Number of excluded blocks (required!)", value = "2", varType = "number", transferName = "reservedSlots"},
| |
| 508 | {key = "Fuel units per item", keyBlank = "Fuel units per item (required!)", value = "80", varType = "number", transferName = "fuelPerItem"}--[[, TODO
| |
| 509 | {key = "Save config to", keyBlank = "Save config to (optional)", value = "", varType = "string", transferName = nil} ]]
| |
| 510 | }, | |
| 511 | {
| |
| 512 | __concat = function(t, s) | |
| 513 | t[currentlySelected].value = t[currentlySelected].value .. (t[currentlySelected].varType=="number" and (tonumber(s)~=nil and s or "") or s) | |
| 514 | end, | |
| 515 | __sub = function(t, s) | |
| 516 | t[currentlySelected].value = t[currentlySelected].value:sub(1, #t[currentlySelected].value - 1) | |
| 517 | end, | |
| 518 | __index = function(t, key) | |
| 519 | if tonumber(key)~=nil then | |
| 520 | return t[key] | |
| 521 | else | |
| 522 | for i = 1, #t do | |
| 523 | if t[i].transferName == key then return t[i] end | |
| 524 | end | |
| 525 | end | |
| 526 | return nil | |
| 527 | end | |
| 528 | } | |
| 529 | ) | |
| 530 | ||
| 531 | local function renderConfig() | |
| 532 | term.clear() | |
| 533 | local w, h = term.getSize() | |
| 534 | for i = 1, #configOptions do | |
| 535 | term.setCursorPos(1, i) | |
| 536 | term.write(#configOptions[i].value>0 and configOptions[i].key or configOptions[i].keyBlank) | |
| 537 | term.setCursorPos(w-1-#configOptions[i].value, i) | |
| 538 | term.write(i==currentlySelected and "[" or " ") | |
| 539 | term.write(configOptions[i].value) | |
| 540 | term.write(i==currentlySelected and "]" or " ") | |
| 541 | end | |
| 542 | term.setCursorPos(1, h-1) | |
| 543 | term.write((currentlySelected == (#configOptions + 1)) and "[Next]" or " Next ") | |
| 544 | term.setCursorPos(w-5, h-1) | |
| 545 | term.write((currentlySelected == (#configOptions + 2)) and "[Exit]" or " Exit ") | |
| 546 | term.setCursorPos(w-2, currentlySelected) | |
| 547 | end | |
| 548 | ||
| 549 | local function showConfig() | |
| 550 | while running do renderConfig() sleep(0.2) end | |
| 551 | end | |
| 552 | ||
| 553 | local function transferValues() | |
| 554 | for i = 1, #configOptions do if configOptions[i].transferName ~= nil then | |
| 555 | data[configOptions[i].transferName] = (configOptions[i].varType=="number" and tonumber or function(a) return a end)(configOptions[i].value) | |
| 556 | end end | |
| 557 | data.coords = {x = 0, y = data.height, z = 0} --We start at the dock.
| |
| 558 | end | |
| 559 | local function handleInputForConfig() | |
| 560 | while running do | |
| 561 | local event, param = os.pullEvent() | |
| 562 | if event == "key" then | |
| 563 | if param == 200 then | |
| 564 | currentlySelected = math.max(1, currentlySelected - 1) | |
| 565 | renderConfig() | |
| 566 | elseif param == 208 then | |
| 567 | currentlySelected = math.min(#configOptions + 2, currentlySelected + 1) | |
| 568 | renderConfig() | |
| 569 | elseif param == 14 then | |
| 570 | local t = configOptions - nil | |
| 571 | renderConfig() | |
| 572 | elseif param == 28 then | |
| 573 | if currentlySelected == #configOptions + 2 then | |
| 574 | exit = true | |
| 575 | running = false | |
| 576 | elseif currentlySelected == #configOptions + 1 then | |
| 577 | transferValues() | |
| 578 | running = false | |
| 579 | term.clear() | |
| 580 | end | |
| 581 | end | |
| 582 | elseif event == "char" then | |
| 583 | local t = configOptions .. param | |
| 584 | renderConfig() | |
| 585 | end | |
| 586 | end | |
| 587 | end | |
| 588 | parallel.waitForAll(handleInputForConfig, showConfig) | |
| 589 | cleanup() | |
| 590 | return exit | |
| 591 | end | |
| 592 | function showDialog(dialogStr, buttons, centre) --Load from old config or not | |
| 593 | local currentlySelected = 1 | |
| 594 | local running = true | |
| 595 | local result = false | |
| 596 | --local dialogStr = "Persistance file from previous run found. Would you like to continue from where you left off?" | |
| 597 | local lines = {}
| |
| 598 | local w, h = term.getSize() | |
| 599 | for line in dialogStr:gmatch("[^\r\n]+") do
| |
| 600 | local currentLine = "" | |
| 601 | for token in line:gmatch("[^%s]+") do
| |
| 602 | if #(currentLine .. token) > w-3 then | |
| 603 | table.insert(lines, currentLine) | |
| 604 | currentLine = token | |
| 605 | else | |
| 606 | currentLine = (currentLine~="" and currentLine .. " " or "") .. token | |
| 607 | end | |
| 608 | end | |
| 609 | if currentLine~="" then table.insert(lines, currentLine) end | |
| 610 | end | |
| 611 | local function renderDialog() | |
| 612 | term.clear() | |
| 613 | for i = 1, #lines do | |
| 614 | term.setCursorPos(centre and ((w - #lines[i]) / 2 + 1) or 1, i + 1) | |
| 615 | term.write(lines[i]) | |
| 616 | end | |
| 617 | for i = 1, #buttons do | |
| 618 | term.setCursorPos((w / (#buttons + 1)) * i - #buttons[i]/2, h - 1) | |
| 619 | term.write((currentlySelected == i) and ("[" .. buttons[i] .. "]") or (" " .. buttons[i] .. " "))
| |
| 620 | end | |
| 621 | end | |
| 622 | ||
| 623 | local function showDialog() | |
| 624 | while running do renderDialog() sleep(0.2) end | |
| 625 | end | |
| 626 | ||
| 627 | local function handleInputForDialog() | |
| 628 | while running do | |
| 629 | local event, param = os.pullEvent() | |
| 630 | if event == "key" then | |
| 631 | if param == 200 then | |
| 632 | currentlySelected = math.max(1, currentlySelected - 1) | |
| 633 | renderDialog() | |
| 634 | elseif param == 208 then | |
| 635 | currentlySelected = math.min(#buttons, currentlySelected + 1) | |
| 636 | renderDialog() | |
| 637 | elseif param == 28 then | |
| 638 | running = false | |
| 639 | end | |
| 640 | end | |
| 641 | end | |
| 642 | end | |
| 643 | parallel.waitForAll(handleInputForDialog, showDialog) | |
| 644 | cleanup() | |
| 645 | return currentlySelected | |
| 646 | end | |
| 647 | ||
| 648 | function showResumeWarning() | |
| 649 | ||
| 650 | local running = true | |
| 651 | local exit = false | |
| 652 | local function displayWarning() | |
| 653 | for i = 10, 0, -1 do | |
| 654 | term.clear() | |
| 655 | local lines = splitIntoLines(string.format( | |
| 656 | [[The turtle will continue to dig shafts in %i seconds. | |
| 657 | Press Enter to exit to shell.]], i)) | |
| 658 | for i = 1, #lines do | |
| 659 | term.setCursorPos(1, i) | |
| 660 | term.write(lines[i]) | |
| 661 | end | |
| 662 | sleep(1) | |
| 663 | if not running then return end | |
| 664 | end | |
| 665 | running = false | |
| 666 | end | |
| 667 | local function waitForKey() | |
| 668 | while running do | |
| 669 | local sEvent, param = os.pullEvent("key")
| |
| 670 | if sEvent == "key" then | |
| 671 | if param == 28 then | |
| 672 | exit = true | |
| 673 | running = false | |
| 674 | dumpToFile() | |
| 675 | end | |
| 676 | end | |
| 677 | end | |
| 678 | end | |
| 679 | parallel.waitForAny(waitForKey, displayWarning) | |
| 680 | return exit | |
| 681 | end | |
| 682 | --[[ | |
| 683 | Entry | |
| 684 | ]] | |
| 685 | args={...}
| |
| 686 | ||
| 687 | if args[1] == "resume" then | |
| 688 | if fs.exists(PERSISTANCE_FILE_PATH) then | |
| 689 | readFromFile() | |
| 690 | --[[print("The turtle will automatically resume in 10 seconds.")
| |
| 691 | print("Press Enter to abort.")
| |
| 692 | parallel.waitForAny(function() sleep(10) running = false end, | |
| 693 | function() | |
| 694 | while running do | |
| 695 | local sEvent, param = os.pullEvent("key")
| |
| 696 | if sEvent == "key" then | |
| 697 | if param == 28 then | |
| 698 | exit = true | |
| 699 | running = false | |
| 700 | end | |
| 701 | end | |
| 702 | end | |
| 703 | end)]] | |
| 704 | if showResumeWarning() then | |
| 705 | cleanup() | |
| 706 | return | |
| 707 | end | |
| 708 | if showProgress(false) then | |
| 709 | cleanup() | |
| 710 | return | |
| 711 | end | |
| 712 | showProgress(true) | |
| 713 | end | |
| 714 | cleanup() | |
| 715 | return | |
| 716 | end | |
| 717 | local startupStatus = getStartupScriptStatus() | |
| 718 | if startupStatus ~= STARTUP_SCRIPT_INSTALL_STATUS.INSTALLED then | |
| 719 | local variations = {"not installed", "different from what this program suggests/needs"}
| |
| 720 | local dialogString = string.format( | |
| 721 | [[The startup script is %s. | |
| 722 | This means that the turtle will not automatically resume when it boots up (e.g. chunk loads). | |
| 723 | Would you like to install the recommended startup script?]], variations[startupStatus]) | |
| 724 | local startupDialogResult = showDialog(dialogString, {"Yes", "No"}, true)
| |
| 725 | if startupDialogResult == 1 then | |
| 726 | installStartupScript() | |
| 727 | end | |
| 728 | end | |
| 729 | local loadFromFile = false | |
| 730 | if fs.exists(PERSISTANCE_FILE_PATH) then | |
| 731 | loadFromFile = showDialog("Persistance file from previous run found. Would you like to continue from where you left off?", {"Yes", "No"}, true) == 1
| |
| 732 | end | |
| 733 | if loadFromFile then | |
| 734 | readFromFile() | |
| 735 | local dirNames = {"north", "east", "south", "west"}
| |
| 736 | local dialogString = string.format( | |
| 737 | [[Persistance file loaded. | |
| 738 | The turtle thinks it is located at: | |
| 739 | Y=%i; relative X,Z (to dock)=%i,%i; facing %s | |
| 740 | Options are: | |
| 741 | 1. The turtle is facing and is located as stated above. | |
| 742 | 2. The turtle is located at the dock, facing away from the output chest. | |
| 743 | ||
| 744 | ]], | |
| 745 | data.coords.y, data.coords.x, data.coords.z, dirNames[data.dir + 1]) | |
| 746 | local posDialogResult = showDialog(dialogString, {"1", "2", "Exit"}, false)
| |
| 747 | if posDialogResult == 2 then | |
| 748 | data.coords = {x = 0, y = data.height, z = 0}
| |
| 749 | data.dir = DIRS.NORTH | |
| 750 | elseif posDialogResult == 3 then | |
| 751 | cleanup() | |
| 752 | return | |
| 753 | end | |
| 754 | else | |
| 755 | if showConfig() then | |
| 756 | cleanup() | |
| 757 | return | |
| 758 | end | |
| 759 | end | |
| 760 | local warningStr = string.format("Before continuing, please make sure that the first slot is occupied by the fuel item, and the next %i slots are occupied by the blocks that shouldn't be mined.", data.reservedSlots)
| |
| 761 | if showDialog(warningStr, {"Continue", "Exit"}, true) == 2 then
| |
| 762 | cleanup() | |
| 763 | return | |
| 764 | end | |
| 765 | if showProgress(false) then | |
| 766 | cleanup() | |
| 767 | return | |
| 768 | end | |
| 769 | showProgress(true) |