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