· 4 years ago · May 25, 2021, 09:38 AM
1local tArgs = {...}
2
3function DigRoom(size_x,size_y,size_z,invtd)
4 --[[
5 Version 3.3.3
6 Recent Changes:
7 Fixed invert. Now works
8 Fixed GPS. Now works
9 Fixed bug stopping rednet from working.
10 New Internals: Made event system and consolidated mining loops
11 New Restarting Logic!
12 As long as position is recorded properly, it should always restart properly
13 New Argument logic!
14 Arguments are much more modular for me to make, and they are no longer case-sensitive. Improved items changed interface
15 New Arguments!
16 -enderChest [t/f or slot number] You can now use an enderchest! Try it out. There are directions
17 -startDown [number] The quarry will now start this many blocks down from its starting point (or up if invert). Great for getting diamonds and things from surface. Chest should still be placed on the top (or use ender chest)
18 -manualPos [xPos] [zPos] [yPos] [facing] Turtle ever gotten it's position wrong, but you don't want to reset? Use this! Check "quarry -help" for changes
19 Renamed several arguments
20 Arguments are no longer ignored if you use "-default", you just won't be prompted for anything.
21 Boolean (t/f) arguments now accept "yes" as well as "true"
22 Fixed small issue in GPS location that may have broken it.
23 ]]
24 --Defining things
25 civilTable = nil; _G.civilTable = {}; setmetatable(civilTable, {__index = _G}); setfenv(1,civilTable)
26 -------Defaults for Arguments----------
27 --Arguments assignable by text
28 x,y,z = size_x,size_y,size_z --These are just in case tonumber fails
29 inverted = (invtd == 'true') --False goes from top down, true goes from bottom up [Default false]
30 rednetEnabled = true --Default rednet on or off [Default false]
31 --Arguments assignable by tArgs
32 dropSide = "front" --Side it will eject to when full or done [Default "front"]
33 careAboutResources = true --Will not stop mining once inventory full if false [Default true]
34 doCheckFuel = true --Perform fuel check [Default true]
35 doRefuel = true --Whenever it comes to start location will attempt to refuel from inventory [Default false]
36 invCheckFreq = 10 --Will check for inventory full every <-- moved spaces [Default 10]
37 keepOpen = 1 --How many inventory slots it will attempt to keep open at all times [Default 1]
38 fuelSafety = "moderate" --How much fuel it will ask for: safe, moderate, and loose [Default moderate]
39 saveFile = "Civil_Quarry_Restore" --Where it saves restore data [Default "Civil_Quarry_Restore"]
40 doBackup = true --If it will keep backups for session persistence [Default true]
41 numberOfStacksPerRun = 8 --How many stacks (number of items) the turtle expects (on average) to have before it must dump off. Not in arguments. [Default 8]
42 gpsEnabled = false -- If option is enabled, will attempt to find position via GPS api [Default false]
43 gpsTimeout = 3 --The number of seconds the program will wait to get GPS coords. Not in arguments [Default 3]
44 logging = true --Whether or not the turtle will log mining runs. [Default ...still deciding]
45 logFolder = "Quarry_Logs" --What folder the turtle will store logs in [Default "Quarry_Logs"]
46 logExtension = "" --The extension of the file (e.g. ".txt") [Default ""]
47 startDown = 0 --How many blocks to start down from the top of the mine [Default 0]
48 enderChestEnabled = false --Whether or not to use an ender chest [Default false]
49 enderChestSlot = 16 --What slot to put the ender chest in [Default 16]
50 --Standard number slots for fuel (you shouldn't care)
51 fuelTable = { --Will add in this amount of fuel to requirement.
52 safe = 1000,
53 moderate = 200,
54 loose = 0
55 } --Default 1000, 200, 0
56 --Standard rednet channels
57 channels = {
58 send = 128 ,
59 receive = 129 ,
60 confirm = "Confirm"
61 }
62
63
64 local supportsRednet = (peripheral.wrap("right") ~= nil)
65
66 --You don't care about these
67 xPos,yPos,zPos,facing,percent,mined,moved,relxPos, rowCheck, connected, isInPath, layersDone, attacked, startY
68 = 0, 1, 1, 0, 0, 0, 0, 1, "right", false, true, 1, 0, 0
69
70 local totals = {cobble = 0, fuel = 0, other = 0} -- Total for display (cannot go inside function)
71 local function count() --Done any time inventory dropped and at end
72 slot = {} --1: Cobble 2: Fuel 3:Other
73 for i=1, 16 do --[1] is type, [2] is number
74 slot[i] = {}
75 slot[i][2] = turtle.getItemCount(i)
76 end
77 slot[1][1] = 1 -- = Assumes Cobble/Main
78 for i=1, 16 do --Cobble Check
79 turtle.select(i)
80 if turtle.compareTo(1) then
81 slot[i][1] = 1
82 totals.cobble = totals.cobble + slot[i][2]
83 elseif turtle.refuel(0) then
84 slot[i][1] = 2
85 totals.fuel = totals.fuel + slot[i][2]
86 else
87 slot[i][1] = 3
88 totals.other = totals.other + slot[i][2]
89 end
90 end
91 turtle.select(1)
92 end
93
94 local getFuel = turtle.getFuelLevel --This is for cleanup at the end
95 do --Common variable name...
96 local flag = turtle.getFuelLevel() == "unlimited"--Unlimited screws up my calculations
97 if flag then --Fuel is disabled
98 turtle.getFuelLevel = function() return math.huge end --Infinite Fuel
99 end --There is no "else" because it will already return the regular getFuel
100 end
101 local checkFuel = turtle.getFuelLevel --Just an alias for backwards compat
102
103 -----------------------------------------------------------------
104 --Input Phase
105 local function screen(xPos,yPos)
106 xPos, yPos = xPos or 1, yPos or 1
107 term.setCursorPos(xPos,yPos); term.clear(); end
108 local function screenLine(xPos,yPos)
109 term.setCursorPos(xPos,yPos); term.clearLine(); end
110
111 screen(1,1)
112 print("----- Welcome to Quarry! -----")
113 print("")
114
115 local sides = {top = "top", right = "right", left = "left", bottom = "bottom", front = "front"} --Used to whitelist sides
116 local changedT, tArgsWithUpper = {}, {}
117 changedT.new = function(key, value) table.insert(changedT,{key, value}) end --Numeric list of lists
118 local function capitalize(text) return (string.upper(string.sub(text,1,1))..string.sub(text,2,-1)) end
119 for 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
120
121 local restoreFound, restoreFoundSwitch = false --Initializing so they are in scope
122 function 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.
123 if trigger == nil then trigger = true end --Defaults to being able to run
124 if not trigger then return end --This is what the trigger is for. Will not run if trigger not there
125 if restoreFoundSwitch or tArgs["-default"] then forcePrompt = false end --Don't want to prompt if these
126 local toGetText = name:lower() --Because all params are now lowered
127 local formatType = formatString:match("^%a+"):lower() or error("Format String Unknown: "..formatString) --Type of format string
128 local args = formatString:sub(({formatString:find(formatType)})[2] + 2).."" --Everything in formatString but the type and space
129 local variable = variableOverride or name --Goes first to the override for name
130 local func = loadstring("return "..variable)
131 setfenv(func,getfenv(1))
132 local originalValue = assert(func)() --This is the default value, for checking to add to changed table
133 if originalValue == nil then error("From addParam, \""..variable.."\" returned nil",2) end --I may have gotten a wrong variable name
134 local givenValue, toRet --Initializing for use
135 if tArgs["-"..toGetText] then
136 givenValue = tArgsWithUpper[tArgs["-"..toGetText]+1] --This is the value after the desired parameter
137 elseif forcePrompt then
138 write(displayText.."? ")
139 givenValue = io.read()
140 end
141 if formatType == "force" then --This is the one exception. Should return true if givenValue is nothing
142 toRet = (tArgs["-"..toGetText] and true) or false --Will return true if param exists, otherwise false
143 end
144 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"
145 if formatType == "boolean" then --All the format strings will be basically be put through a switch statement
146 toRet = givenValue:sub(1,1):lower() == "y" or givenValue:sub(1,1):lower() == "t" --Accepts true or yes
147 if formatString == "boolean special" then
148 toRet = givenValue:sub(1,1):lower() ~= "n" and givenValue:sub(1,1):lower() ~= "f" --Accepts anything but false or no
149 end
150 elseif formatType == "string" then
151 toRet = givenValue:match("^[%w%.]+") --Basically anything not a space or control character etc
152 elseif formatType == "number" then
153 toRet = tonumber(givenValue) --Note this is a local, not the above so we don't change anything
154 if not toRet then return end --We need a number... Otherwise compare errors
155 toRet = math.abs(math.floor(toRet)) --Get proper integers
156 local startNum, endNum = formatString:match("(%d+)%-(%d+)") --Gets range of numbers
157 startNum, endNum = tonumber(startNum), tonumber(endNum)
158 if not ((toRet >= startNum) and (toRet <= endNum)) then return end --Can't use these
159 elseif formatType == "side" then
160 local exclusionTab = {} --Ignore the wizardry here. Just getting arguments without format string
161 for a in args:gmatch("%S+") do exclusionTab[a] = true end --This makes a list of the sides to not include
162 if not exclusionTab[givenValue] then toRet = sides[givenValue] end --If side is not excluded
163 elseif formatType == "list" then
164 toRet = {}
165 for a in args:gmatch("[^,]") do
166 table.insert(toRet,a)
167 end
168 elseif formatType == "force" then --Do nothing, everything is already done
169 else error("Improper formatType",2)
170 end
171 if toRet == nil then return end --Don't want to set variables to nil... That's bad
172 tempParam = toRet --This is what loadstring will see :D
173 local func = loadstring(variable.." = tempParam")
174 setfenv(func, getfenv(1))
175 func()
176 tempParam = nil --Cleanup of global
177 if toRet ~= originalValue then
178 changedT.new(displayText, tostring(toRet))
179 end
180 return toRet
181 end
182
183 --Check if it is a turtle
184 if not(turtle or tArgs["help"] or tArgs["-help"] or tArgs["-?"] or tArgs["?"]) then
185 print("This is not a turtle, you might be looking for the \"Companion Rednet Program\" \nCheck My forum thread for that")
186 print("Press 'q' to quit, or any other key to start help ")
187 if ({os.pullEvent("char")})[2] ~= "q" then tArgs.help = true else error("",0) end
188 end
189
190
191 if tArgs["help"] or tArgs["-help"] or tArgs["-?"] or tArgs["?"] then
192 print("You have selected help, press any key to continue"); print("Use arrow keys to naviate, q to quit"); os.pullEvent("key")
193 local pos = 1
194 local key = 0
195 while pos <= #help and key ~= keys.q do
196 if pos < 1 then pos = 1 end
197 screen(1,1)
198 print(help[pos].title)
199 for a=1, #help[pos] do print(help[pos][a]) end
200 repeat
201 _, key = os.pullEvent("key")
202 until key == 200 or key == 208 or key == keys.q
203 if key == 200 then pos = pos - 1 end
204 if key == 208 then pos = pos + 1 end
205 end
206 error("",0)
207 end
208
209 --Saving
210 addParam("doBackup", "Backup Save File", "boolean")
211 addParam("saveFile", "Save File Name", "string")
212
213 restoreFound = fs.exists(saveFile)
214 restoreFoundSwitch = (tArgs["-restore"] or tArgs["-resume"]) and restoreFound
215 if restoreFoundSwitch then
216 local file = fs.open(saveFile,"r")
217 local test = file.readAll() ~= ""
218 file.close()
219 if test then
220 os.run(getfenv(1),saveFile)
221 if gpsEnabled then --If it had saved gps coordinates
222 print("Found GPS Start Coordinates")
223 local currLoc = {gps.locate(gpsTimeout)} or {}
224 if #currLoc > 0 and #gpsStartPos > 0 and #gpsSecondPos > 0 then --Cover all the different positions I'm using
225 print("GPS Position Successfully Read")
226 if currLoc[1] == gpsStartPos[1] and currLoc[3] == gpsStartPos[3] then --X coord, y coord, z coord in that order
227 xPos, yPos, zPos = 0,1,1
228 if facing ~= 0 then turnTo(0) end
229 print("Is at start")
230 else
231 if inverted then --yPos setting
232 ------------------------------------------------FIX THIS
233 end
234 local function copyTable(tab) local toRet = {}; for a, b in pairs(tab) do toRet[a] = b end; return toRet end
235 local a, b = copyTable(gpsStartPos), copyTable(gpsSecondPos) --For convenience
236 if b[3] - a[3] == -1 then--If went north (-Z)
237 a[1] = a[1] - 1 --Shift x one to west to create a "zero"
238 xPos, zPos = -currLoc[3] + a[3], currLoc[1] + -a[1]
239 elseif b[1] - a[1] == 1 then--If went east (+X)
240 a[3] = a[3] - 1 --Shift z up one to north to create a "zero"
241 xPos, zPos = currLoc[1] + -a[1], currLoc[3] + -a[3]
242 elseif b[3] - a[3] == 1 then--If went south (+Z)
243 a[1] = a[1] + 1 --Shift x one to east to create a "zero"
244 xPos, zPos = currLoc[3] + a[3], -currLoc[1] + a[3]
245 elseif b[1] - a[1] == -1 then--If went west (-X)
246 a[3] = a[3] + 1 --Shift z down one to south to create a "zero"
247 xPos, zPos = -currLoc[1] + a[1], -currLoc[3] + a[3]
248 else
249 print("Improper Coordinates")
250 print("GPS Locate Failed, Using Standard Methods") ----Maybe clean this up a bit to use flags instead.
251 end
252 end
253 print("X Pos: ",xPos)
254 print("Y Pos: ",yPos)
255 print("Z Pos: ",zPos)
256 print("Facing: ",facing)
257 else
258 print("GPS Locate Failed, Using Standard Methods")
259 end
260 print("Restore File read successfully. Starting in 3"); sleep(3)
261 end
262 else
263 fs.delete(saveFile)
264 print("Restore file was empty, sorry, aborting")
265 error("",0)
266 end
267 else --If turtle is just starting
268 events = {} --This is the event queue :D
269 originalFuel = checkFuel() --For use in logging. To see how much fuel is REALLY used
270 end
271 --Dimesnions
272 --[[
273 if tArgs["-dim"] then local num = tArgs["-dim"];
274 x = tonumber(tArgs[num + 1]) or x; z = tonumber(tArgs[num + 2]) or z; y = tonumber(tArgs[num + 3]) or y
275 elseif not (tArgs["-default"] or restoreFoundSwitch) then
276 print("What dimensions?")
277 print("")
278 --This will protect from negatives, letters, and decimals
279 term.write("Length? ")
280 x = math.floor(math.abs(tonumber(io.read()) or x))
281 term.write("Width? ")
282 z = math.floor(math.abs(tonumber(io.read()) or z))
283 term.write("Height? ")
284 y = math.floor(math.abs(tonumber(io.read()) or y))
285 changedT.new("Length",x); changedT.new("Width",z); changedT.new("Height",y)
286 end
287 -]]
288 --Invert
289 --addParam("invert", "Inverted","boolean", true, nil, "inverted")
290 --addParam("startDown","Start Down","number 1-256")
291 --Inventory
292 addParam("chest", "Chest Drop Side", "side front", nil, nil, "dropSide")
293 addParam("enderChest","Ender Chest Enabled","boolean special", nil, nil, "enderChestEnabled") --This will accept anything (including numbers) thats not "f" or "n"
294 addParam("enderChest", "Ender Chest Slot", "number 1-16", nil, nil, "enderChestSlot") --This will get the number slot if given
295 --Rednet
296 addParam("rednet", "Rednet Enabled","boolean",true, supportsRednet, "rednetEnabled")
297 addParam("gps", "GPS Location Services", "force", nil, (not restoreFoundSwitch) and supportsRednet, "gpsEnabled" ) --Has these triggers so that does not record position if restarted.
298 if gpsEnabled and not restoreFoundSwitch then
299 gpsStartPos = {gps.locate(gpsTimeout)} --Stores position in array
300 gpsEnabled = #gpsStartPos > 0 --Checks if location received properly. If not, position is not saved
301 end
302 addParam("sendChannel", "Rednet Send Channel", "number 1-65535", false, supportsRednet, "channels.send")
303 addParam("receiveChannel","Rednet Receive Channel", "number 1-65535", false, supportsRednet, "channels.receive")
304 --Fuel
305 addParam("doRefuel", "Refuel from Inventory","boolean", nil, turtle.getFuelLevel() ~= math.huge) --math.huge due to my changes
306 addParam("doCheckFuel", "Check Fuel", "boolean", nil, turtle.getFuelLevel() ~= math.huge)
307 --Logging
308 addParam("logging", "Logging", "boolean")
309 addParam("logFolder", "Log Folder", "string")
310 addParam("logExtension","Log Extension", "string")
311 --Misc
312 addParam("startY", "Start Y","number 1-256")
313 addParam("invCheckFreq","Inventory Check Frequency","number 1-342")
314 addParam("keepOpen", "Slots to Keep Open", "number 1-15")
315 addParam("careAboutResources", "Care About Resources","boolean")
316 --Manual Position
317 if tArgs["-manualpos"] then --Gives current coordinates in xPos,zPos,yPos, facing
318 local a = tArgs["-manualpos"]
319 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
320 changedT.new("xPos",xPos); changedT.new("zPos",zPos); changedT.new("yPos",yPos); changedT.new("facing",facing)
321 restoreFoundSwitch = true --So it doesn't do beginning of quarry behavior
322 end
323
324 local function saveProgress(extras) --Session persistence
325 exclusions = { modem = true, }
326 if doBackup then
327 local toWrite = ""
328 for a,b in pairs(getfenv(1)) do
329 if not exclusions[a] then
330 --print(a ," ", b, " ", type(b)) --Debug
331 if type(b) == "string" then b = "\""..b.."\"" end
332 if type(b) == "table" then b = textutils.serialize(b) end
333 if type(b) ~= "function" then
334 toWrite = toWrite..a.." = "..tostring(b).."\n"
335 end
336 end
337 end
338 toWrite = toWrite.."doCheckFuel = false\n" --It has already used fuel, so calculation unnesesary
339 local file
340 repeat
341 file = fs.open(saveFile,"w")
342 until file --WHY DOES IT SAY ATTEMPT TO INDEX NIL!!!
343 file.write(toWrite)
344 if type(extras) == "table" then
345 for a, b in pairs(extras) do
346 file.write(a.." = "..tostring(b))
347 end
348 end
349 file.close()
350 end
351 end
352
353 local area = x*z
354 local volume = x*y*z
355 local lastHeight = y%3
356 layers = math.ceil(y/3)
357 local yMult = layers --This is basically a smart y/3 for movement
358 local moveVolume = (area * yMult) --Kept for display percent
359 --Calculating Needed Fuel--
360 local exStack = numberOfStacksPerRun --Expected stacks of items before full
361 neededFuel = yMult * x * z + --This is volume it will run through
362 (startDown + y)*(2+1/(64*exStack)) + --This is simplified and includes the y to get up times up and down + how many times it will drop stuff off
363 (x+z)*(yMult + 1/(64*exStack)) --Simplified as well: It is getting to start of row plus getting to start of row how many times will drop off stuff
364 --Original equation: x*z*y/3 + y * 2 + (x+z) * y/3 + (x + z + y) * (1/64*8)
365 neededFuel = math.ceil(neededFuel)
366
367 --Getting Fuel
368 if doCheckFuel and checkFuel() < neededFuel then
369 neededFuel = neededFuel + fuelTable[fuelSafety] --For safety
370 print("Not enough fuel")
371 print("Current: ",checkFuel()," Needed: ",neededFuel)
372 print("Starting SmartFuel...")
373 sleep(2) --So they can read everything.
374 term.clear()
375 local oneFuel, neededFuelItems
376 local currSlot = 0
377 local function output(text, x, y) --For displaying fuel
378 local currX, currY = term.getCursorPos()
379 term.setCursorPos(x,y)
380 term.clearLine()
381 term.write(text)
382 term.setCursorPos(currX,currY)
383 end
384 local function roundTo(num, target) --For stacks of fuel
385 if num >= target then return target elseif num < 0 then return 0 else return num end
386 end
387 local function updateScreen()
388 output("Welcome to SmartFuel! Now Refueling...", 1,1)
389 output("Currently taking fuel from slot "..currSlot,1,2)
390 output("Current single fuel: "..tostring(oneFuel or 0),1,3)
391 output("Current estimate of needed fuel: ",1,4)
392 output("Single Items: "..math.ceil(neededFuelItems or 0),4,5)
393 output("Stacks: "..math.ceil((neededFuelItems or 0) / 64),4,6)
394 output("Needed Fuel: "..tostring(neededFuel),1,12)
395 output("Current Fuel: "..tostring(checkFuel()),1,13)
396 end
397 while checkFuel() <= neededFuel do
398 currSlot = currSlot + 1
399 turtle.select(currSlot)
400 updateScreen()
401 while turtle.getItemCount(currSlot) == 0 do sleep(1.5) end
402 repeat
403 local previous = checkFuel()
404 turtle.refuel(1)
405 oneFuel = checkFuel() - previous
406 updateScreen()
407 until (oneFuel or 0) > 0 --Not an if to prevent errors if fuel taken out prematurely.
408 neededFuelItems = (neededFuel - checkFuel()) / oneFuel
409 turtle.refuel(math.ceil(roundTo(neededFuelItems, 64))) --Change because can only think about 64 at once.
410 if turtle.getItemCount(roundTo(currSlot + 1, 16)) == 0 then --Resets if no more fuel
411 currSlot = 0
412 end
413 neededFuelItems = (neededFuel - checkFuel()) / oneFuel
414 end
415 end
416 --Ender Chest Obtaining
417 if enderChestEnabled then
418 while turtle.getItemCount(enderChestSlot) ~= 1 do
419 screen(1,1)
420 print("You have decided to use an Ender Chest!")
421 print("Please place one Ender Chest in slot ",enderChestSlot)
422 sleep(1)
423 end
424 print("Ender Chest in slot ",enderChestSlot, " checks out")
425 sleep(2)
426 end
427 --Initial Rednet Handshake
428 if rednetEnabled then
429 screen(1,1)
430 print("Rednet is Enabled")
431 print("The Channel to open is "..channels.send)
432 modem = peripheral.wrap("right")
433 modem.open(channels.receive)
434 local i = 0
435 repeat
436 local id = os.startTimer(3)
437 i=i+1
438 print("Sending Initial Message "..i)
439 modem.transmit(channels.send, channels.receive, "{ 'Initial' }")
440 local message
441 repeat
442 local event, idCheck, channel,_,locMessage, distance = os.pullEvent()
443 message = locMessage
444 until (event == "timer" and idCheck == id) or (event == "modem_message" and channel == channels.receive and message == channels.confirm)
445 until message == channels.confirm
446 connected = true
447 print("Connection Confirmed!")
448 sleep(1.5)
449 end
450 local function biometrics(sendChannel)
451 local commands = { Confirm = "Confirm" }
452 local toSend = { ["x"] = x, ["y"] = (layers), ["z"] = z, --The y calc is weird...
453 ["xPos"] = xPos, ["yPos"] = yPos, ["zPos"] = zPos,
454 ["percent"] = percent, ["mined" ]= mined,
455 ["fuel"] = checkFuel(), ["moved"] = moved,
456 ["remainingBlocks"] = (volume-mined), ["ID"] = os.getComputerID(),
457 ["isInPath"] = isInPath, --Whether it is going back to start
458 ["volume"] = volume, ["area"] = area}
459 modem.transmit(channels.send, channels.receive, textutils.serialize(toSend))
460 id = os.startTimer(0.1)
461 local event, message
462 repeat
463 local locEvent, idCheck, confirm, _, locMessage, distance = os.pullEvent()
464 event, message = locEvent, locMessage
465 until (event == "timer" and idCheck == id) or (event == "modem_message" and confirm == channels.receive)
466 if event == "modem_message" then connected = true else connected = false end
467 --Stuff to do for different commands
468 end
469 --Showing changes to settings
470 screen(1,1)
471 print("Your selected settings:")
472 if #changedT == 0 then
473 print("Completely Default")
474 else
475 for i=1, #changedT do
476 print(changedT[i][1],": ",changedT[i][2]) --Name and Value
477 end
478 end
479 print("\nStarting in 3"); sleep(1); print("2"); sleep(1); print("1"); sleep(1.5) --Dramatic pause at end
480
481
482
483 ----------------------------------------------------------------
484 --Define ALL THE FUNCTIONS
485 function eventAdd(...)
486 return table.insert(events,1, {...}) or true
487 end
488 function eventGet(pos)
489 return events[tonumber(pos) or #events]
490 end
491 function eventPop(pos)
492 return table.remove(events,tonumber(pos) or #events) or false --This will return value popped, tonumber returns nil if fail, so default to end
493 end
494 function eventRun(value, ...)
495 local argsList = {...}
496 if type(value) == "string" then
497 if value:sub(-1) ~= ")" then --So supports both "up()" and "up"
498 value = value .. "("
499 for a, b in pairs(argsList) do --Appending arguments
500 local toAppend
501 if type(b) == "table" then toAppend = textutils.serialize(b)
502 elseif type(b) == "string" then toAppend = "\""..tostring(b).."\"" --They weren't getting strings around them
503 else toAppend = tostring(b) end
504 value = value .. (toAppend or "true") .. ", "
505 end
506 if value:sub(-1) ~= "(" then --If no args, do not want to cut off
507 value = value:sub(1,-3)..""
508 end
509 value = value .. ")"
510 end
511 --print(value) --Debug
512 local func = loadstring(value)
513 setfenv(func, getfenv(1))
514 return func()
515 end
516 end
517
518 function runAllEvents()
519 while #events > 0 do
520 local toRun = eventGet()
521 --print(toRun[1]) --Debug
522 eventRun(unpack(toRun))
523 eventPop()
524 end
525 end
526
527 function display() --This is just the last screen that displays at the end
528 screen(1,1)
529 print("Total Blocks Mined: "..mined)
530 print("Current Fuel Level: "..turtle.getFuelLevel())
531 print("Cobble: "..totals.cobble)
532 print("Usable Fuel: "..totals.fuel)
533 print("Other: "..totals.other)
534 if rednetEnabled then
535 print("")
536 print("Sent Stop Message")
537 finalTable = {{["Mined: "] = mined}, {["Cobble: "] = totals.cobble}, {["Fuel: "] = totals.fuel},
538 {["Other: "] = totals.other}, {["Fuel: "] = checkFuel()} }
539 modem.transmit(channels.send,channels.receive,"stop")
540 modem.transmit(channels.send,channels.receive,textutils.serialize(finalTable))
541 modem.close(channels.receive)
542 end
543 if doBackup then fs.delete(saveFile) end
544 end
545 function updateDisplay() --Runs in Mine(), display information to the screen in a certain place
546 screen(1,1)
547 print("Blocks Mined")
548 print(mined)
549 print("Percent Complete")
550 print(percent.."%")
551 print("Fuel")
552 print(checkFuel())
553 -- screen(1,1)
554 -- print("Xpos: ")
555 -- print(xPos)
556 -- print("RelXPos: ")
557 -- print(relxPos)
558 -- print("Z Pos: ")
559 -- print(zPos)
560 -- print("Y pos: ")
561 -- print(yPos)
562 if rednetEnabled then
563 screenLine(1,7)
564 print("Connected: "..tostring(connected))
565 end
566 end
567 function logMiningRun(textExtension, extras) --Logging mining runs
568 if logging then
569 local number
570 if not fs.isDir(logFolder) then
571 fs.delete(logFolder)
572 fs.makeDir(logFolder)
573 number = 1
574 else
575 local i = 0
576 repeat
577 i = i + 1
578 until not fs.exists(logFolder.."/Quarry_Log_"..tostring(i)..(textExtension or ""))
579 number = i
580 end
581 handle = fs.open(logFolder.."/Quarry_Log_"..tostring(number)..(textExtension or ""),"w")
582 local function write(...)
583 for a, b in ipairs({...}) do
584 handle.write(tostring(b))
585 end
586 handle.write("\n")
587 end
588 write("Welcome to the Quarry Logs!")
589 write("Entry Number: ",number)
590 write("Dimensions (X Z Y): ",x," ",z," ", y)
591 write("Blocks Mined: ", mined)
592 write(" Cobble: ", totals.cobble)
593 write(" Usable Fuel: ", totals.fuel)
594 write(" Other: ",totals.other)
595 write("Total Fuel Used: ", (originalFuel or (neededFuel + checkFuel()))- checkFuel()) --Protect against errors with some precision
596 write("Expected Fuel Use: ", neededFuel)
597 handle.close()
598 end
599 end
600 function isFull(slots)
601 slots = slots or 16
602 local numUsed = 0
603 sleep(0)
604 for i=1, 16 do
605 if turtle.getItemCount(i) > 0 then numUsed = numUsed + 1 end
606 end
607 if numUsed > slots then
608 return true
609 end
610 return false
611 end
612 function dig(doAdd, func)
613 doAdd = doAdd or true
614 func = func or turtle.dig
615 if func() then
616 if doAdd then
617 mined = mined + 1
618 end
619 return true
620 end
621 return false
622 end
623 if not inverted then --Regular functions :) I switch definitions for optimizatoin (I thinK)
624 function digUp(doAdd)
625 return dig(doAdd,turtle.digUp)
626 end
627 function digDown(doAdd)
628 return dig(doAdd,turtle.digDown)
629 end
630 else
631 function digDown(doAdd)
632 return dig(doAdd,turtle.digUp)
633 end
634 function digUp(doAdd)
635 return dig(doAdd,turtle.digDown)
636 end
637 end
638 function relativeXCalc()
639 if rowCheck == "right" then relxPos = xPos else relxPos = (x-xPos)+1 end
640 end
641 function forward(doAdd)
642 if doAdd == nil then doAdd = true end
643 if turtle.forward() then
644 if doAdd then
645 moved = moved + 1
646 end
647 if facing == 0 then
648 xPos = xPos + 1
649 elseif facing == 1 then
650 zPos = zPos + 1
651 elseif facing == 2 then
652 xPos = xPos - 1
653 elseif facing == 3 then
654 zPos = zPos - 1
655 else
656 error("Function forward, facing should be 0 - 3, got "..tostring(facing),2)
657 end
658 relativeXCalc()
659 return true
660 end
661 return false
662 end
663 function up(sneak)
664 sneak = sneak or 1
665 if inverted and sneak == 1 then
666 down(-1)
667 else
668 while not turtle.up() do --Absolute dig, not relative
669 if not dig(true, turtle.digUp) then
670 attackUp()
671 sleep(0.5)
672 end
673 end
674 yPos = yPos - sneak --Oh! I feel so clever
675 end --This works because inverted :)
676 saveProgress()
677 end
678 function down(sneak)
679 sneak = sneak or 1
680 local count = 0
681 if inverted and sneak == 1 then
682 up(-1)
683 else
684 while not turtle.down() do
685 count = count + 1
686 if not dig(true, turtle.digDown) then --This is absolute dig down, not relative
687 attackDown()
688 sleep(0.2)
689 end
690 if count > 20 then bedrock() end
691 end
692 yPos = yPos + sneak
693 end
694 saveProgress()
695 end
696 function right(num)
697 num = num or 1
698 for i=1, num do facing = coterminal(facing+1); saveProgress(); turtle.turnRight() end
699 end
700 function left(num)
701 num = num or 1
702 for i=1, num do facing = coterminal(facing-1); saveProgress(); turtle.turnLeft() end
703 end
704 function attack(doAdd, func)
705 doAdd = doAdd or true
706 func = func or turtle.attack
707 if func() then
708 if doAdd then
709 attacked = attacked + 1
710 end
711 return true
712 end
713 return false
714 end
715 function attackUp(doAdd)
716 if inverted then
717 return attack(doAdd, turtle.attackDown)
718 else
719 return attack(doAdd, turtle.attackUp)
720 end
721 end
722 function attackDown(doAdd)
723 if inverted then
724 return attack(doAdd, turtle.attackUp)
725 else
726 return attack(doAdd, turtle.attackDown)
727 end
728 end
729
730
731 function mine(doDigDown, doDigUp, outOfPath,doCheckInv) -- Basic Move Forward
732 if doCheckInv == nil then doCheckInv = true end
733 if doDigDown == nil then doDigDown = true end
734 if doDigUp == nil then doDigUp = true end
735 if outOfPath == nil then outOfPath = false end
736 if inverted then
737 doDigUp, doDigDown = doDigDown, doDigUp --Just Switch the two if inverted
738 end
739 if doRefuel and checkFuel() <= fuelTable[fuelSafety]/2 then
740 for i=1, 16 do
741 if turtle.getItemCount(i) > 0 then
742 turtle.select(i)
743 if checkFuel() < 200 + fuelTable[fuelSafety] then
744 turtle.refuel()
745 end
746 end
747 end
748 end
749 local count = 0
750 while not forward(not outOfPath) do
751 sleep(0) --Calls coroutine.yield to prevent errors
752 count = count + 1
753 if not dig() then
754 attack()
755 end
756 if count > 10 then
757 attack()
758 sleep(0.2)
759 end
760 if count > 50 then
761 if turtle.getFuelLevel() == 0 then --Don't worry about inf fuel because I modified this function
762 saveProgress({doCheckFuel = true})
763 error("No more fuel",0)
764 elseif yPos > (startY-7) then --If it is near bedrock
765 bedrock()
766 else --Otherwise just sleep for a bit to avoid sheeps
767 sleep(1)
768 end
769 end
770 end
771 checkSanity() --Not kidding... This is necessary
772 saveProgress(tab)
773 if doDigUp then
774 while turtle.detectUp() do
775 sleep(0) --Calls coroutine.yield
776 if not dig(true,turtle.digUp) then --This needs to be an absolute, because we are switching doDigUp/Down
777 attackUp()
778 count = count + 1
779 end
780 if count > 50 and yPos > (startY-7) then --Same deal with bedrock as above
781 bedrock()
782 end
783 end
784 end
785 if doDigDown then
786 dig(true,turtle.digDown) --This needs to be absolute as well
787 end
788 percent = math.ceil(moved/moveVolume*100)
789 updateDisplay()
790 isInPath = (not outOfPath) --For rednet
791 if doCheckInv and careAboutResources then
792 if moved%invCheckFreq == 0 then
793 if isFull(16-keepOpen) then dropOff() end
794 end; end
795 if rednetEnabled then biometrics() end
796 end
797 --Insanity Checking
798 function checkSanity()
799 if isInPath and not (facing == 0 or facing == 2) and #events == 0 then --If mining and not facing proper direction and not in a turn
800 turnTo(0)
801 rowCheck = "right"
802 end
803 if xPos < 0 or xPos > x or zPos < 0 or zPos > z or yPos < 0 then
804 saveProgress()
805 print("Oops. Detected that quarry was outside of predefined boundaries.")
806 print("Please go to my forum thread and report this with a short description of what happened")
807 print("If you could also run \"pastebin put Civil_Quarry_Restore\" and give me that code it would be great")
808 error("",0)
809 end
810 end
811
812 local function fromBoolean(input) --Like a calculator
813 if input then return 1 end
814 return 0
815 end
816 local function multBoolean(first,second) --Boolean multiplication
817 return (fromBoolean(first) * fromBoolean(second)) == 1
818 end
819 function coterminal(num, limit) --I knew this would come in handy :D
820 limit = limit or 4 --This is for facing
821 return math.abs((limit*fromBoolean(num < 0))-(math.abs(num)%limit))
822 end
823 if tArgs["-manualpos"] then
824 facing = coterminal(facing) --Done to improve support for "-manualPos"
825 if facing == 0 then rowCheck = "right" elseif facing == 2 then rowCheck = "left" end --Ditto
826 relativeXCalc() --Ditto
827 end
828
829 --Direction: Front = 0, Right = 1, Back = 2, Left = 3
830
831 function turnTo(num)
832 num = num or facing
833 num = coterminal(num) --Prevent errors
834 local turnRight = true
835 if facing-num == 1 or facing-num == -3 then turnRight = false end --0 - 1 = -3, 1 - 0 = 1, 2 - 1 = 1
836 while facing ~= num do --The above is used to smartly turn
837 if turnRight then
838 right()
839 else
840 left()
841 end
842 end
843 end
844 function goto(x,z,y, toFace)
845 --Will first go to desired z pos, then x pos, y pos varies
846 x = x or 1; y = y or 1; z = z or 1; toFace = toFace or facing
847 if yPos > y then --Will go up first if below position
848 while yPos~=y do up() end
849 end
850 if zPos > z then
851 turnTo(3)
852 elseif zPos < z then
853 turnTo(1)
854 end
855 while zPos ~= z do mine(false,false,true,false) end
856 if xPos > x then
857 turnTo(2)
858 elseif xPos < x then
859 turnTo(0)
860 end
861 while xPos ~= x do mine(false,false,true,false) end
862 if yPos < y then --Will go down after if above position
863 while yPos~=y do down() end
864 end
865 turnTo(toFace,"right")
866 saveProgress()
867 end
868 function drop(side, final, allowSkip)
869 side = sides[side] or "front" --The final number means that it will
870 if final then final = 0 else final = 1 end --drop a whole stack at the end
871 local allowSkip = allowSkip or (final == 0) --This will allow drop(side,t/f, rednetConnected)
872 count()
873 if doRefuel then
874 for i=1, 16 do
875 if slot[i][1] == 2 then
876 turtle.select(i); turtle.refuel()
877 end
878 end
879 turtle.select(1)
880 end
881 if side == "right" then turnTo(1) end
882 if side == "left" then turnTo(3) end
883 local whereDetect, whereDrop1, whereDropAll
884 local _1 = slot[1][2] - final --All but one if final, all if not final
885 if side == "top" then
886 whereDetect = turtle.detectUp ; whereDrop = turtle.dropUp
887 elseif side == "bottom" then
888 whereDetect = turtle.detectDown ; whereDrop = turtle.dropDown
889 else
890 whereDetect = turtle.detect; whereDrop = turtle.drop
891 end
892 local function waitDrop(val) --This will just drop, but wait if it can't
893 val = val or 64
894 local try = 1
895 while not whereDrop(val) do
896 print("Chest Full, Try "..try)
897 try = try + 1
898 sleep(2)
899 end
900 end
901 repeat
902 local detected = whereDetect()
903 if detected then
904 waitDrop(_1)
905 for i=1, 2 do --This is so I quit flipping missing items when chests are partially filled
906 for i=2, 16 do
907 if turtle.getItemCount(i) > 0 then
908 turtle.select(i)
909 waitDrop(nil, i)
910 end
911 end
912 end
913 elseif not allowSkip then
914 print("Waiting for chest placement place a chest to continue")
915 while not whereDetect() do
916 sleep(1)
917 end
918 end
919 until detected or allowSkip
920 if not allowSkip then totals.cobble = totals.cobble - 1 end
921 turtle.select(1)
922 end
923 function dropOff() --Not local because called in mine()
924 local currX,currZ,currY,currFacing = xPos, zPos, yPos, facing
925 if careAboutResources and not enderChestEnabled then --Regularly
926 eventAdd("goto", 1,1,currY,2) --Need this step for "-startDown"
927 eventAdd("goto(0,1,1,2)")
928 eventAdd("drop", dropSide,false)
929 eventAdd("turnTo(0)")
930 eventAdd("mine",false,false,true,false)
931 eventAdd("goto(1,1,1, 0)")
932 eventAdd("goto", 1, 1, currY, 0)
933 eventAdd("goto", currX,currZ,currY,currFacing)
934 elseif careAboutResources then --If using an enderChest
935 eventAdd("turnTo",currFacing-2)
936 eventAdd("dig",false)
937 eventAdd("turtle.select",enderChestSlot)
938 eventAdd("turtle.place")
939 eventAdd("drop","front",false)
940 eventAdd("turtle.select", enderChestSlot)
941 eventAdd("dig",false)
942 eventAdd("turnTo",currFacing)
943 eventAdd("turtle.select(1)")
944 end
945 runAllEvents()
946 return true
947 end
948 function bedrock()
949 if checkFuel() == 0 then error("No Fuel",0) end
950 local origin = {x = xPos, y = yPos, z = zPos}
951 print("Bedrock Detected")
952 if turtle.detectUp() then
953 print("Block Above")
954 local var
955 if facing == 0 then var = 2 elseif facing == 2 then var = 0 else error("Was facing left or right on bedrock") end
956 goto(xPos,zPos,yPos,var)
957 for i=1, relxPos do mine(true,true); end
958 end
959 goto(0,1,1,2)
960 drop(dropSide, true)
961 turnTo(0)
962 display()
963 print("\nFound bedrock at these coordinates: ")
964 print(origin.x," Was position in row\n",origin.z," Was row in layer\n",origin.y," Blocks down from start")
965 error("",0)
966 end
967
968 function endOfRowTurn(startZ, wasFacing, mineFunctionTable)
969 local halfFacing = 1
970 local toFace = coterminal(wasFacing + 2) --Opposite side
971 if zPos == startZ then
972 if facing ~= halfFacing then turnTo(halfFacing) end
973 mine(unpack(mineFunctionTable or {}))
974 end
975 if facing ~= toFace then
976 turnTo(toFace)
977 end
978 end
979
980 -------------------------------------------------------------------------------------
981 --Pre-Mining Stuff dealing with session persistence
982 runAllEvents()
983 if toQuit then error("",0) end --This means that it was stopped coming for its last drop
984
985 local doDigDown, doDigUp = (lastHeight ~= 1), (lastHeight == 0) --Used in lastHeight
986 if not restoreFoundSwitch then --Regularly
987 --Check if it is a mining turtle
988 if not isMiningTurtle then
989 local a, b = turtle.dig()
990 if a then mined = mined + 1; isMiningTurtle = true
991 elseif b == "Nothing to dig with" then
992 print("This is not a mining turtle. To make a mining turtle, craft me together with a diamond pickaxe")
993 error("",0)
994 end
995 end
996 mine(false,false,true) --Get into quarry by going forward one
997 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.
998 gpsSecondPos = {gps.locate(gpsTimeout)} --Note: Does not run this if it has already been restarted.
999 end
1000 for i = 1, startDown do
1001 eventAdd("down") --Add a bunch of down events to get to where it needs to be.
1002 end
1003 runAllEvents()
1004 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.
1005 else --restore found
1006 if doDigDown then digDown() end
1007 if doDigUp then digUp() end --Get blocks missed before stopped
1008 end
1009 --Mining Loops--------------------------------------------------------------------------
1010 turtle.select(1)
1011 while layersDone <= layers do -------------Height---------
1012 local lastLayer = layersDone == layers --If this is the last layer
1013 local secondToLastLayer = (layersDone + 1) == layers --This is for the going down at the end of a layer.
1014 moved = moved + 1 --To account for the first position in row as "moved"
1015 if doDigDown then digDown() end --This is because it doesn't mine first block in layer
1016 if not restoreFoundSwitch then rowCheck = "right" end
1017 relativeXCalc()
1018 while zPos <= z do -------------Width----------
1019 while relxPos < x do ------------Length---------
1020 mine(not lastLayer or (doDigDown and lastLayer), not lastLayer or (doDigUp and lastLayer)) --This will be the idiom that I use for the mine function
1021 end ---------------Length End-------
1022 if zPos ~= z then --If not on last row of section
1023 local func
1024 if rowCheck == "right" then --Swithcing to next row
1025 func = "right"; rowCheck = "left"; else func = "left"; rowCheck = "right" end --Which way to turn
1026 eventAdd("endOfRowTurn", zPos, facing , {not lastLayer or (doDigDown and lastLayer), not lastLayer or (doDigUp and lastLayer)}) --The table is passed to the mine function
1027 runAllEvents()
1028 else break
1029 end
1030 end ---------------Width End--------
1031 eventAdd("goto",1,1,yPos,0) --Goto start of layer
1032 runAllEvents()
1033 if not lastLayer then --If there is another layer
1034 for i=1, 2+fromBoolean(not(lastHeight~=0 and secondToLastLayer)) do down() end --The fromBoolean stuff means that if lastheight is 1 and last and layer, will only go down two
1035 end
1036 layersDone = layersDone + 1
1037 restoreFoundSwitch = false --This is done so that rowCheck works properly upon restore
1038 end ---------------Height End-------
1039
1040 eventAdd("goto",1,1,yPos,2) --Allows for startDown variable
1041 eventAdd("goto",0,1,1,2)
1042
1043 --Output to a chest or sit there
1044 if enderChestEnabled and not turtle.detect() then
1045 eventAdd("turtle.select",enderChestSlot)
1046 eventAdd("turtle.place")
1047 eventAdd("turtle.select(1)")
1048 end
1049 eventAdd("drop",dropSide, true)
1050 eventAdd("turnTo(0)")
1051
1052 --Display was moved above to be used in bedrock function
1053 eventAdd("display")
1054 --Log current mining run
1055 eventAdd("logMiningRun",logExtension)
1056 toQuit = true --I'll use this flag to clean up
1057 runAllEvents()
1058 --Cleanup
1059 turtle.getFuelLevel = getFuel
1060end
1061
1062DigRoom(tonumber(tArgs[1]),tonumber(tArgs[2]),tonumber(tArgs[3]),tArgs[4])
1063