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