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