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