View difference between Paste ID: gJzyhYpj and vmFJPGdj
SHOW: | | - or go back to the newest paste.
1
--Civilwargeeky's Quarry Program--
2
  VERSION = "3.6.2"
3
--[[ 
4
Recent Changes:
5
  New Ore Quarry System
6
  Added New Rednet Support
7
  Quarry no longer goes to start at end of row!
8
  Turtle can go left!
9
  New Arguments!
10
  -flatBedrock [t/f]: The turtle will initially dig down to bedrock (or possibly a mob) and set startDown from that
11
  -left [t/f]: The turtle will quarry to the left instead of the right
12
  -maxFuel [number]: The number the turtle will stop fueling at. Basically just sets checkFuelLimit to this number
13
  -fuelChest [nothing or slot number]: Prompts for a chest with fuel it. This will be used if the turtle runs out of fuel. Recommended with maxfuel and doCheckFuel false
14
]]
15
--Defining things
16
civilTable = nil; _G.civilTable = {}; setmetatable(civilTable, {__index = getfenv()}); setfenv(1,civilTable)
17
originalDay = os.day() --Used in logging
18
numResumed = 0 --Number of times turtle has been resumed
19
-------Defaults for Arguments----------
20
--Arguments assignable by text
21
x,y,z = 3,3,3 --These are just in case tonumber fails
22
inverted = false --False goes from top down, true goes from bottom up [Default false] 
23
rednetEnabled = false --Default rednet on or off  [Default false]
24
--Arguments assignable by tArgs
25
dropSide = "front" --Side it will eject to when full or done [Default "front"]
26
careAboutResources = true --Will not stop mining once inventory full if false [Default true]
27
doCheckFuel = true --Perform fuel check [Default true]
28
doRefuel = false --Whenever it comes to start location will attempt to refuel from inventory [Default false]
29
keepOpen = 1 --How many inventory slots it will attempt to keep open at all times [Default 1]
30
fuelSafety = "moderate" --How much fuel it will ask for: safe, moderate, and loose [Default moderate]
31
excessFuelAmount = math.huge --How much fuel the turtle will get maximum. Limited by turtle.getFuelLimit in recent CC [Default math.huge]
32
saveFile = "Civil_Quarry_Restore" --Where it saves restore data [Default "Civil_Quarry_Restore"]
33
autoResume = true --If true, turtle will auto-restart when loaded. [Default true]
34
startupRename = "oldStartup.quarry" --What the startup is temporarily renamed to [Default "oldStartup.quarry"]
35
startupName = "startup" --What the turtle auto-resumes with [Default "startup"]
36
doBackup = true --If it will keep backups for session persistence [Default true]
37
uniqueExtras = 8 --How many different items (besides cobble) the turtle expects. [Default 8]
38
maxTries = 50 --How many times turtle will try to dig a block before it "counts" bedrock [Default 50]
39
gpsEnabled = false -- If option is enabled, will attempt to find position via GPS api [Default false]
40
gpsTimeout = 3 --The number of seconds the program will wait to get GPS coords. Not in arguments [Default 3]
41
legacyRednet = false --Use this if playing 1.4.7
42
logging = true --Whether or not the turtle will log mining runs. [Default ...still deciding]
43
logFolder = "Quarry_Logs" --What folder the turtle will store logs in [Default "Quarry_Logs"]
44
logExtension = "" --The extension of the file (e.g. ".txt") [Default ""]
45
flatBedrock = false --If true, will go down to bedrock to set startDown [Default false]
46
startDown = 0 --How many blocks to start down from the top of the mine [Default 0]
47
enderChestEnabled = false --Whether or not to use an ender chest [Default false]
48
enderChestSlot = 16 --What slot to put the ender chest in [Default 16]
49
fuelChestEnabled = false --Whether or not to use a fuel chest [Default false]
50
fuelChestSlot = 15 --What slot to put the fuel chest in [Default 15]
51
goLeftNotRight = false --Quarry to left, not right (parameter is "left") [Default false]
52
oreQuarry = false --Enables ore quarry functionality [Default false]
53
oreQuarryBlacklistName = "oreQuarryBlacklist.txt" --This is the file that will be parsed for item names [Default "oreQuarryBlacklist"]
54
dumpCompareItems = true --If ore quarry, the turtle will dump items compared to (like cobblestone) [Default true]
55
inventoryMax = 16 --The max number of slots in the turtle inventory [Default 16] (Not assignable by parameter)
56
quadEnabled = false --Whether or not to request a quadRotor when out of fuel [Default false]
57
quadTimeout = 60 * 5 --How long the turtle will wait for a quadRotor [Default 5 minutes]
58
--Standard number slots for fuel (you shouldn't care)
59
fuelTable = { --Will add in this amount of fuel to requirement.
60
safe = 1000,
61
moderate = 200,
62
loose = 0 } --Default 1000, 200, 0
63
--Standard rednet channels
64
channels = {
65
send = os.getComputerID() + 1  ,
66
receive = os.getComputerID() + 101 ,
67
confirm = "Turtle Quarry Receiver",
68
message = "Civil's Quarry",
69
fingerprint = "quarry"
70
}
71
72
--AVERAGE USER: YOU DON'T CARE BELOW THIS POINT
73
74
local help_paragraph = [[
75
Welcome!: Welcome to quarry help. Below are help entries for all parameters. Examples and tips are at the bottom.
76
-default: This will force no prompts. If you use this and nothing else, only defaults will be used.
77
-dim: [length] [width] [height] This sets the dimensions for the quarry
78
-invert: [t/f] If true, quarry will be inverted (go up instead of down)
79
-rednet: [t/f] If true and you have a wireless modem on the turtle, will attempt to make a rednet connection for sending important information to a screen
80
-restore / -resume: If your quarry stopped in the middle of its run, use this to resume at the point where the turtle was. Not guarenteed to work properly. For more accurate location finding, check out the -GPS parameter
81
-autoResume / autoRestore: Turtle will automatically resume if stopped. Replaces startup
82
-oreQuarry: [t/f] If true, the turtle will use ore quarry mode. It will not mine the blocks that are placed in the turtle initially. So if you put in stone, it will ignore stone blocks and only mine ores.
83
-oreQuarry: [t/f] If you are using a newer version of CC, you won't have to put in any compare blocks. (CC 1.64+)
84
-blacklist: [file name] If using oreQuarry, this is the blacklist file it will read. Example --
85
  minecraft:stone
86
  minecraft:sand
87
  ThermalExpansion:Sponge
88
  ThermalFoundation:Storage
89
  
90
  If you have bspkrsCore, look for "UniqueNames.txt" in your config
91
-atChest: [force] This is for use with "-restore," this will tell the restarting turtle that it is at its home chest, so that if it had gotten lost, it now knows where it is.
92
-doRefuel: [t/f] If true, the turtle will refuel itself with coal and planks it finds on its mining run
93
-doCheckFuel: [t/f] If you for some reason don't want the program to check fuel usage, set to false. This is honestly a hold-over from when the refueling algorithm was awful...
94
-uniqueExtras: [number] The expected number of slots filled with low-stacking items like ore. Higher numbers request more fuel.
95
-maxFuel: [number] How much the turtle will fuel to max (limited by turtle in most cases)
96
-chest: [side] This specifies what side the chest at the end will be on. You can say "top", "bottom", "front", "left", or "right"
97
-enderChest: This one is special. If you use "-enderChest true" then it will use an enderChest in the default slot. However, you can also do "-enderChest [slot]" then it will take the ender chest from whatever slot you tell it to. Like 7... or 14... or whatever.
98
-fuelChest: See the above, but for a fueling chest. Reccommend use with -maxFuel and -doCheckFuel false
99
-GPS: [force] If you use "-GPS" and there is a GPS network, then the turtle will record its first two positions to precisly calculate its position if it has to restart. This will only take two GPS readings
100
-quad: [t/f] This forces the use of GPS. Make sure you have a network set up. This will request to be refueled by a quadrotor from Lyqyd's mod if the turtle is out of fuel
101
-quadTimeout: [number] The amount of time the turtle will wait for a quadRotor
102
-sendChannel: [number] This is what channel your turtle will send rednet messages on
103
-receiveChannel: [number] This is what channel your turtle will receive rednet messages on
104
-legacyRednet: [t/f] Check true if using 1.4.7
105
-startY: [current Y coord] Randomly encountering bedrock? This is the parameter for you! Just give it what y coordinate you are at right now. If it is not within bedrock range, it will never say it found bedrock
106
-startupRename: [file name] What to rename any existing startup to.
107
-startupName: [file name] What the turtle will save its startup file to.
108
-extraDropItems: [force] If oreQuarry then this will prompt the user for extra items to drop, but not compare to (like cobblestone)
109
-dumpCompareItems: [t/f] If oreQuarry and this is true, the turtle will dump off compare blocks instead of storing them in a chest
110
-oldOreQuarry: [t/f] If you are using new CC versions, you can use this to use the old oreQuarry.
111
-left: [t/f] If true, turtle will quarry to the left instead of the right
112
-maxTries: [number] This is the number of times the turtle will try to dig before deciding its run into bedrock.
113
-logging: [t/f] If true, will record information about its mining run in a folder at the end of the mining run
114
-doBackup: [t/f] If false, will not back up important information and cannot restore, but will not make an annoying file (Actually I don't really know why anyone would use this...)
115
-saveFile: [word] This is what the backup file will be called
116
-logFolder: [word] The folder that quarry logs will be stored in
117
-logExtension: [word] The extension given to each quarry log (e.g. ".txt" or ".notepad" or whatever)
118
-keepOpen: [number] This is the number of the slots the turtle will make sure are open. It will check every time it mines
119
-careAboutResources: [t/f] Who cares about the materials! If set to false, it will just keep mining when its inventory is full
120
-startDown: [number] If you set this, the turtle will go down this many blocks from the start before starting its quarry
121
  =
122
  C _ |
123
      |
124
      |
125
      |
126
      |_ _ _ _ >
127
-promptAll: This is the opposite of -Default, it prompts for everything
128
-manualPos: [xPos] [zPos] [yPos] [facing] This is for advanced use. If the server reset when the turtle was in the middle of a 100x100x100 quarry, fear not, you can now manually set the position of the turtle. yPos is always positive. The turtle's starting position is 0, 1, 1, 0. Facing is measured 0 - 3. 0 is forward, and it progresses clockwise. Example- "-manualPos 65 30 30 2"
129
-help: Thats what this is :D
130
Examples: Everything below is examples and tips for use
131
Important Note:
132
  None of the above parameters are necessary. They all have default values, and the above are just if you want to change them.
133
Examples [1]: 
134
  Want to just start a quarry from the interface, without going through menus? It's easy! Just use some parameters. Assume you called the program "quarry." To start a 10x6x3 quarry, you just type in "quarry -dim 10 6 3 -default". 
135
  You just told it to start a quarry with dimensions 10x6x3, and "-default" means it won't prompt you about invert or rednet. Wasn't that easy?
136
Examples [2]:
137
  Okay, so you've got the basics of this now, so if you want, you can type in really long strings of stuff to make the quarry do exactly what you want. Now, say you want a 40x20x9, but you want it to go down to diamond level, and you're on the surface (at y = 64). You also want it to send rednet messages to your computer so you can see how its doing. 
138
Examples [2] [cont.]:
139
  Oh yeah! You also want it to use an ender chest in slot 12 and restart if the server crashes. Yeah, you can do that. You would type
140
  "quarry -dim 40x20x9 -invert false -startDown 45 -rednet true -enderChest 12 -restore"
141
  BAM. Now you can just let that turtle do it's thing
142
Tips:
143
  The order of the parameters doesn't matter. "quarry -invert false -rednet true" is the same as "quarry -rednet true -invert false"
144
  
145
  Capitalization doesn't matter. "quarry -iNVErt FALSe" does the same thing as "quarry -invert false"
146
Tips [cont.]:
147
  For [t/f] parameters, you can also use "yes" and "no" so "quarry -invert yes"
148
  
149
  For [t/f] parameters, it only cares about the first letter. So you can use "quarry -invert t" or "quarry -invert y"
150
Tips [cont.]:
151
  If you are playing with fuel turned off, the program will automatically change settings for you so you don't have to :D
152
  
153
  If you want, you can load this program onto a computer, and use "quarry -help" so you can have help with the parameters whenever you want.
154
Internal Config:
155
  At the top of this program is an internal configuration file. If there is some setup that you use all the time, you can just change the config value at the top and run "quarry -default" for a quick setup.
156
  
157
  You can also use this if there are settings that you don't like the default value of.
158
]]
159
160
--NOTE: BIOS 114 MEANS YOU FORGOT A COLON
161
--Parsing help for display
162
--[[The way the help table works:
163
All help indexes are numbered. There is a help[i].title that contains the title,
164
and the other lines are in help[i][1] - help[i][#help[i] ]
165
Different lines (e.g. other than first) start with a space.
166
As of now, the words are not wrapped, fix that later]]
167
local help = {}
168
local i = 0
169
local titlePattern = ".-%:" --Find the beginning of the line, then characters, then a ":"
170
local textPattern = "%:.+" --Find a ":", then characters until the end of the line
171
for a in help_paragraph:gmatch("\n?.-\n") do --Matches in between newlines
172
local current = string.sub(a,1,-2).."" --Concatenate Trick
173
if string.sub(current,1,1) ~= " " then
174
i = i + 1
175
help[i] = {}
176
help[i].title = string.sub(string.match(current, titlePattern),1,-2)..""
177
help[i][1] = string.sub(string.match(current,textPattern) or " ",3,-1)
178
elseif string.sub(current,1,1) == " " then
179
table.insert(help[i], string.sub(current,2, -1).."")
180
end
181
end
182
183
local supportsRednet
184
if peripheral.find then
185
  supportsRednet = peripheral.find("modem") or false
186
else
187
  supportsRednet = (peripheral.getType("right") == "modem") or false
188
end
189
190
local tArgs = {...}
191
--Pre-defining variables that need to be saved
192
      xPos,yPos,zPos,facing,percent,mined,moved,relxPos, rowCheck, connected, isInPath, layersDone, attacked, startY, chestFull, gotoDest, atChest, fuelLevel, numDropOffs, allowedItems, compareSlots, dumpSlots, selectedSlot, extraDropItems, oldOreQuarry, specialSlots, relzPos
193
    = 0,   1,   1,   0,     0,      0,    0,    1,       true   ,  false,     true,     1,          0,        0,      false,     "",       false,   0,         0,           {},             {},           {},      1,            false,          false,        {},           0
194
    
195
local statusString
196
197
--Initializing various inventory management tables
198
for i=1, inventoryMax do 
199
  allowedItems[i] = 0 --Number of items allowed in slot when dropping items
200
  dumpSlots[i] = false --Does this slot contain junk items?
201
end --compareSlots is a table of the compare slots, not all slots with a condition
202
totals = {cobble = 0, fuel = 0, other = 0} -- Total for display (cannot go inside function), this goes up here because many functions use it
203
204
local function newSpecialSlot(index, value)
205
  value = tonumber(value) or 0 --We only want numerical indexes
206
  if specialSlots[value] then return false end --Can't overwrite other slots
207
  specialSlots[index] = value
208
  specialSlots[value] = index
209
  return true
210
end
211
212
function resetDumpSlots()
213
    for i=1, inventoryMax do
214
      if oldOreQuarry then
215
        if turtle.getItemCount(i) > 0 and i~= specialSlots.enderChest then
216
          dumpSlots[i] = true
217
        else
218
          dumpSlots[i] = false
219
        end
220
      else
221
        dumpSlots[i] = false
222
      end
223
    end
224
    if not oldOreQuarry and specialSlots.enderChest == 1 then
225
      dumpSlots[2] = true
226
    elseif not oldOreQuarry then
227
      dumpSlots[1] = true
228
    end
229
end
230
231
local function copyTable(tab) local toRet = {}; for a, b in pairs(tab) do toRet[a] = b end; return toRet end --This goes up here because it is a basic utility
232
233
--NOTE: rowCheck is a bit. true = "right", false = "left"
234
    
235
local foundBedrock = false
236
237
local checkFuel, checkFuelLimit
238
if turtle then --Function inits
239
  checkFuel = turtle.getFuelLevel
240
  if turtle.getFuelLevel() == "unlimited" then --Fuel is disabled --Unlimited screws up my calculations
241
    checkFuel = function() return math.huge end --Infinite Fuel
242
  end --There is no "else" because it will already return the regular getFuel
243
  if turtle.getFuelLimit then
244
    checkFuelLimit = function() return math.min(turtle.getFuelLimit(), excessFuelAmount) end --Return the limiting one
245
    if turtle.getFuelLimit() == "unlimited" then 
246
      checkFuelLimit = function() return math.huge end
247
    end
248
  else
249
    checkFuelLimit = function() return excessFuelAmount end --If the function doesn't exist 
250
  end
251
252
  
253
  turtle.select(1) --To ensure this is correct
254
end
255
256
257
function select(slot)
258
  if slot ~= selectedSlot and slot > 0 and slot <= inventoryMax then
259
    selectedSlot = slot
260
    return turtle.select(slot), selectedSlot
261
  end
262
end
263
  
264
265
 -----------------------------------------------------------------
266
--Input Phase
267
local function screen(xPos,yPos)
268
xPos, yPos = xPos or 1, yPos or 1
269
term.setCursorPos(xPos,yPos); term.clear(); end
270
local function screenLine(xPos,yPos)
271
term.setCursorPos(xPos,yPos); term.clearLine(); end
272
273
screen(1,1)
274
print("----- Welcome to Quarry! -----")
275
print("")
276
277
local sides = {top = "top", right = "right", left = "left", bottom = "bottom", front = "front"} --Used to whitelist sides
278
local changedT, tArgsWithUpper = {}, {}
279
changedT.new = function(key, value) table.insert(changedT,{key, value}) end --Numeric list of lists
280
local function capitalize(text) return (string.upper(string.sub(text,1,1))..string.sub(text,2,-1)) end
281
for i=1, #tArgs do tArgsWithUpper[i] = tArgs[i]; tArgsWithUpper[tArgsWithUpper[i]] = i; tArgs[i] = tArgs[i]:lower(); tArgs[tArgs[i]] = i end --My signature key-value pair system, now with upper
282
283
local restoreFound, restoreFoundSwitch = false --Initializing so they are in scope
284
function addParam(name, displayText, formatString, forcePrompt, trigger, variableOverride) --To anyone that doesn't understand this very well, probably not your best idea to go in here.
285
  if trigger == nil then trigger = true end --Defaults to being able to run
286
  if not trigger then return end --This is what the trigger is for. Will not run if trigger not there
287
  if restoreFoundSwitch or tArgs["-default"] then forcePrompt = false end --Don't want to prompt if these. Default is no variable because resuming
288
  if not restoreFoundSwitch and tArgs["-promptall"] then forcePrompt = true end
289
  local toGetText = name:lower() --Because all params are now lowered
290
  local formatType = formatString:match("^%a+"):lower() or error("Format String Unknown: "..formatString) --Type of format string
291
  local args = formatString:sub(({formatString:find(formatType)})[2] + 2).."" --Everything in formatString but the type and space
292
  local variable = variableOverride or name --Goes first to the override for name
293
  local func = loadstring("return "..variable)
294
  setfenv(func,getfenv(1))
295
  local originalValue = assert(func)() --This is the default value, for checking to add to changed table
296
  if originalValue == nil then error("From addParam, \""..variable.."\" returned nil",2) end --I may have gotten a wrong variable name
297
  local givenValue, toRet --Initializing for use
298
  if tArgs["-"..toGetText] then
299
    givenValue = tArgsWithUpper[tArgs["-"..toGetText]+1] --This is the value after the desired parameter
300
  elseif forcePrompt then
301
    write(displayText.."? ")
302
    givenValue = io.read()
303
  end
304
  if formatType == "force" then --This is the one exception. Should return true if givenValue is nothing
305
    toRet = (tArgs["-"..toGetText] and true) or false --Will return true if param exists, otherwise false
306
  end
307
  if not (givenValue or toRet) or (type(givenValue) == "string" and #givenValue == 0) then return end --Don't do anything if you aren't given anything. Leave it as default, except for "force"
308
  if formatType == "boolean" then --All the format strings will be basically be put through a switch statement
309
    toRet = givenValue:sub(1,1):lower() ~= "n" and givenValue:sub(1,1):lower() ~= "f" --Accepts anything but false or no
310
  elseif formatType == "string" then
311
    toRet = givenValue:match("^[%w%.]+") --Basically anything not a space or control character etc
312
  elseif formatType == "number" then
313
    toRet = tonumber(givenValue) --Note this is a local, not the above so we don't change anything
314
    if not toRet then return end --We need a number... Otherwise compare errors
315
    toRet = math.abs(math.floor(toRet)) --Get proper integers
316
    local startNum, endNum = formatString:match("(%d+)%-(%d+)") --Gets range of numbers
317
    startNum, endNum = tonumber(startNum), tonumber(endNum)
318
    if not ((toRet >= startNum) and (toRet <= endNum)) then return end --Can't use these
319
  elseif formatType == "side" then
320
    local exclusionTab = {} --Ignore the wizardry here. Just getting arguments without format string
321
    for a in args:gmatch("%S+") do exclusionTab[a] = true end --This makes a list of the sides to not include
322
    if not exclusionTab[givenValue] then toRet = sides[givenValue] end --If side is not excluded
323
  elseif formatType == "list" then
324
    toRet = {}
325
    for a in args:gmatch("[^,]") do
326
      table.insert(toRet,a)
327
    end
328
  elseif formatType == "force" then --Do nothing, everything is already done
329
  else error("Improper formatType",2)
330
  end
331
  if toRet == nil then return end --Don't want to set variables to nil... That's bad
332
  tempParam = toRet --This is what loadstring will see :D
333
  local func = loadstring(variable.." = tempParam")
334
  setfenv(func, getfenv(1))
335
  func()
336
  tempParam = nil --Cleanup of global
337
  if toRet ~= originalValue and displayText ~= "" then
338
    changedT.new(displayText, tostring(toRet))
339
  end
340
  return toRet
341
end
342
343
--Check if it is a turtle
344
if not(turtle or tArgs["help"] or tArgs["-help"] or tArgs["-?"] or tArgs["?"]) then --If all of these are false then
345
  print("This is not a turtle, you might be looking for the \"Companion Rednet Program\" \nCheck My forum thread for that")
346
  print("Press 'q' to quit, or any other key to start help ")
347
  if ({os.pullEvent("char")})[2] ~= "q" then tArgs.help = true else error("",0) end
348
end
349
350
if tArgs["help"] or tArgs["-help"] or tArgs["-?"] or tArgs["?"] then
351
  print("You have selected help, press any key to continue"); print("Use arrow keys to navigate, q to quit"); os.pullEvent("key")
352
  local pos = 1
353
  local key = 0
354
  while pos <= #help and key ~= keys.q do
355
    if pos < 1 then pos = 1 end
356
    screen(1,1) 
357
    print(help[pos].title)
358
    for a=1, #help[pos] do print(help[pos][a]) end
359
    repeat
360
      _, key = os.pullEvent("key")
361
    until key == 200 or key == 208 or key == keys.q
362
    if key == 200 then pos = pos - 1 end
363
    if key == 208 then pos = pos + 1 end
364
  end
365
  error("",0)
366
end
367
368
--Saving
369
addParam("doBackup", "Backup Save File", "boolean")
370
addParam("saveFile", "Save File Name", "string")
371
372
restoreFound = fs.exists(saveFile)
373
restoreFoundSwitch = (tArgs["-restore"] or tArgs["-resume"] or tArgs["-atchest"]) and restoreFound and doBackup
374
if restoreFoundSwitch then
375
  local file = fs.open(saveFile,"r")
376
  local test = file.readAll() ~= ""
377
  file.close()
378
  if test then
379
    os.run(getfenv(1),saveFile) --This is where the actual magic happens
380
    numResumed = numResumed + 1
381
    if checkFuel() ~= math.huge then --If turtle uses fuel
382
      if fuelLevel - checkFuel() == 1 then
383
        if facing == 0 then xPos = xPos + 1
384
        elseif facing == 2 then xPos = xPos - 1
385
        elseif facing == 1 then zPos = zPos + 1
386
        elseif facing == 3 then zPos = zPos - 1 end
387
      elseif fuelLevel - checkFuel() ~= 0 then
388
        print("Very Strange Fuel in Restore Section...")
389
        print("Current: ",checkFuel())
390
        print("Saved: ",fuelLevel)
391
        print("Difference: ",fuelLevel - checkFuel())
392
        os.pullEvent("char")
393
      end
394
     end
395
    if gpsEnabled then --If it had saved gps coordinates
396
      print("Found GPS Start Coordinates") 
397
      local currLoc = {gps.locate(gpsTimeout)} or {}
398
      local backupPos = {xPos, yPos, zPos} --This is for comparing to later
399
      if #currLoc > 0 and #gpsStartPos > 0 and #gpsSecondPos > 0 then --Cover all the different positions I'm using
400
        print("GPS Position Successfully Read")
401
        if currLoc[1] == gpsStartPos[1] and currLoc[3] == gpsStartPos[3] then --X coord, y coord, z coord in that order
402
          xPos, yPos, zPos = 0,1,1
403
          if facing ~= 0 then turnTo(0) end
404
          print("Is at start")
405
        else
406
          if inverted then --yPos setting
407
          ------------------------------------------------FIX THIS
408
          end
409
          local a, b = copyTable(gpsStartPos), copyTable(gpsSecondPos) --For convenience
410
          local flag = true --So we can account for left quarry
411
          if b[3] - a[3] == -1 then--If went north (-Z)
412
            a[1] = a[1] - 1 --Shift x one to west to create a "zero"
413
            xPos, zPos = -currLoc[3] + a[3], currLoc[1] + -a[1]
414
          elseif b[1] - a[1] == 1 then--If went east (+X)
415
            a[3] = a[3] - 1 --Shift z up one to north to create a "zero"
416
            xPos, zPos = currLoc[1] + -a[1], currLoc[3] + -a[3]
417
          elseif b[3] - a[3] == 1 then--If went south (+Z)
418
            a[1] = a[1] + 1 --Shift x one to east to create a "zero"
419
            xPos, zPos = currLoc[3] + a[3], -currLoc[1] + a[3]
420
          elseif b[1] - a[1] == -1 then--If went west (-X)
421
            a[3] = a[3] + 1 --Shift z down one to south to create a "zero"
422
            xPos, zPos = -currLoc[1] + a[1], -currLoc[3] + a[3]
423
          else
424
            flag = false
425
            print("Improper Coordinates")
426
            print("GPS Locate Failed, Using Standard Methods")        ----Maybe clean this up a bit to use flags instead.
427
          end
428
          if flag and goLeftNotRight then --This accounts for left quarry (barred to left only because there might be errors in a regular, causing neg/0
429
            zPos = math.abs(zPos-1) + 1
430
          end
431
        end
432
        print("X Pos: ",xPos)
433
        print("Y Pos: ",yPos)
434
        print("Z Pos: ",zPos)
435
        print("Facing: ",facing)
436
        for i=1, 3, 2 do --We want 1 and 3, but 2 could be coming back to start.
437
          if backupPos[i] ~= currLoc[i] then
438
            events = {} --We want to remove event queue if not in proper place, so won't turn at end of row or things.
439
          end
440
        end
441
      else
442
        print("GPS Locate Failed, Using Standard Methods")
443
      end    
444
    print("Restore File read successfully. Starting in 3"); sleep(3)
445
    end
446
  else
447
    fs.delete(saveFile)
448
    print("Restore file was empty, sorry, aborting")
449
    error("",0)
450
  end
451
else --If turtle is just starting
452
  events = {} --This is the event queue :D
453
  originalFuel = checkFuel() --For use in logging. To see how much fuel is REALLY used
454
end
455
456
--Dimensions
457
if tArgs["-dim"] then 
458
  local a,b,c = x,y,z
459
  local num = tArgs["-dim"]
460
  x = tonumber(tArgs[num + 1]) or x; z = tonumber(tArgs[num + 2]) or z; y = tonumber(tArgs[num + 3]) or y
461
  if a ~= x then changedT.new("Length", x) end
462
  if c ~= z then changedT.new("Width", z) end
463
  if b ~= y then changedT.new("Height", y) end
464
elseif not (tArgs["-default"] or restoreFoundSwitch) then
465
  print("What dimensions?")
466
  print("")
467
  --This will protect from negatives, letters, and decimals
468
  term.write("Length? ")
469
  x = math.floor(math.abs(tonumber(io.read()) or x))
470
  term.write("Width? ")
471
  z = math.floor(math.abs(tonumber(io.read()) or z))
472
  term.write("Height? ")
473
  y = math.floor(math.abs(tonumber(io.read()) or y))
474
  changedT.new("Length",x); changedT.new("Width",z); changedT.new("Height",y)
475
end
476
--Params: parameter/variable name, display name, type, force prompt, boolean condition, variable name override
477
--Invert
478
addParam("flatBedrock","Go to bedrock", "boolean") --Not done before GPS because GPS only runs on restart
479
addParam("invert", "Inverted","boolean", true, not flatBedrock, "inverted") --Not flat bedrock, because invert will be set to false
480
addParam("startDown","Start Down","number 1-256", nil, not flatBedrock)
481
addParam("left","Left Quarry","boolean", nil, nil, "goLeftNotRight")
482
--Inventory
483
addParam("chest", "Chest Drop Side", "side front", nil, nil, "dropSide")
484
addParam("enderChest","Ender Chest Enabled","boolean special", nil, nil, "enderChestEnabled") --This will accept anything (including numbers) thats not "f" or "n"
485
addParam("enderChest", "Ender Chest Slot", "number 1-16", nil, nil, "enderChestSlot") --This will get the number slot if given
486
newSpecialSlot("enderChest",enderChestEnabled and enderChestSlot or 0) --This allows for easy checking and getting --This makes everything better (0)
487
addParam("fuelChest","Fuel Chest Enabled","boolean special", nil, nil, "fuelChestEnabled") --See above notes
488
addParam("fuelChest", "Fuel Chest Slot", "number 1-16", nil, nil, "fuelChestSlot")
489
newSpecialSlot("fuelChest", fuelChestEnabled and fuelChestSlot or 0)
490
if (enderChestEnabled and fuelChestEnabled) and (enderChestSlot == fuelChestSlot) then
491
  error("Warning: Ender Chest Slot and Fuel Chest Slot cannot be the same",0)
492
end
493
--Rednet
494
addParam("rednet", "Rednet Enabled","boolean",true, supportsRednet, "rednetEnabled")
495
addParam("sendChannel", "Rednet Send Channel", "number 1-65535", false, supportsRednet, "channels.send")
496
addParam("receiveChannel","Rednet Receive Channel", "number 1-65535", false, supportsRednet, "channels.receive")
497
addParam("fingerprint","Sending Fingerprint", "string", false, supportsRednet, "channels.fingerprint")
498
addParam("legacyRednet","Legacy Rednet","boolean", false, supportsRednet)
499
--Quad Rotor --Must be before GPS
500
if addParam("quad", "Quad Rotor Enabled","boolean",nil, rednetEnabled, "quadEnabled") then --This returns true if param found :3
501
  gpsEnabled = true
502
end
503
addParam("quadTimeout","Quad Rotor Timeout","number 1-1000000", nil, quadEnabled) --The amount of time to wait for a quadRotor
504
--GPS
505
addParam("gps", "GPS Location Services", "force", nil, (not restoreFoundSwitch) and supportsRednet, "gpsEnabled" ) --Has these triggers so that does not record position if restarted.
506
if gpsEnabled and not restoreFoundSwitch then
507
  gpsStartPos = {gps.locate(gpsTimeout)} --Stores position in array
508
  gpsEnabled = #gpsStartPos > 0 --Checks if location received properly. If not, position is not saved
509
  if quadEnabled and not gpsEnabled then
510
    error("You have no GPS network. You may not use Quad Rotors",0)
511
  end
512
end
513
--Fuel
514
addParam("uniqueExtras","Unique Items", "number 0-15")
515
addParam("doRefuel", "Refuel from Inventory","boolean", nil, checkFuel() ~= math.huge) --math.huge due to my changes
516
addParam("doCheckFuel", "Check Fuel", "boolean", nil, checkFuel() ~= math.huge)
517
excessFuelAmount = excessFuelAmount or math.huge --Math.huge apparently doesn't save properly
518
addParam("maxFuel", "Max Fuel", "number 1-999999999", nil, checkFuel() ~= math.huge, "excessFuelAmount")
519
--Logging
520
addParam("logging", "Logging", "boolean")
521
addParam("logFolder", "Log Folder", "string")
522
addParam("logExtension","Log Extension", "string")
523
--Misc
524
addParam("startY", "Start Y","number 1-256")
525
addParam("keepOpen", "Slots to Keep Open", "number 1-15")
526
addParam("careAboutResources", "Care About Resources","boolean")
527
addParam("maxTries","Tries Before Bedrock", "number 1-9001")
528
--Auto Startup
529
addParam("autoResume", "Auto Resume", "boolean", nil, doBackup)
530
addParam("autoRestart", "Auto Restart", "boolean", nil, doBackup, "autoResume")
531
addParam("startupRename", "Startup Rename","string", nil, autoResume)
532
addParam("startupName", "Startup File", "string", nil, autoResume)
533
--Ore Quarry
534
addParam("oreQuarry", "Ore Quarry", "boolean" )
535
if oreQuarry and not turtle.inspect then
536
  oldOreQuarry = true
537
  oreQuarry = false
538
end
539
--Old Ore
540
addParam("oldOreQuarry", "Old Ore Quarry", "boolean")
541
addParam("dumpCompareItems", "Dump Compare Items", "boolean", nil, oldOreQuarry) --Do not dump compare items if not oreQuarry
542
addParam("extraDropItems", "", "force", nil, oldOreQuarry) --Prompt for extra dropItems
543
addParam("extraDumpItems", "", "force", nil, oldOreQuarry, "extraDropItems") --changed to Dump
544
--New Ore
545
addParam("blacklist","Ore Blacklist", "string", nil, oreQuarry, "oreQuarryBlacklistName")
546
--Mod Related
547
548
549
--for flatBedrock
550
if flatBedrock then
551
  inverted = false
552
end
553
554
--Auto Startup functions
555
local function doAutoResumeStuff()
556
  if fs.exists(startupName) then
557
    if fs.exists(startupRename) then fs.delete(startupRename) end
558
    fs.move(startupName, startupRename)
559
  end
560
  local file = fs.open(startupName,"w") --Startup File
561
  file.writeLine( --The below is on the left because spacing
562
[[
563
--This is an auto-generated startup
564
--Made by civilwargeeky's Variable Size Quarry
565
print("Now Resuming Quarry")
566
print("Press any key to quit. You have 5 seconds.")
567
sleep(1)
568
function deleteStuff()
569
  fs.delete("]]..startupName..[[")
570
  if fs.exists("]]..startupRename..[[") then
571
    fs.move("]]..startupRename.."\",\""..startupName..[[")
572
  end
573
end
574
local event
575
if fs.exists("]]..saveFile..[[") then
576
  for i=5,1,-1 do
577
    print(i)
578
    os.startTimer(1)
579
    event = os.pullEvent()
580
    if event == "key" then break end
581
  end
582
  if event == "timer" then
583
    os.run({},"]]..shell.getRunningProgram()..[[","-resume")
584
  else
585
586
    deleteStuff()
587
  end
588
else
589
  print("Never mind, no save file found")
590
  deleteStuff()
591
end
592
  ]])
593
  file.close()
594
end
595
if autoResume and not restoreFoundSwitch then --Don't do for restore because would overwrite renamed thing. Can't edit mid-run because no shell in restarted
596
  doAutoResumeStuff()
597
end
598
--oreQuarry blacklist
599
local blacklist = { "minecraft:air",  "minecraft:bedrock", "minecraft:cobblestone", "minecraft:dirt", "minecraft:ice", "minecraft:ladder", "minecraft:netherrack", "minecraft:sand", "minecraft:sandstone",
600
  "minecraft:snow", "minecraft:snow_layer", "minecraft:stone", "minecraft:gravel", "minecraft:grass", "minecraft:torch" }
601
for a,b in pairs(blacklist) do
602
  blacklist[b], blacklist[b] = true, nil --Switch
603
end
604
if fs.exists(oreQuarryBlacklistName) then --Loading user-defined blacklist
605
  local file = fs.open(oreQuarryBlacklistName, "r")
606
  blacklist = {}
607
  for a in file:readAll():gmatch("[^,]+") do
608
    blacklist[a:match("%S+:%S+")] = true --Grab only the actual characters, not whitespaces
609
  end
610
  file:close()
611
end
612
613
--Manual Position
614
if tArgs["-manualpos"] then --Gives current coordinates in xPos,zPos,yPos, facing
615
  local a = tArgs["-manualpos"]
616
  xPos, zPos, yPos, facing = tonumber(tArgs[a+1]) or xPos, tonumber(tArgs[a+2]) or zPos, tonumber(tArgs[a+3]) or yPos, tonumber(tArgs[a+4]) or facing
617
  changedT.new("xPos",xPos); changedT.new("zPos",zPos); changedT.new("yPos",yPos); changedT.new("facing",facing)
618
  restoreFoundSwitch = true --So it doesn't do beginning of quarry behavior
619
  for i=0,4 do tArgs[a+i] = "" end --Get rid of this argument from future restores
620
end
621
if addParam("atChest", "Is at Chest", "force") then --This sets position to 0,1,1, facing forward, and queues the turtle to go back to proper row.
622
  local neededLayer = math.floor((yPos+1)/3)*3-1 --Make it a proper layer, +- because mining rows are 2, 5, etc.
623
  if neededLayer > 2 and neededLayer%3 ~= 2 then --If turtle was not on a proper mining layer
624
    print("Last known pos was not in proper layer, restarting quarry")
625
    sleep(4)
626
    neededLayer = 2
627
  end
628
  if ((neededLayer-2)/3) % 2 == 1 then neededLayer = neededLayer - 3 end --Because with weird end of row, just go to last basic row
629
  xPos, zPos, yPos, facing, rowCheck, layersDone = 0,1,1, 0, true, math.ceil(neededLayer/3)
630
  doAutoResumeStuff() --This was probably deleted when they hit a key to launch with -atChest
631
  events = {{"goto",1,1,neededLayer, 0}}
632
end
633
634
635
local function saveProgress(extras) --Session persistence
636
exclusions = { modem = true, }
637
if doBackup then
638
local toWrite = ""
639
for a,b in pairs(getfenv(1)) do
640
  if not exclusions[a] then
641
      --print(a ,"   ", b, "   ", type(b)) --Debug
642
    if type(b) == "string" then b = "\""..b.."\"" end
643
    if type(b) == "table" then b = textutils.serialize(b) end
644
    if type(b) ~= "function" then
645
      toWrite = toWrite..a.." = "..tostring(b).."\n"
646
    end
647
  end
648
end
649
toWrite = toWrite.."doCheckFuel = false\n" --It has already used fuel, so calculation unnecessary
650
local file
651
repeat
652
  file = fs.open(saveFile,"w")
653
until file
654
file.write(toWrite)
655
if type(extras) == "table" then
656
  for a, b in pairs(extras) do
657
    file.write(a.." = "..tostring(b).."\n")
658
  end
659
end
660
if checkFuel() ~= math.huge then --Used for location comparing
661
  file.write("fuelLevel = "..tostring(checkFuel()).."\n")
662
end
663
file.close()
664
end
665
end
666
667
local area = x*z
668
local volume = x*y*z
669
local lastHeight = y%3
670
layers = math.ceil(y/3)
671
local yMult = layers --This is basically a smart y/3 for movement
672
local moveVolume = (area * yMult) --Kept for display percent
673
--Calculating Needed Fuel--
674
do --Because many local variables unneeded elsewhere
675
  local changeYFuel = 2*(y + startDown)
676
  local dropOffSupplies = 2*(x + z + y + startDown) --Assumes turtle as far away as possible, and coming back
677
  local frequency = math.ceil(((moveVolume/(64*(15-uniqueExtras) + uniqueExtras)) ) ) --This is complicated: volume / inventory space of turtle, defined as 64*full stacks + 1 * unique stacks.
678
                                                                                     --max of 15 full stacks because once one item is picked up, slot is "full". Ceil to count for initial back and forth
679
  if enderChestEnabled then frequency = 0 end --Never goes back to start
680
  neededFuel = moveVolume + changeYFuel + (frequency * dropOffSupplies) + ((x + z) * layers) --x + z *layers because turtle has to come back from far corner every layer
681
  neededFuel = neededFuel + fuelTable[fuelSafety] --For safety
682
end
683
684
if neededFuel+checkFuel() > checkFuelLimit() and doCheckFuel then--Checks for if refueling goes over turtle fuel limit
685
  if not (doRefuel or fuelChestEnabled) then
686
    screen()
687
    print("Turtle cannot hold enough fuel\n")
688
    print("Options: \n1. Select a smaller size (press q) \n2. Enable Mid-Run Refueling (any other key)")
689
    if ({os.pullEvent("char")})[2] == "q" then 
690
      screen(); print("Okay"); error("",0) 
691
    else
692
      doRefuel = true
693
    end
694
  end
695
  neededFuel = checkFuelLimit()-checkFuel()-1
696
end
697
    
698
    
699
--Getting Fuel
700
local hasRefueled --This is for oreQuarry prompting
701
if doCheckFuel and checkFuel() < neededFuel then
702
  hasRefueled = true
703
  print("Not enough fuel")
704
  print("Current: ",checkFuel()," Needed: ",neededFuel)
705
  print("Starting SmartFuel...")
706
  sleep(2) --So they can read everything.
707
  term.clear()
708
  local oneFuel, neededFuelItems = 0,0 --Initializing Variables
709
  local currSlot = 0
710
  local function output(text, x, y) --For displaying fuel statistics
711
    local currX, currY = term.getCursorPos()
712
    term.setCursorPos(x,y)
713
    term.clearLine()
714
    term.write(text)
715
    term.setCursorPos(currX,currY)
716
  end
717
  local function roundTo(num, target) --For stacks of fuel and turtle slots when undergoing addition/subtraction
718
    if num >= target then return target elseif num < 0 then return 0 else return num end
719
  end
720
  local function updateScreen()
721
    output("Welcome to SmartFuel! Now Refueling...", 1,1)
722
    output("Currently taking fuel from slot "..currSlot,1,2)
723
    output("Current single fuel: "..tostring(oneFuel or 0),1,3)
724
    output("Current estimate of needed fuel: ",1,4)
725
    output("Single Items: "..math.ceil(neededFuelItems),4,5)
726
    output("Stacks:       "..math.ceil(neededFuelItems / 64),4,6)
727
    output("Needed Fuel: "..tostring(neededFuel),1,12)
728
    output("Current Fuel: "..tostring(checkFuel()),1,13)
729
  end
730
  while checkFuel() <= neededFuel do
731
    currSlot = currSlot + 1
732
    select(currSlot)
733
    if currSlot ~= 1 and not turtle.refuel(0) then --If it's not the first slot, and not fuel, go back to start
734
      currSlot = 1; select(currSlot)
735
    end
736
    updateScreen()
737
    while turtle.getItemCount(currSlot) == 0 do
738
      sleep(1.5)
739
    end
740
    repeat --TODO: Probably unnecessary loop, remove later
741
      local previous = checkFuel()
742
      turtle.refuel(1)
743
      oneFuel = checkFuel() - previous
744
      updateScreen()
745
    until (oneFuel or 0) > 0 --Not an if to prevent errors if fuel taken out prematurely.
746
    neededFuelItems = math.ceil((neededFuel - checkFuel()) / oneFuel)
747
    turtle.refuel(roundTo(neededFuelItems, 64)) --Change because can only think about 64 at once.
748
    if turtle.getItemCount(roundTo(currSlot + 1, inventoryMax)) == 0 then --Resets if no more fuel
749
      currSlot = 0
750
    end
751
    neededFuelItems = math.ceil((neededFuel - checkFuel()) / oneFuel) --This line is not repeated uselessly, it's for the display function
752
  end
753
  select(1)
754
end
755
--Ender Chest Obtaining
756
function promptSpecialSlot(specialSlot, name)
757
  while turtle.getItemCount(specialSlots[specialSlot]) ~= 1 do
758
    screen(1,1)
759
    print("You have decided to use a ",name,"!")
760
    print("Please place one ",name," in slot ",specialSlots[specialSlot])
761
    sleep(1)
762
  end
763
  print(name," in slot ",specialSlots[specialSlot], " checks out")
764
end
765
function checkSpecialSlot(specialSlot, name)
766
 if restoreFoundSwitch and turtle.getItemCount(specialSlots[specialSlot]) == 0 then --If the turtle was stopped while dropping off items.
767
    select(specialSlots[specialSlot])
768
    turtle.dig()
769
    select(1)
770
  end
771
  promptSpecialSlot(specialSlot, name)
772
  allowedItems[specialSlots[specialSlot]] = 1
773
  sleep(2)
774
end
775
if enderChestEnabled then
776
  checkSpecialSlot("enderChest","Ender Chest")
777
end
778
if fuelChestEnabled then
779
  checkSpecialSlot("fuelChest","Fuel Chest")
780
end
781
--Fuel Chest Obtaining
782
783
--Setting which slots are marked as compare slots
784
if oldOreQuarry then
785
  if not restoreFoundSwitch then --We don't want to reset compare blocks every restart
786
    local counter = 0
787
    for i=1, inventoryMax do if turtle.getItemCount(i) > 0 and i ~= specialSlots.enderChest then counter = counter+1 end end --If the slot has items, but isn't enderChest slot if it is enabled
788
789
    screen(1,1)
790
    print("You have selected an Ore Quarry!")
791
    if counter == 0 or hasRefueled then --If there are no compare slots, or the turtle has refueled, and probably has fuel in inventory
792
      print("Please place your compare blocks in the first slots\n")
793
      
794
      print("Press Enter when done")
795
      repeat until ({os.pullEvent("key")})[2] == 28 --Should wait for enter key to be pressed
796
    else
797
      print("Registering slots as compare slots")
798
      sleep(1)
799
    end
800
    for i=1, inventoryMax do
801
      if turtle.getItemCount(i) > 0 then
802
        if i ~= specialSlots.enderChest then
803
          table.insert(compareSlots, i) --Compare slots are ones compared to while mining. Conditions are because we Don't want to compare to enderChest
804
          allowedItems[i] = 1 --Blacklist is for dropping off items. The number is maximum items allowed in slot when dropping off
805
          dumpSlots[i] = true --We also want to ignore all excess of these items, like dirt
806
        end
807
      end
808
    end
809
    if extraDropItems then
810
      screen(1,1)
811
      print("Put in extra drop items now\n")
812
      print("Press Enter when done")
813
      repeat until ({os.pullEvent("key")})[2] == 28 --Should wait for enter key to be pressed
814
      for i=1,inventoryMax do
815
        if not dumpSlots[i] and turtle.getItemCount(i) > 0 then --I don't want to modify from above, so I check it hasn't been assigned.
816
          dumpSlots[i] = true
817
          allowedItems[i] = 1
818
        end
819
      end
820
    end
821
    --This is could go very wrong if this isn't here
822
    if #compareSlots >= inventoryMax-keepOpen then screen(1,1); error("You have more quarry compare items than keep open slots, the turtle will continuously come back to start. Please fix.",0) end
823
  end
824
  local counter = 0
825
  for a, b in pairs(compareSlots) do if  turtle.getItemCount(b) > 0 then counter = counter + 1 end end
826
  if counter == 0 then
827
    screen(1,1)
828
    print("You have an ore quarry without any compare slots. Continue? y/n")
829
    if ({os.pullEvent("char")})[2] ~= "y" then error("",0) end
830
  end
831
elseif not oreQuarry then --This was screwing up dumpCompareItems
832
  dumpCompareItems = false --If not an ore quarry, this should definitely be false
833
  if specialSlots.enderChest == 1 then
834
    dumpSlots[2] = true
835
  else
836
    dumpSlots[1] = true
837
  end
838
end
839
840
--Rednet Handshake
841
function newMessageID()
842
  return math.random(1,2000000000)
843
end
844
function sendMessage(send, receive, message)
845
  if legacyRednet then
846
    if type(message) == "table" then message = textutils.serialize(message) end
847
    return modem.transmit(send, receive, message)
848
  end
849
  return modem.transmit(send , receive, {fingerprint = channels.fingerprint, id = newMessageID(), message = message})
850
end
851
if rednetEnabled then
852
  screen(1,1)
853
  print("Rednet is Enabled")
854
  print("The Channel to open is "..channels.send)
855
  if peripheral.find then
856
    modem = peripheral.find("modem")
857
  else
858
    modem = peripheral.wrap("right")
859
  end
860
  modem.open(channels.receive)
861
  local i = 0
862
    repeat
863
      local id = os.startTimer(3)
864
      i=i+1
865
      print("Sending Initial Message "..i)
866
      sendMessage(channels.send, channels.receive, channels.message)
867
      local message = {} --Have to initialize as table to prevent index nil
868
      repeat
869
        local event, idCheck, channel,_,locMessage, distance = os.pullEvent()
870
        if locMessage then message = locMessage end
871
        if legacyRednet then --For that one guy that uses 1.4.7
872
          message = {message = message}
873
        end
874
      until (event == "timer" and idCheck == id) or (event == "modem_message" and channel == channels.receive and type(message) == "table")
875
    until message.message == channels.confirm
876
  connected = true
877
  print("Connection Confirmed!")
878
  sleep(1.5)
879
end
880
function biometrics(isAtBedrock, requestQuad)
881
  if not rednetEnabled then return end --This function won't work if rednet not enabled :P
882
  local toSend = { label = os.getComputerLabel() or "No Label", id = os.getComputerID(),
883
    percent = percent, zPos = relzPos, xPos = relxPos, yPos = yPos,
884
    layersDone = layersDone, x = x, z = z, layers = layers,
885
    openSlots = getNumOpenSlots(), mined = mined, moved = moved,
886
    chestFull = chestFull, isAtChest = (xPos == 0 and yPos == 1 and zPos == 1),
887
    isGoingToNextLayer = (gotoDest == "layerStart"), foundBedrock = foundBedrock,
888
    fuel = checkFuel(), volume = volume, status = statusString,
889
    }
890
  if requestQuad and isInPath then --If we are requesting a quadRotor to send help
891
    if not gps.locate(gpsTimeout) then
892
      print("\nOH NOES! Trying to reach quadrotor, but can't get GPS position!")
893
      sleep(1)
894
    else
895
      toSend.firstPos = gpsStartPos
896
      toSend.secondPos = gpsSecondPos
897
      toSend.emergencyLocation = {gps.locate(gpsTimeout)}
898
    end
899
  end
900
  sendMessage(channels.send, channels.receive, toSend)
901
  id = os.startTimer(0.1)
902
  local event, received
903
  repeat
904
    local locEvent, idCheck, confirm, _, locMessage, distance = os.pullEvent()
905
    event, received = locEvent, locMessage or {message = ""}
906
    if legacyRednet and type(received) == "string" then
907
      received = {message = received}
908
    end
909
  until (event == "timer" and idCheck == id) or (event == "modem_message" and confirm == channels.receive and type(received) == "table")
910
  if event == "modem_message" then connected = true else connected = false end
911
  local message = received.message:lower()
912
  if message == "stop" or message == "quit" or message == "kill" then error("Rednet said to stop...",0) end
913
  if message == "return" then
914
    endingProcedure()
915
    error('Rednet said go back to start...',0)
916
  end
917
  if message == "drop" then
918
    dropOff()
919
  end
920
  if message == "pause" then
921
    print("\nTurtle is paused. Send 'resume' or press any character to resume")
922
    statusString = "Paused"
923
    repeat
924
      sleep(1) --The turtle sends out periodic messages, which will clear the receiver's queue and send a message (if it exists)
925
      sendMessage(channels.send, channels.receive, toSend) --This may be a bit overkill, sending the whole message again, but whatever.
926
      local event, idCheck, confirm, _, message, distance = os.pullEvent()
927
    until (event == "modem_message" and confirm == channels.receive and (message.message == "resume" or message.message == "unpause" or message.message == "pause")) or (event == "char")
928
    statusString = nil
929
  end
930
  if message == "refuel" then
931
    print("\nEngaging in emergency refueling")
932
    emergencyRefuel()
933
  end
934
  
935
end
936
--Showing changes to settings
937
screen(1,1)
938
print("Your selected settings:")
939
if #changedT == 0 then
940
print("Completely Default")
941
else
942
for i=1, #changedT do
943
print(changedT[i][1],": ",changedT[i][2]) --Name and Value
944
end
945
end
946
print("\nStarting in 3"); sleep(1); print("2"); sleep(1); print("1"); sleep(1.5) --Dramatic pause at end
947
948
949
950
----------------------------------------------------------------
951
--Define ALL THE FUNCTIONS
952
--Event System Functions
953
function eventAddAt(pos, ...)
954
  return table.insert(events,pos, {...}) or true
955
end
956
function eventAdd(...) --Just a wrapper
957
  return eventAddAt(1, ...)
958
end
959
function eventGet(pos)
960
  return events[tonumber(pos) or #events]
961
end
962
function eventPop(pos)
963
  return table.remove(events,tonumber(pos) or #events) or false --This will return value popped, tonumber returns nil if fail, so default to end
964
end
965
function eventRun(value, ...)
966
  local argsList = {...}
967
  if type(value) == "string" then
968
    if value:sub(-1) ~= ")" then --So supports both "up()" and "up"
969
      value = value .. "("
970
      for a, b in pairs(argsList) do --Appending arguments
971
        local toAppend
972
        if type(b) == "table" then toAppend = textutils.serialize(b)
973
        elseif type(b) == "string" then toAppend = "\""..tostring(b).."\"" --They weren't getting strings around them
974
        else toAppend = tostring(b) end
975
        value = value .. (toAppend or "true") .. ", "
976
      end
977
      if value:sub(-1) ~= "(" then --If no args, do not want to cut off
978
        value = value:sub(1,-3)..""
979
      end
980
      value = value .. ")"
981
    end
982
    --print(value) --Debug
983
    local func = loadstring(value)
984
    setfenv(func, getfenv(1))
985
    return func()
986
  end
987
end
988
function eventClear(pos)
989
  if pos then events[pos] = nil else events = {} end
990
end   
991
function runAllEvents()
992
  while #events > 0 do
993
    local toRun = eventGet()
994
    --print(toRun[1]) --Debug
995
    eventRun(unpack(toRun))
996
    eventPop()
997
  end
998
end
999
1000
--Display Related Functions
1001
function display() --This is just the last screen that displays at the end
1002
  screen(1,1)
1003
  print("Total Blocks Mined: "..mined)
1004
  print("Current Fuel Level: "..checkFuel())
1005
  print("Cobble: "..totals.cobble)
1006
  print("Usable Fuel: "..totals.fuel)
1007
  print("Other: "..totals.other)
1008
  if rednetEnabled then
1009
    print("")
1010
    print("Sent Stop Message")
1011
    if legacyRednet then --This was the traditional stopping signal
1012
      print("Sent Legacy Stop")
1013
      sendMessage(channels.send, channels.receive, "stop")
1014
    end
1015
    local finalTable = {mined = mined, cobble = totals.cobble, fuelblocks = totals.fuel,
1016
        other = totals.other, fuel = checkFuel(), isDone = true }
1017
    sendMessage(channels.send,channels.receive, finalTable)
1018
    modem.close(channels.receive)
1019
  end
1020
  if doBackup then 
1021
    fs.delete(saveFile)
1022
    if autoResume then --Getting rid of the original startup files and replacing
1023
      fs.delete(startupName)
1024
      if fs.exists(startupRename) then
1025
        fs.move(startupRename, startupName)
1026
      end
1027
    end
1028
  end
1029
end
1030
function updateDisplay() --Runs in Mine(), display information to the screen in a certain place
1031
screen(1,1)
1032
print("Blocks Mined")
1033
print(mined)
1034
print("Percent Complete")
1035
print(percent.."%")
1036
print("Fuel")
1037
print(checkFuel())
1038
  -- screen(1,1)
1039
  -- print("Xpos: ")
1040
  -- print(xPos)
1041
  -- print("RelXPos: ")
1042
  -- print(relxPos)
1043
  -- print("Z Pos: ")
1044
  -- print(zPos)
1045
  -- print("Y pos: ")
1046
  -- print(yPos)
1047
if rednetEnabled then
1048
screenLine(1,7)
1049
print("Connected: "..tostring(connected))
1050
end
1051
end
1052
--Utility functions
1053
function logMiningRun(textExtension, extras) --Logging mining runs
1054
  if not logging then return end
1055
  local number, name = 0
1056
  if not fs.isDir(logFolder) then
1057
    fs.delete(logFolder)
1058
    fs.makeDir(logFolder)
1059
  end
1060
  repeat
1061
    number = number + 1 --Number will be at least 2
1062
    name = logFolder.."/Quarry_Log_"..tostring(number)..(textExtension or "")
1063
  until not fs.exists(name)
1064
  local handle = fs.open(name,"w")
1065
  local function write(...)
1066
    for a, b in ipairs({...}) do
1067
      handle.write(tostring(b))
1068
    end
1069
    handle.write("\n")
1070
  end
1071
  local function boolToText(bool) if bool then return "Yes" else return "No" end end
1072
  write("Welcome to the Quarry Logs!")
1073
  write("Entry Number: ",number)
1074
  write("Quarry Version: ",VERSION)
1075
  write("Dimensions (X Z Y): ",x," ",z," ", y)
1076
  write("Blocks Mined: ", mined)
1077
  write("  Cobble: ", totals.cobble)
1078
  write("  Usable Fuel: ", totals.fuel)
1079
  write("  Other: ",totals.other)
1080
  write("Total Fuel Used: ",  (originalFuel or (neededFuel + checkFuel()))- checkFuel()) --Protect against errors with some precision
1081
  write("Expected Fuel Use: ", neededFuel)
1082
  write("Days to complete mining run: ",os.day()-originalDay)
1083
  write("Day Started: ", originalDay)
1084
  write("Number of times resumed: ", numResumed)
1085
  write("Was an ore quarry? ",boolToText(oreQuarry or oldOreQuarry))
1086
  write("Was inverted? ",boolToText(invert))
1087
  write("Was using rednet? ",boolToText(rednetEnabled))
1088
  write("Chest was on the ",dropSide," side")
1089
  if startDown > 0 then write("Started ",startDown," blocks down") end
1090
  handle.close()
1091
end
1092
--Inventory related functions
1093
function isFull(slots) --Checks if there are more than "slots" used inventory slots.
1094
  slots = slots or inventoryMax
1095
  local numUsed = 0
1096
  sleep(0)
1097
  for i=1, inventoryMax do
1098
    if turtle.getItemCount(i) > 0 then numUsed = numUsed + 1 end
1099
  end
1100
  if numUsed > slots then
1101
    return true 
1102
  end
1103
  return false
1104
end
1105
function countUsedSlots() --Returns number of slots with items in them, as well as a table of item counts
1106
  local toRet, toRetTab = 0, {}
1107
  for i=1, inventoryMax do
1108
    local a = turtle.getItemCount(i)
1109
    if a > 0 then toRet = toRet + 1 end
1110
    table.insert(toRetTab, a)
1111
  end
1112
  return toRet, toRetTab
1113
end
1114
function getSlotsTable() --Just get the table from above
1115
  local _, toRet = countUsedSlots()
1116
  return toRet
1117
end
1118
function getChangedSlots(tab1, tab2) --Returns a table of changed slots. Format is {slotNumber, numberChanged}
1119
  local toRet = {}
1120
  for i=1, math.min(#tab1, #tab2) do
1121
    diff = math.abs(tab2[i]-tab1[i])
1122
    if diff > 0 then
1123
      table.insert(toRet, {i, diff})
1124
    end
1125
  end
1126
  return toRet
1127
end
1128
function getFirstChanged(tab1, tab2) --Just a wrapper. Probably not needed
1129
  local a = getChangedSlots(tab1,tab2)
1130
  return (a[1] or {"none"})[1]
1131
end
1132
1133
function getRep(which, list) --Gets a representative slot of a type. Expectation is a sequential table of types
1134
  for a,b in pairs(list) do
1135
    if b == which then return a end
1136
  end
1137
  return false
1138
end
1139
function assignTypes(types, count) --The parameters allow a preexisting table to be used, like a table from the original compareSlots...
1140
  types, count = types or {1}, count or 1 --Table of types and current highest type
1141
  for i=1, inventoryMax do
1142
    if turtle.getItemCount(i) > 0 and not specialSlots[i] then --Not special slots so we don't count ender chests
1143
      select(i)
1144
      for k=1, count do
1145
        if turtle.compareTo(getRep(k, types)) then types[i] = k end
1146
      end
1147
      if not types[i] then
1148
        count = count + 1
1149
        types[i] = count
1150
      end
1151
      if oreQuarry then
1152
        if blacklist[turtle.getItemDetail().name] then
1153
          dumpSlots[i] = true
1154
        else
1155
          dumpSlots[i] = false
1156
        end
1157
      end
1158
    end
1159
  end
1160
  select(1)
1161
  return types, count
1162
end
1163
function getTableOfType(which, list) --Returns a table of all the slots of which type
1164
  local toRet = {}
1165
  for a, b in pairs(list) do 
1166
    if b == which then
1167
      table.insert(toRet, a)
1168
    end
1169
  end
1170
  return toRet
1171
end
1172
1173
--This is so the turtle will properly get types, otherwise getRep of a type might not be a dumpSlot, even though it should be.
1174
if not restoreFoundSwitch then --We only want this to happen once
1175
  if oldOreQuarry then --If its not ore quarry, this screws up type assigning
1176
    initialTypes, initialCount = assignTypes()
1177
  else
1178
    initialTypes, initialCount = {1}, 1
1179
  end
1180
end
1181
1182
function count(add) --Done any time inventory dropped and at end, true=add, false=nothing, nil=subtract
1183
  local mod = -1
1184
  if add then mod = 1 end
1185
  if add == false then mod = 0 end
1186
  slot = {}        --1: Filler 2: Fuel 3:Other --[1] is type, [2] is number
1187
  for i=1, inventoryMax do   
1188
    slot[i] = {}
1189
    slot[i][2] = turtle.getItemCount(i)
1190
  end
1191
  
1192
  local function iterate(toSet , rawTypes, set)
1193
    for _, a in pairs(getTableOfType(toSet, rawTypes)) do --Get all slots matching type
1194
      slot[a][1] = set --Set official type to "set"
1195
    end
1196
  end
1197
  
1198
  --This assigns "dumb" types to all slots based on comparing, then based on knowledge of dump type slots, changes all slots matching a dump type to one. Otherwise, if the slot contains fuel, it is 2, else 3
1199
  local rawTypes, numTypes = assignTypes(copyTable(initialTypes), initialCount) --This gets increasingly numbered types, copyTable because assignTypes will modify it
1200
  
1201
  for i=1, numTypes do
1202
    if (select(getRep(i, rawTypes)) or true) and turtle.refuel(0) then --Selects the rep slot, checks if it is fuel
1203
      iterate(i, rawTypes, 2) --This type is fuel
1204
    elseif dumpSlots[getRep(i,(oreQuarry and rawTypes) or initialTypes)] then --If the rep of this slot is a dump item. This is initial types so that the rep is in dump slots. rawTypes if oreQuarry to get newly assigned dumps
1205
      iterate(i, rawTypes, 1) --This type is cobble/filler
1206
    else
1207
      iterate(i, rawTypes, 3) --This type is other
1208
    end
1209
  end
1210
    
1211
  for i=1,inventoryMax do
1212
    if specialSlots[i] then --Do nothing!
1213
    elseif slot[i][1] == 1 then totals.cobble = totals.cobble + (slot[i][2] * mod)
1214
    elseif slot[i][1] == 2 then totals.fuel = totals.fuel + (slot[i][2] * mod)
1215
    elseif slot[i][1] == 3 then totals.other = totals.other + (slot[i][2] * mod) end
1216
  end
1217
1218
  select(1)
1219
end
1220
1221
--Mining functions
1222
function dig(doAdd, mineFunc, inspectFunc) --Note, turtle will not bother comparing if not given an inspectFunc
1223
  if doAdd == nil then doAdd = true end
1224
  mineFunc = mineFunc or turtle.dig
1225
  local function retTab(tab) if type(tab) == "table" then return tab end end --Please ignore the stupid one-line trickery. I felt special writing that. (Unless it breaks, then its cool)
1226
    --Mine if not in blacklist. inspectFunc returns success and (table or string) so retTab filters out the string and the extra table prevents errors.
1227
  if not oreQuarry or not inspectFunc or not blacklist[(retTab(({inspectFunc()})[2]) or {name = "none"}).name] then --Will stop at first false, last part won't run if one of first are false
1228
   if mineFunc() then
1229
     if doAdd then
1230
       mined = mined + 1
1231
     end
1232
     return true
1233
   else
1234
     return false
1235
   end
1236
  end
1237
  return true --This only runs if oreQuarry but item not in blacklist
1238
end
1239
1240
--Refuel Functions
1241
function emergencyRefuel()
1242
  local continueEvac = true --This turns false if more fuel is acquired
1243
  if fuelChestEnabled then --This is pretty much the only place that this will be used
1244
    if not fuelChestPhase then --Since I want to do things with return of enderRefuel, I will just make a special system. All of this is for backup safety.
1245
      fuelChestPhase = 0 --Global variable will be saved
1246
      fuelChestProperFacing = facing
1247
    end
1248
    if fuelChestPhase == 0 then
1249
      turnTo(coterminal(fuelChestProperFacing+2))
1250
      dig(false)
1251
      fuelChestPhase = 1
1252
      saveProgress()
1253
    end
1254
    if fuelChestPhase == 1 then
1255
      select(specialSlots.fuelChest)
1256
      turtle.place()
1257
      fuelChestPhase = 2
1258
      saveProgress()
1259
    end
1260
    if fuelChestPhase == 2 then
1261
      if not enderRefuel() then --Returns false if slots are full
1262
        select(specialSlots.fuelChest)
1263
        turtle.drop() --Somehow stuff got in here...
1264
      end
1265
      fuelChestPhase = 3
1266
      saveProgress()
1267
    end
1268
    if fuelChestPhase == 3 then
1269
      select(specialSlots.fuelChest)
1270
      dig(false)
1271
      select(1)
1272
      fuelChestPhase = 4
1273
      saveProgress()
1274
    end
1275
    if fuelChestPhase == 4 then
1276
      turnTo(fuelChestProperFacing)
1277
      fuelChestProperFacing = nil --Getting rid of saved values
1278
      fuelChestPhase = nil
1279
      continueEvac = false
1280
    end
1281
  elseif quadEnabled then --Ask for a quadRotor
1282
    screen()
1283
    print("Attempting an emergency Quad Rotor refuel")
1284
    print("The turtle will soon send a message, then wait ",quadTimeout," seconds before moving on")
1285
    print("Press any key to break timer")
1286
    biometrics(nil, true)
1287
    local timer, counter, event, id, counterID = os.startTimer(quadTimeout), 0
1288
    local startInventory = getSlotsTable()
1289
    repeat
1290
      if id == counterID then counter = counter + 1 end
1291
      counterID = os.startTimer(1)
1292
      screenLine(1,6)
1293
      print("Seconds elapsed: ",counter)
1294
      event, id = os.pullEvent() --Waits for a key or fuel or the timer
1295
    until (event == "timer" and id == timer) or event == "key" or event == "turtle_inventory"
1296
    if event == "turtle_inventory" then --If fuel was actually delivered
1297
      local slot = getFirstChanged(startInventory, getSlotsTable())
1298
      select(slot)
1299
      midRunRefuel(slot)
1300
    end        
1301
  elseif doRefuel then --Attempt an emergency refueling
1302
    screen()
1303
    print("Attempting an emergency refuel")
1304
    print("Fuel Level:    ",checkFuel())
1305
    print("Distance Back: ",(xPos+zPos+yPos+1))
1306
    print("Categorizing Items")
1307
    count(false) --Do not add count, but categorize
1308
    local fuelSwitch, initialFuel = false, checkFuel() --Fuel switch so we don't go over limit (in emergency...)
1309
    print("Going through available fuel slots")
1310
    for i=1, inventoryMax do
1311
      if fuelSwitch then break end
1312
      if turtle.getItemCount(i) > 0 and slot[i][1] == 2 then --If there are items and type 2 (fuel)
1313
        select(i)
1314
        fuelSwitch = midRunRefuel(i) --See above "function drop" for usage
1315
      end
1316
    end
1317
    select(1) --Cleanup
1318
    print("Done fueling")
1319
    if checkFuel() > initialFuel then 
1320
      continueEvac = false
1321
      print("Evac Aborted")
1322
    else
1323
      print("Evac is a go, returning to base")
1324
      sleep(1.5) --Pause for reading
1325
    end
1326
  end
1327
  return continueEvac
1328
end
1329
1330
1331
function digUp(doAdd, ignoreInspect)--Regular functions :) I switch definitions for optimization (I think)
1332
  return dig(doAdd, turtle.digUp, (not ignoreInspect and turtle.inspectUp) or nil)
1333
end
1334
function digDown(doAdd, ignoreInspect)
1335
  return dig(doAdd, turtle.digDown, (not ignoreInspect and turtle.inspectDown) or nil)
1336
end
1337
if inverted then --If inverted, switch the options
1338
  digUp, digDown = digDown, digUp
1339
end
1340
1341
function smartDig(doDigUp, doDigDown) --This function is used only in mine when oldOreQuarry
1342
  if inverted then doDigUp, doDigDown = doDigDown, doDigUp end --Switching for invert
1343
  local blockAbove, blockBelow = doDigUp and turtle.detectUp(), doDigDown and turtle.detectDown() --These control whether or not the turtle digs
1344
  local index = 1
1345
  for i=1, #compareSlots do
1346
    if not (blockAbove or blockBelow) then break end --We don't want to go selecting if there is nothing to dig
1347
    index = i --To access out of scope
1348
    select(compareSlots[i])
1349
    if blockAbove and turtle.compareUp() then blockAbove = false end
1350
    if blockBelow and turtle.compareDown() then blockBelow = false end
1351
  end
1352
  table.insert(compareSlots, 1, table.remove(compareSlots, index)) --This is so the last selected slot is the first slot checked, saving a select call
1353
  if blockAbove then dig(true, turtle.digUp) end
1354
  if blockBelow then dig(true, turtle.digDown) end
1355
end
1356
1357
function relxCalc()
1358
  if layersDone % 2 == 1 then
1359
    relzPos = zPos
1360
  else
1361
    relzPos = (z-zPos) + 1
1362
  end
1363
  if relzPos % 2 == 1 then 
1364
    relxPos = xPos 
1365
  else 
1366
    relxPos = (x-xPos)+1 
1367
  end
1368
  if layersDone % 2 == 0 and z % 2 == 1 then
1369
    relxPos = (x-relxPos)+1
1370
  end
1371
end
1372
function forward(doAdd)
1373
  if doAdd == nil then doAdd = true end
1374
  if turtle.forward() then
1375
    if doAdd then
1376
      moved = moved + 1
1377
    end
1378
    if facing == 0 then
1379
      xPos = xPos + 1
1380
    elseif facing == 1 then
1381
      zPos = zPos + 1
1382
    elseif facing == 2 then
1383
      xPos = xPos - 1
1384
    elseif facing == 3 then
1385
      zPos = zPos - 1
1386
    else
1387
      error("Function forward, facing should be 0 - 3, got "..tostring(facing),2)
1388
    end
1389
    relxCalc()
1390
    return true
1391
  end
1392
  return false
1393
end
1394
function verticalMove(moveFunc, yDiff, digFunc, attackFunc)
1395
  local count = 0
1396
  while not moveFunc() do
1397
    if not digFunc(true, true) then --True True is doAdd, and ignoreInspect
1398
      attackFunc()
1399
      sleep(0.5)
1400
      count = count + 1
1401
      if count > maxTries and yPos > (startY-7) then bedrock() end
1402
    end
1403
  end
1404
  yPos = yDiff + yPos
1405
  saveProgress()
1406
  biometrics()
1407
  return true
1408
end
1409
function up() --Uses other function if inverted
1410
  verticalMove(inverted and turtle.down or turtle.up, -1, digUp, attackUp) --Other functions deal with invert already
1411
end
1412
function down()
1413
  verticalMove(inverted and turtle.up or turtle.down, 1, digDown, attackDown)
1414
end
1415
1416
   
1417
function right(num)
1418
  num = num or 1
1419
  for i=1, num do 
1420
    facing = coterminal(facing+1)
1421
    saveProgress()
1422
    if not goLeftNotRight then turtle.turnRight() --Normally
1423
    else turtle.turnLeft() end --Left Quarry
1424
  end
1425
end
1426
function left(num)
1427
  num = num or 1
1428
  for i=1, num do 
1429
  facing = coterminal(facing-1)
1430
  saveProgress()
1431
  if not goLeftNotRight then turtle.turnLeft() --Normally
1432
  else turtle.turnRight() end --Left Quarry
1433
end
1434
end
1435
  
1436
function attack(doAdd, func)
1437
  doAdd = doAdd or true
1438
  func = func or turtle.attack
1439
  if func() then
1440
    if doAdd then 
1441
      attacked = attacked + 1
1442
    end
1443
    return true
1444
  end
1445
  return false
1446
end
1447
function attackUp(doAdd)
1448
  if inverted then
1449
    return attack(doAdd, turtle.attackDown)
1450
  else
1451
    return attack(doAdd, turtle.attackUp)
1452
  end
1453
end
1454
function attackDown(doAdd)
1455
  if inverted then
1456
    return attack(doAdd, turtle.attackUp)
1457
  else
1458
    return attack(doAdd, turtle.attackDown)
1459
  end
1460
end
1461
1462
function detect(func)
1463
  func = func or turtle.detect
1464
  return func()
1465
end
1466
function detectUp(ignoreInvert)
1467
  if inverted and not ignoreInvert then return detect(turtle.detectDown)
1468
  else return detect(turtle.detectUp) end
1469
end
1470
function detectDown(ignoreInvert)
1471
  if inverted and not ignoreInvert then return detect(turtle.detectUp)
1472
  else return detect(turtle.detectDown) end
1473
end
1474
1475
1476
1477
function mine(doDigDown, doDigUp, outOfPath,doCheckInv) -- Basic Move Forward
1478
  if doCheckInv == nil then doCheckInv = true end
1479
  if doDigDown == nil then doDigDown = true end
1480
  if doDigUp == nil then doDigUp = true end
1481
  if outOfPath == nil then outOfPath = false end
1482
  isInPath = (not outOfPath) --For rednet
1483
  if not outOfPath and (checkFuel() <= xPos + zPos + yPos + 5) then --If the turtle can just barely get back to the start, we need to get it there. We don't want this to activate coming back though...
1484
    local continueEvac = false --It will be set true unless at start
1485
    if xPos ~= 0 then
1486
      continueEvac = emergencyRefuel() --This is a huge list of things to do in an emergency
1487
    end
1488
    if continueEvac then
1489
      eventClear() --Clear any annoying events for evac
1490
      local currPos = yPos
1491
      endingProcedure() --End the program
1492
      print("Turtle ran low on fuel so was brought back to start for you :)\n\nTo resume where you left off, use '-startDown "..tostring(currPos-1).."' when you start")
1493
      error("",0)
1494
    end
1495
  end
1496
  local count = 0
1497
  if not outOfPath then dig() end  --This speeds up the quarry by a decent amount if there are more mineable blocks than air
1498
  while not forward(not outOfPath) do
1499
    sleep(0) --Calls coroutine.yield to prevent errors
1500
    count = count + 1
1501
    if not dig() then
1502
      attack()
1503
    end
1504
    if count > 10 then
1505
      attack()
1506
      sleep(0.2)
1507
    end
1508
    if count > maxTries then
1509
      if checkFuel() == 0 then --Don't worry about inf fuel because I modified this function
1510
        saveProgress({doCheckFuel = true, doRefuel = true})
1511
        os.reboot()
1512
      elseif yPos > (startY-7) and turtle.detect() then --If it is near bedrock
1513
        bedrock()
1514
      else --Otherwise just sleep for a bit to avoid sheeps
1515
        sleep(1)
1516
      end
1517
    end
1518
  end
1519
  checkSanity() --Not kidding... This is necessary
1520
  saveProgress(tab)
1521
1522
  if not oldOreQuarry then
1523
    if doDigUp then--The digging up and down part
1524
      sleep(0) --Calls coroutine.yield
1525
      if not digUp(true) and detectUp() then --This is relative: will dig down first on invert
1526
        if not attackUp() then
1527
          if yPos > (startY-7) then bedrock() end --Checking for bedrock, but respecting user wishes
1528
        end
1529
      end
1530
    end
1531
    if doDigDown then
1532
     digDown(true) --This needs to be absolute as well
1533
    end
1534
  else --If oldQuarry
1535
    smartDig(doDigUp,doDigDown)
1536
  end
1537
  percent = math.ceil(moved/moveVolume*100)
1538
  updateDisplay()
1539
  if doCheckInv and careAboutResources then
1540
    if isFull(inventoryMax-keepOpen) then dropOff() end
1541
  end
1542
  biometrics()
1543
end
1544
--Insanity Checking
1545
function checkSanity()
1546
  if not isInPath then --I don't really care if its not in the path.
1547
    return true
1548
  end
1549
  if not (facing == 0 or facing == 2) and #events == 0 then --If mining and not facing proper direction and not in a turn
1550
    turnTo(0)
1551
    rowCheck = true
1552
  end
1553
  if xPos < 0 or xPos > x or zPos < 0 or zPos > z or yPos < 0 then
1554
    saveProgress()
1555
    print("I have gone outside boundaries, attempting to fix (maybe)")
1556
    if xPos > x then goto(x, zPos, yPos, 2) end --I could do this with some fancy math, but this is much easier
1557
    if xPos < 0 then goto(1, zPos, yPos, 0) end
1558
    if zPos > z then goto(xPos, z, yPos, 3) end
1559
    if zPos < 0 then goto(xPos, 1, yPos, 1) end
1560
    relxCalc() --Get relxPos properly
1561
    eventClear()
1562
    
1563
    --[[
1564
    print("Oops. Detected that quarry was outside of predefined boundaries.")
1565
    print("Please go to my forum thread and report this with a short description of what happened")
1566
    print("If you could also run \"pastebin put Civil_Quarry_Restore\" and give me that code it would be great")
1567
    error("",0)]]
1568
  end
1569
end
1570
1571
local function fromBoolean(input) --Like a calculator
1572
if input then return 1 end
1573
return 0
1574
end
1575
local function multBoolean(first,second) --Boolean multiplication
1576
return (fromBoolean(first) * fromBoolean(second)) == 1
1577
end
1578
function coterminal(num, limit) --I knew this would come in handy :D
1579
limit = limit or 4 --This is for facing
1580
return math.abs((limit*fromBoolean(num < 0))-(math.abs(num)%limit))
1581
end
1582
if tArgs["-manualpos"] then
1583
  facing = coterminal(facing) --Done to improve support for "-manualPos"
1584
  if facing == 0 then rowCheck = true elseif facing == 2 then rowCheck = false end --Ditto
1585
  relxCalc() --Ditto
1586
end
1587
1588
--Direction: Front = 0, Right = 1, Back = 2, Left = 3
1589
function turnTo(num)
1590
  num = num or facing
1591
  num = coterminal(num) --Prevent errors
1592
  local turnRight = true
1593
  if facing-num == 1 or facing-num == -3 then turnRight = false end --0 - 1 = -3, 1 - 0 = 1, 2 - 1 = 1
1594
  while facing ~= num do          --The above is used to smartly turn
1595
    if turnRight then
1596
      right()
1597
    else 
1598
      left() 
1599
    end
1600
  end
1601
end
1602
function goto(x,z,y, toFace, destination, updateStatus)
1603
  --Will first go to desired z pos, then x pos, y pos varies
1604
  x = x or 1; y = y or 1; z = z or 1; toFace = toFace or facing
1605
  gotoDest = destination or "" --This is used by biometrics.
1606
  statusString = "Going somewhere"
1607
  --Possible destinations: layerStart, quarryStart
1608
  if yPos > y then --Will go up first if below position
1609
    while yPos~=y do up() end
1610
  end
1611
  if zPos > z then
1612
    turnTo(3)
1613
  elseif zPos < z then 
1614
    turnTo(1)
1615
  end
1616
  while zPos ~= z do mine(false,false,true,false) end
1617
  if xPos > x then
1618
    turnTo(2)
1619
  elseif xPos < x then
1620
    turnTo(0)
1621
  end
1622
  while xPos ~= x do mine(false,false,true,false) end
1623
  if yPos < y then --Will go down after if above position
1624
    while yPos~=y do down() end
1625
  end
1626
  turnTo(toFace)
1627
  saveProgress()
1628
  gotoDest = ""
1629
  statusString = nil
1630
end
1631
function getNumOpenSlots()
1632
  local toRet = 0
1633
  for i=1, inventoryMax do
1634
    if turtle.getItemCount(i) == 0 then
1635
      toRet = toRet + 1
1636
    end
1637
  end
1638
  return toRet
1639
end
1640
1641
--Ideas: Bring in inventory change-checking functions, count blocks that have been put in, so it will wait until all blocks have been put in.
1642
local function waitDrop(slot, allowed, whereDrop) --This will just drop, but wait if it can't
1643
  allowed = allowed or 0
1644
  while turtle.getItemCount(slot) > allowed do --No more half items stuck in slot!
1645
    local tries = 1
1646
    while not whereDrop(turtle.getItemCount(slot)-allowed) do --Drop off only the amount needed
1647
      screen(1,1)
1648
      print("Chest Full, Try "..tries)
1649
      chestFull = true
1650
      biometrics()--To send that the chest is full
1651
      tries = tries + 1
1652
      sleep(2)
1653
    end
1654
    chestFull = false
1655
  end
1656
end
1657
1658
function midRunRefuel(i, allowed)
1659
  allowed = allowed or allowedItems[i]
1660
  local numToRefuel = turtle.getItemCount(i)-allowed
1661
  if checkFuel() >= checkFuelLimit() then return true end --If it doesn't need fuel, then signal to not take more
1662
  local firstCheck = checkFuel()
1663
  if numToRefuel > 0 then turtle.refuel(1) end --This is so we can see how many fuel we need.
1664
  local singleFuel
1665
  if checkFuel() - firstCheck > 0 then singleFuel = checkFuel() - firstCheck else singleFuel = math.huge end --If fuel is 0, we want it to be huge so the below will result in 0 being taken
1666
  --Refuel      The lesser of   max allowable or         remaining fuel space         /    either inf or a single fuel (which can be 0)
1667
  turtle.refuel(math.min(numToRefuel-1, math.ceil((checkFuelLimit()-checkFuel()) / singleFuel))) --The refueling part of the the doRefuel option
1668
  if checkFuel() >= checkFuelLimit() then return true end --Do not need any more fuel
1669
  return false --Turtle can still be fueled
1670
end
1671
1672
function enderRefuel() --Assumes a) An enderchest is in front of it b) It needs fuel
1673
  local slot
1674
  for a,b in ipairs(getSlotsTable()) do
1675
    if b == 0 then slot = a; break end
1676
  end
1677
  if not slot then return false end --No room for fueling
1678
  select(slot)
1679
  repeat
1680
    print("Required Fuel: ",excessFuelAmount)
1681
    print("Current Fuel: ",checkFuel())
1682
    local tries = 0
1683
    while not turtle.suck() do
1684
      sleep(1)
1685
      statusString = "No Fuel in Ender Chest"
1686
      biometrics() --Let user know that fuel chest is empty
1687
      print(statusString,". Try: ",tries)
1688
      tries = tries + 1
1689
    end
1690
    statusString = nil
1691
  until midRunRefuel(slot, 0) --Returns true when should not refuel any more
1692
  if not turtle.drop() then turtle.dropDown() end --If cannot put fuel back, just drop it, full fuel chest = user has too much fuel already
1693
  return true -- :D
1694
end
1695
1696
  
1697
function drop(side, final)
1698
  side = sides[side] or "front"
1699
  local dropFunc, detectFunc, dropFacing = turtle.drop, turtle.detect, facing+2
1700
  if side == "top" then dropFunc, detectFunc = turtle.dropUp, turtle.detectUp end
1701
  if side == "bottom" then dropFunc, detectFunc = turtle.dropDown, turtle.detectDown end
1702
  if side == "right" then turnTo(1); dropFacing = 0 end
1703
  if side == "left" then turnTo(3); dropFacing = 0 end
1704
  local properFacing = facing --Capture the proper direction to be facing
1705
  
1706
  count(true) --Count number of items before drop. True means add. This is before chest detect, because could be final
1707
  
1708
  while not detectFunc() do 
1709
    if final then return end --If final, we don't need a chest to be placed, but there can be
1710
    chestFull = true
1711
    biometrics() --Let the user know there is a problem with chest
1712
    screen(1,1) --Clear screen
1713
    print("Waiting for chest placement on ",side," side (when facing quarry)")
1714
    sleep(2)
1715
  end
1716
  chestFull = false
1717
  
1718
  local fuelSwitch = false --If doRefuel, this can switch so it won't overfuel
1719
  for i=1,inventoryMax do
1720
    --if final then allowedItems[i] = 0 end --0 items allowed in all slots if final ----It is already set to 1, so just remove comment if want change
1721
    if turtle.getItemCount(i) > 0 then --Saves time, stops bugs
1722
      if slot[i][1] == 1 and dumpCompareItems then turnTo(dropFacing) --Turn around to drop junk, not store it. dumpComapareItems is global config
1723
      else turnTo(properFacing) --Turn back to proper position... or do nothing if already there
1724
      end 
1725
      select(i)
1726
      if doRefuel and slot[i][1] == 2 then --Intelligently refuels to fuel limit
1727
        if not fuelSwitch then --Not in the conditional because we don't want to waitDrop excess fuel. Not a break so we can drop junk
1728
          fuelSwitch = midRunRefuel(i)
1729
        end
1730
      else 
1731
        waitDrop(i, allowedItems[i], dropFunc)
1732
      end
1733
    end
1734
  end
1735
  
1736
  if oldOreQuarry then count(nil) end--Subtract the items still there if oreQuarry
1737
  resetDumpSlots() --So that slots gone aren't counted as dump slots next
1738
  
1739
  select(1) --For fanciness sake
1740
1741
end
1742
1743
function dropOff() --Not local because called in mine()
1744
  local currX,currZ,currY,currFacing = xPos, zPos, yPos, facing
1745
  if careAboutResources then
1746
    if not enderChestEnabled then --Regularly
1747
      eventAdd("goto", 1,1,currY,2) --Need this step for "-startDown"
1748
      eventAdd("goto(0,1,1,2)")
1749
      eventAdd("drop", dropSide,false)
1750
      eventAdd("turnTo(0)")
1751
      eventAdd("mine",false,false,true,false)
1752
      eventAdd("goto(1,1,1, 0)")
1753
      eventAdd("goto", 1, 1, currY, 0)
1754
      eventAdd("goto", currX,currZ,currY,currFacing)
1755
    else --If using an enderChest
1756
      if turtle.getItemCount(specialSlots.enderChest) ~= 1 then eventAdd("promptSpecialSlot('enderChest','Ender Chest')") end
1757
      eventAdd("turnTo",currFacing-2)
1758
      eventAdd("dig",false)
1759
      eventAdd("select",specialSlots.enderChest)
1760
      eventAdd("turtle.place")
1761
      eventAdd("drop","front",false)
1762
      eventAdd("turnTo",currFacing-2)
1763
      eventAdd("select", specialSlots.enderChest)
1764
      eventAdd("dig",false)
1765
      eventAdd("turnTo",currFacing)
1766
      eventAdd("select(1)")
1767
    end
1768
    runAllEvents()
1769
    numDropOffs = numDropOffs + 1 --Analytics tracking
1770
  end
1771
  return true
1772
end
1773
function endingProcedure() --Used both at the end and in "biometrics"
1774
  eventAdd("goto",1,1,yPos,2,"quarryStart") --Allows for startDown variable
1775
  eventAdd("goto",0,1,1,2, "quarryStart") --Go back to base
1776
  runAllEvents()
1777
  --Output to a chest or sit there
1778
  if enderChestEnabled then
1779
    if dropSide == "right" then eventAdd("turnTo(1)") end --Turn to proper drop side
1780
    if dropSide == "left" then eventAdd("turnTo(3)") end
1781
    eventAdd("dig(false)") --This gets rid of a block in front of the turtle.
1782
    eventAdd("select",specialSlots.enderChest)
1783
    eventAdd("turtle.place")
1784
    eventAdd("select(1)")
1785
  end
1786
  eventAdd("drop",dropSide, true)
1787
  eventAdd("turnTo(0)")
1788
1789
  --Display was moved above to be used in bedrock function
1790
  eventAdd("display")
1791
  --Log current mining run
1792
  eventAdd("logMiningRun",logExtension)
1793
  toQuit = true --I'll use this flag to clean up (legacy)
1794
  runAllEvents()
1795
end
1796
function bedrock()
1797
  foundBedrock = true --Let everyone know
1798
  if rednetEnabled then biometrics() end
1799
  if checkFuel() == 0 then error("No Fuel",0) end
1800
  local origin = {x = xPos, y = yPos, z = zPos}
1801
  print("Bedrock Detected")
1802
  if turtle.detectUp() and not turtle.digUp() then
1803
    print("Block Above")
1804
    local var
1805
    if facing == 0 then var = 2 elseif facing == 2 then var = 0 else error("Was facing left or right on bedrock") end
1806
    goto(xPos,zPos,yPos,var)
1807
    for i=1, relxPos do mine(false, false); end
1808
  else
1809
    up() --Go up two to avoid any bedrock. 
1810
    up()
1811
  end
1812
  eventClear() --Get rid of any excess events that may be run. Don't want that.
1813
  endingProcedure()
1814
  print("\nFound bedrock at these coordinates: ")
1815
  print(origin.x," Was position in row\n",origin.z," Was row in layer\n",origin.y," Blocks down from start")
1816
  error("",0)
1817
end
1818
1819
function endOfRowTurn(startZ, wasFacing, mineFunctionTable)
1820
local halfFacing = ((layersDone % 2 == 1) and 1) or 3
1821
local toFace = coterminal(wasFacing + 2) --Opposite side
1822
if zPos == startZ then
1823
  if facing ~= halfFacing then turnTo(halfFacing) end
1824
  mine(unpack(mineFunctionTable or {}))
1825
end
1826
if facing ~= toFace then
1827
  turnTo(toFace)
1828
end
1829
end
1830
1831
1832
-------------------------------------------------------------------------------------
1833
--Pre-Mining Stuff dealing with session persistence
1834
runAllEvents()
1835
if toQuit then error("",0) end --This means that it was stopped coming for its last drop
1836
1837
local doDigDown, doDigUp = (lastHeight ~= 1), (lastHeight == 0) --Used in lastHeight
1838
if not restoreFoundSwitch then --Regularly
1839
  --Check if it is a mining turtle
1840
  if not isMiningTurtle then
1841
    local a, b = turtle.dig()
1842
    if a then 
1843
      mined = mined + 1
1844
      isMiningTurtle = true
1845
    elseif b == "Nothing to dig with" or b == "No tool to dig with" then 
1846
      print("This is not a mining turtle. To make a mining turtle, craft me together with a diamond pickaxe")
1847
      error("",0)
1848
    end
1849
  end
1850
  mine(false,false,true) --Get into quarry by going forward one
1851
  if gpsEnabled and not restoreFoundSwitch then --The initial locate is done in the arguments. This is so I can figure out what quadrant the turtle is in.
1852
    gpsSecondPos = {gps.locate(gpsTimeout)} --Note: Does not run this if it has already been restarted.
1853
  end
1854
  for i = 1, startDown do
1855
    eventAdd("down") --Add a bunch of down events to get to where it needs to be.
1856
  end
1857
  runAllEvents()
1858
  if flatBedrock then
1859
    while (detectDown() and digDown(false, true)) or not detectDown() do --None of these functions are non-invert protected because inverse always false here
1860
      down()
1861
      startDown = startDown + 1
1862
    end
1863
    startDown = startDown - y + 1
1864
    for i=1, y-2 do
1865
      up() --It has hit bedrock, now go back up for proper 3 wide mining
1866
    end
1867
  elseif not(y == 1 or y == 2) then
1868
    down() --Go down to align properly. If y is one or two, it doesn't need to do this.
1869
  end
1870
else --restore found
1871
  if not(layersDone == layers and not doDigDown) then digDown() end
1872
  if not(layersDone == layers and not doDigUp) then digUp() end  --Get blocks missed before stopped
1873
end
1874
--Mining Loops--------------------------------------------------------------------------
1875
select(1)
1876
while layersDone <= layers do -------------Height---------
1877
local lastLayer = layersDone == layers --If this is the last layer
1878
local secondToLastLayer = (layersDone + 1) == layers --This is a check for going down at the end of a layer.
1879
moved = moved + 1 --To account for the first position in row as "moved"
1880
if not(layersDone == layers and not doDigDown) then digDown() end --This is because it doesn't mine first block in layer
1881
if not restoreFoundSwitch and layersDone % 2 == 1 then rowCheck = true end
1882
relxCalc()
1883
while relzPos <= z do -------------Width----------
1884
while relxPos < x do ------------Length---------
1885
mine(not lastLayer or (doDigDown and lastLayer), not lastLayer or (doDigUp and lastLayer)) --This will be the idiom that I use for the mine function
1886
end ---------------Length End-------
1887
if relzPos ~= z then --If not on last row of section
1888
  local func
1889
  if rowCheck == true then --Switching to next row
1890
  func = "right"; rowCheck = false; else func = false; rowCheck = true end --Which way to turn
1891
    eventAdd("endOfRowTurn", zPos, facing , {not lastLayer or (doDigDown and lastLayer), not lastLayer or (doDigUp and lastLayer)}) --The table is passed to the mine function
1892
    runAllEvents()
1893
else break
1894
end
1895
end ---------------Width End--------
1896
if layersDone % 2 == 0 then --Will only go back to start on non-even layers
1897
  eventAdd("goto",1,1,yPos,0, "layerStart") --Goto start of layer
1898
else
1899
  eventAdd("turnTo",coterminal(facing-2))
1900
end
1901
if not lastLayer then --If there is another layer
1902
  for i=1, 2+fromBoolean(not(lastHeight~=0 and secondToLastLayer)) do eventAdd("down()") end --The fromBoolean stuff means that if lastheight is 1 and last and layer, will only go down two
1903
end
1904
eventAdd("relxCalc")
1905
layersDone = layersDone + 1
1906
restoreFoundSwitch = false --This is done so that rowCheck works properly upon restore
1907
runAllEvents()
1908
end ---------------Height End-------
1909
1910
endingProcedure() --This takes care of getting to start, dropping in chest, and displaying ending screen