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