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