CometWolf

new cTurtle

Dec 23rd, 2013
3,283
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 51.04 KB | None | 0 0
  1. assert(turtle,"This API may only be used on turtles!")
  2. local ver = 2.5
  3. local turtleOS = turtle.inspect and 1.64 or fs.getDir and 1.63 or turtle.equipRight and 1.6 or peripheral.getNames and 1.51
  4. local tFile = {
  5. --file path table
  6. ["cTurtle"] = "cTurtle"
  7. }
  8. tFile.directory = "/"..tFile.cTurtle.."Files"
  9. tFile.data = tFile.directory.."/data"
  10. tFile.restarted = tFile.directory.."/restarted"
  11. tFile.settings = tFile.directory.."/settings"
  12.  
  13. local unlimitedFuel = turtle.getFuelLevel() == "unlimited"
  14.  
  15. tSettings = {}
  16. function saveSettings(path,tTable)
  17.   path = path or tFile.settings
  18.   tTable = tTable or tSettings
  19.   local file = fs.open(path,"w")
  20.   for k,v in pairs(tTable) do
  21.     if type(v) == "table" then
  22.       v = textutils.serialize(v):gsub("\n%s-","")
  23.     elseif v == true then
  24.       v = "true"
  25.     elseif v == false then
  26.       v = "false"
  27.     end
  28.     file.writeLine(k.." = "..v)
  29.   end
  30.   file.close()
  31. end
  32.  
  33. local function loadSettings(path,tTable)
  34.   path = path or tFile.settings
  35.   tTable = tTable or tSettings
  36.   local file = fs.open(path,"r")
  37.   local line = file.readLine()
  38.   while line do
  39.     local k = line:match"^%S+"
  40.     local v = line:sub(#k+4,#line)
  41.     if v:match"^%d+$" then --number
  42.       v = tonumber(v)
  43.     elseif v == "true" then --true
  44.       v = true
  45.     elseif v == "false" then --false
  46.       v = false
  47.     elseif v:match"^{.-}$" then --table
  48.       v = textutils.unserialize(v)
  49.     end
  50.     tTable[k] = v
  51.     line = file.readLine()
  52.   end
  53.   file.close()
  54. end
  55.  
  56. --settings, configure these from external programs using cTurtle.tSettings.setting = setting then cTurtle.saveSettings()
  57. if fs.exists(tFile.settings) then
  58.   loadSettings()
  59. else
  60.   tSettings.renderMove = true --wether to render info page on movement or not
  61.   tSettings.enderFuel = false --slot of enderchest containing fuel
  62.   tSettings.autoRefuel = false --wether to check inventory for fuel before stopping when out of fuel
  63.   tSettings.controlChannel = 9280 --channel for external modem control, modemControl(true) must be called prior to usage
  64.   tSettings.controlResponseChannel = 9281 --channel for modem responses
  65.   tSettings.swarmChannel = 9290 --channel for swarm controls
  66.   tSettings.swarmResponseChannel = 9291 --channel for swarm responses
  67.   tSettings.swarm = false --swarm number. Swarm turtles will not break other turtles.
  68.   tSettings.formation = false --swarm formation
  69.   tSettings.tEnderFuel = {} -- slots to check enderchest for fuel
  70.   for i=17,54 do
  71.     tSettings.tEnderFuel[#tSettings.tEnderFuel+1] = i
  72.   end
  73. end
  74.  
  75. tTimer = {
  76. --timer table
  77. ["inputCoords"] = 20, --time to cancel coordinate input
  78. ["dig"] = 0.1, --time to wait after digging a block, incase of sand or gravel
  79. ["moveFail"] = 0.5, --time to wait if not digging and path is blocked
  80. ["enderFuel"] = 60 --time between each enderchest fuel check.
  81. }
  82.  
  83. --init tables
  84. local tColors
  85. --color table
  86. if term.isColor() then
  87.   tColors = {
  88.     ["screen"] = colors.lightBlue,
  89.     ["text"] = colors.white,
  90.     ["textBackground"] = colors.black,
  91.     ["topBar"] = colors.blue,
  92.     ["bottomBar"] = colors.blue,
  93.     ["topBarText"] = colors.yellow,
  94.     ["bottomBarText"] = colors.yellow
  95.   }
  96. else
  97.   tColors = {
  98.   --black & white color table
  99.     ["screen"] = colors.white,
  100.     ["text"] = colors.white,
  101.     ["textBackground"] = colors.black,
  102.     ["topBar"] = colors.black,
  103.     ["bottomBar"] = colors.black,
  104.     ["topBarText"] = colors.white,
  105.     ["bottomBarText"] = colors.white
  106.   }
  107. end
  108.  
  109. local tText = {
  110. --string table
  111.   ["topBar"] = "cTurtle ver: "..ver,
  112.   ["x"] = "X: ",
  113.   ["y"] = "Y: ",
  114.   ["z"] = "Z: ",
  115.   ["dir"] = "Dir: ",
  116.   ["fuel"] = "Fuel: ",
  117.   ["failedGPS"] = "No GPS, please input co-ordinates"
  118. }
  119.  
  120. local tTerm = {}
  121. --terminal size table
  122. tTerm.x,tTerm.y = term.getSize()
  123. tTerm.xMid = tTerm.x/2
  124. tTerm.yMid = tTerm.y/2
  125.  
  126. local tCurs = {
  127. --cursor table
  128.   ["x"] = 1,
  129.   ["y"] = 1
  130. }
  131.  
  132. tDir = {
  133. --direction conversion table
  134.   ["X+"]        = {
  135.     "x+","-x-","1","+1","-3","east","-west","e","-w"
  136.   },
  137.   ["X-"]        = {
  138.     "x-","-x+","3","+3","-1","west","-east","w","-e"
  139.   },
  140.   ["Z+"]        = {
  141.     "z+","-z-","2","+2","-4","south","-north","s","-n"
  142.   },
  143.   ["Z-"]        = {
  144.     "z-","-z+","4","+4","-2","north","-south","n","-s"
  145.   },
  146.   ["Y+"]        = {
  147.     "y+","-y-","up","-down","top","-bottom","5","+5","-6"
  148.   },
  149.   ["Y-"]        = {
  150.     "y-","-y+","down","-up","bottom","-top","6","+6","-5"
  151.   }
  152. }
  153.  
  154. tDirText = {
  155. --text to direction conversion table
  156.   ["forward"]   = {
  157.     "forward","front","ahead","-back","-backward","-backwards","-behind"
  158.   },
  159.   ["backward"]  = {
  160.     "back","backward","backwards","behind","around","-front","-forward","-ahead"
  161.   },
  162.   ["left"]      = {
  163.     "left","-right"
  164.   },
  165.   ["right"]     = {
  166.     "right","-left"
  167.   }
  168. }
  169.  
  170. tNumDir = {
  171. --number to symbol conversion table
  172.   "X+",
  173.   "Z+",
  174.   "X-",
  175.   "Z-",
  176.   "Y+",
  177.   "Y-"
  178. }
  179.  
  180. tNumHeading = {
  181. --number to heading conversion table
  182.   "east",
  183.   "south",
  184.   "west",
  185.   "north",
  186.   "up",
  187.   "down"
  188. }
  189.  
  190. tDirNum = {
  191. --symbol to number conversion table
  192.   ["X+"] = 1,
  193.   ["X-"] = 3,
  194.   ["Z+"] = 2,
  195.   ["Z-"] = 4,
  196.   ["Y+"] = 5,
  197.   ["Y-"] = 6
  198. }
  199.  
  200. tNumSide = {
  201. --number to turtle side conversion table
  202.   [1] = "front",
  203.   [2] = "front",
  204.   [3] = "front",
  205.   [4] = "front",
  206.   [5] = "top",
  207.   [6] = "bottom"
  208. }
  209. tPos = {} --location table
  210. tDest = {} --destination table
  211.  
  212.  
  213. --custom native turtle functions
  214.  
  215. tPos.selectedSlot = 1
  216. turtle.select(1)
  217. local turtleSelect = _G.turtle.select
  218. _G.turtle.select = function(slot)
  219.   turtleSelect(slot)
  220.   tPos.selectedSlot = slot
  221. end
  222.  
  223. native = turtle.native or turtle
  224.  
  225. eventHandler = {} --stores custom event handler functions
  226. function clearEventHandler(skipcTurtle) --idfk why i can't just redef it....
  227.   local tKey = {}
  228.   for k in pairs(eventHandler) do
  229.     if not skipcTurtle
  230.     or (k ~= "refuel" and k ~= "moveFail") then
  231.       tKey[#tKey+1] = k
  232.     end
  233.   end
  234.   for i=1,#tKey do --fuck you next
  235.     eventHandler[tKey[i]] = nil
  236.   end
  237. end
  238. local function waitForResponse(id) --modified to not throw away non-turtle events and handle cTurtle events
  239.   local resBool,resString
  240.   while true do
  241.     local tEvent = {os.pullEvent()}
  242.     if tEvent[1] == "turtle_response" then
  243.       if tEvent[2] == id then
  244.         if tEvent[3] then
  245.           resBool = true
  246.         else
  247.           resBool,resString = false, tEvent[4]
  248.         end
  249.         return resBool,resString
  250.       end
  251.     elseif tEvent[1] == "cTurtle" then
  252.       return tEvent[2]
  253.     elseif eventHandler[tEvent[1]] then
  254.       eventHandler[tEvent[1]](tEvent)
  255.     end
  256.   end
  257. end
  258.  
  259. local function wrap(sCommand) --get native function
  260.   return turtleOS >= 1.6 and native[sCommand]
  261.   or function(...)
  262.     local id = native[sCommand](...)
  263.     if id == -1 then
  264.       return false
  265.     end
  266.     return waitForResponse(id)
  267.   end
  268. end
  269.  
  270. -- Wrap standard commands
  271. local turtle = {}
  272. turtle["getItemCount"] = native.getItemCount
  273. turtle["getItemSpace"] = native.getItemSpace
  274. turtle["getFuelLevel"] = native.getFuelLevel
  275.  
  276. for k,v in pairs( native ) do
  277.   if type( k ) == "string" and type( v ) == "function" and turtle[k] == nil then
  278.     turtle[k] = wrap( k )
  279.   end
  280. end
  281.  
  282. -- Wrap peripheral commands
  283. if peripheral.getType("left") == "workbench" then
  284.   turtle["craft"] = function( ... )
  285.     local id = peripheral.call( "left", "craft", ... )
  286.     return waitForResponse( id )
  287.   end
  288. elseif peripheral.getType("right") == "workbench" then
  289.   turtle["craft"] = function( ... )
  290.     local id = peripheral.call( "right", "craft", ... )
  291.     return waitForResponse( id )
  292.   end
  293. end
  294.  
  295. --internal API functions
  296. tTextDir = {}
  297. local function updateDirs()
  298.   --updates table containing current directions
  299.   tTextDir.forward = tPos.dir
  300.   tTextDir.right = dirAdd(tPos.dir)
  301.   tTextDir.left = dirSub(tPos.dir)
  302.   tTextDir.backward = dirAdd(tPos.dir,2)
  303.   return tTextDir
  304. end
  305.  
  306. local function getTurtleFunc(FUNCTION,DIRECTION,oneTurn)
  307.   --simplified function for acessing all turtle functions with cTurtle's direction capabilities
  308.   assert(DIRECTION,"No direction given for function "..FUNCTION)
  309.   DIRECTION = dirStandardize(DIRECTION)
  310.   if DIRECTION < 5 then -- not Y+ or Y-
  311.     if FUNCTION == "move" then
  312.       FUNCTION = "forward"
  313.     end
  314.     if oneTurn == true then
  315.       turn(DIRECTION)
  316.       return turtle[FUNCTION]
  317.     elseif oneTurn == false then
  318.       return turtle[FUNCTION]
  319.     else
  320.       return function()
  321.         turn(DIRECTION)
  322.         return turtle[FUNCTION]()
  323.       end
  324.     end
  325.   elseif DIRECTION == 5 then --Y+
  326.     if FUNCTION == "move" then
  327.       return turtle.up
  328.     end
  329.     return turtle[FUNCTION.."Up"]
  330.   elseif DIRECTION == 6 then -- Y-
  331.     if FUNCTION == "move" then
  332.       return turtle.down
  333.     end
  334.     return turtle[FUNCTION.."Down"]
  335.   end
  336. end
  337.  
  338.  
  339. local function turnRight(AMOUNT)
  340.   --proper right turn
  341.   AMOUNT = AMOUNT or 1
  342.   if AMOUNT < 1 then
  343.     AMOUNT = -AMOUNT
  344.   end
  345.   for i=1,AMOUNT do
  346.     tPos.dir = dirAdd(tPos.dir,1)
  347.     saveData()
  348.     --updates data prior to turn, to compensate for crash during turn
  349.     turtle.turnRight()
  350.     if renderMove then
  351.       refreshLines()
  352.     end
  353.   end
  354.   updateDirs()
  355. end
  356.  
  357. local function turnLeft(AMOUNT)
  358.   --proper left turn
  359.   AMOUNT = AMOUNT or 1
  360.   if AMOUNT < 1 then
  361.     AMOUNT = -AMOUNT
  362.   end
  363.   for i=1,AMOUNT do
  364.     tPos.dir = dirSub(tPos.dir,1)
  365.     saveData()
  366.     --updates data prior to turn, to compensate for crash during turn
  367.     turtle.turnLeft()
  368.     if renderMove then
  369.       refreshLines()
  370.     end
  371.   end
  372.   updateDirs()
  373. end
  374.  
  375. local function sleep(time)
  376.   -- modified to not throw away events and cancel cTurtle events if c is pressed
  377.   local timerId = os.startTimer(time or 0)
  378.   while true do
  379.     local tEvent = {os.pullEvent()}
  380.     if tEvent[1] == "timer" and tEvent[2] == timerId then
  381.       return time
  382.     elseif tEvent[1] == "key" and tEvent[2] == 46 then --c
  383.       os.queueEvent("cTurtle","cancel")
  384.     else
  385.       os.queueEvent(unpack(tEvent))
  386.     end
  387.   end
  388. end
  389.  
  390. --API functions
  391. function update(swarm)
  392.   if swarm then --update swarm turtles through rednet
  393.     local cTurtleFile = {
  394.       id = "all",
  395.       [1] = "cTurtle update"
  396.     }
  397.     for line in fs.open(tFile.cTurtle,"r").readLine do
  398.       cTurtleFile[#cTurtleFile+1] = line
  399.     end
  400.     _G.modem.transmit(tSettings.swarmChannel,tSettings.swarmResponseChannel,cTurtleFile)
  401.   else --update self
  402.     local response,paste = http.get("http://pastebin.com/raw.php?i=JRPN0P8x")
  403.     if response then
  404.       paste = response.readAll()
  405.       response.close()
  406.       --save to file
  407.       local file = fs.open(tFile.cTurtle,"w")
  408.       file.writeLine(paste)
  409.       file.close()
  410.       os.reboot()
  411.     else
  412.       return false
  413.     end
  414.   end
  415. end
  416.  
  417. tInput = {}
  418. function input()
  419.   --enables co-ordinate input
  420.   local tFieldOrder = {"x","y","z","dir"}
  421.   for i=1,#tFieldOrder do
  422.     --creates input field table
  423.     local key,value = tFieldOrder[i],tPos[tFieldOrder[i]]
  424.     if key == "dir" and string.match(string.format(value),"%d") then
  425.       value = tNumDir[value]
  426.     end
  427.     tInput[i] = {}
  428.     tInput[i]["value"] = string.format(value)
  429.     tInput[i]["field"] = tText[key]
  430.     tInput[i]["startX"] = #tInput[i]["field"]+2
  431.   end
  432.   term.setBackgroundColor(tColors.screen)
  433.   term.clear()
  434.   refreshLines()
  435.   renderBars(tText.failedGPS)
  436.   tCurs.x,tCurs.Y = 0,1
  437.   term.setCursorPos(tInput[tCurs.y]["startX"],tCurs.y*2+1)
  438.   term.setCursorBlink(true)
  439.   local timeOut = os.startTimer(tTimer.inputCoords)
  440.   while true do
  441.     --user input begins
  442.     local tEvent = {os.pullEvent()}
  443.     if timeOut
  444.     and (tEvent[1] == "key" or tEvent[1] == "mouse_click" or tEvent[1] == "char") then
  445.       --cancel timeout
  446.       timeOut = nil
  447.     end
  448.     if tEvent[1] == "mouse_click" then
  449.       if tEvent[2] == 1 then
  450.         local clickY = (tEvent[4]-1)/2
  451.         if tFieldOrder[clickY] and tEvent[3] < tTerm.xMid-1 then
  452.           local clickX = tEvent[3]-#tInput[clickY]["field"]-1
  453.           if clickX > 0 then
  454.             tCurs.y = clickY
  455.             tCurs.x = math.min(#tInput[clickY]["value"],clickX-1)
  456.           end
  457.         end
  458.       end
  459.     elseif tEvent[1] == "key" then
  460.       --special keyes
  461.       if tEvent[2] == 200 then --up arrow
  462.         tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]
  463.         tCurs.y = tCurs.y-1
  464.         tCurs.x = tCurs.x-#tInput[math.max(1,tCurs.y)]["field"]
  465.       elseif tEvent[2] == 208 then --down arrow
  466.         tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]
  467.         tCurs.y = tCurs.y+1
  468.         tCurs.x= tCurs.x-#tInput[math.min(#tInput,tCurs.y)]["field"]
  469.       elseif tEvent[2] == 203 then --left arrow
  470.         tCurs.x = tCurs.x-1
  471.       elseif tEvent[2] == 205 then --right arrow
  472.         tCurs.x = tCurs.x+1
  473.       elseif tEvent[2] == 28 then --enter
  474.         if tCurs.y >= #tInput then
  475.           --input complete
  476.           if dirStandardize(tInput[4]["value"]) then
  477.             break
  478.           else
  479.             renderBars"Faulty direction."
  480.           end
  481.         else
  482.           tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]-#tInput[tCurs.y+1]["field"]
  483.           tCurs.y = tCurs.y+1
  484.         end
  485.       elseif tEvent[2] == 14 and tCurs.x > 0 then --backspace
  486.         tInput[tCurs.y]["value"] = string.sub(tInput[tCurs.y]["value"],1,tCurs.x-1)..string.sub(tInput[tCurs.y]["value"],tCurs.x+1,#tInput[tCurs.y]["value"])
  487.         tCurs.x = tCurs.x-1
  488.       elseif tEvent[2] == 206 then --end
  489.         tCurs.x = #tInput[tCurs.y]["value"]
  490.       elseif tEvent[2] == 206 then --home
  491.         tCurs.x = 1
  492.       elseif tEvent[2] == 211 then --delete
  493.         if #tInput[tCurs.y]["value"] == 1 and tCurs.x <= 0 then
  494.           tInput[tCurs.y]["value"] = ""
  495.         else
  496.           tInput[tCurs.y]["value"] = string.sub(tInput[tCurs.y]["value"],1,tCurs.x)..string.sub(tInput[tCurs.y]["value"],tCurs.x+2,#tInput[tCurs.y]["value"])
  497.         end
  498.       end
  499.       --cursor check
  500.       tCurs.y = math.min(#tInput,tCurs.y)
  501.       tCurs.y = math.max(1,tCurs.y)
  502.       tCurs.x = math.min(#tInput[tCurs.y]["value"],tCurs.x)
  503.       tCurs.x = math.max(0,tCurs.x)
  504.     elseif tEvent[1] == "char" then
  505.       local input
  506.       if #tInput[tCurs.y]["value"]+#tInput[tCurs.y]["field"] >= tTerm.xMid-3 then
  507.         --limits amount of characters to bar size
  508.       elseif tEvent[2]:match"[%+%-]" and tInput[tCurs.y]["value"]:match"[%+%-]" then
  509.         --only one of either + or -
  510.       elseif tInput[tCurs.y]["field"]:match"Dir:" and tEvent[2]:match"[XZxznorthNORTHsuSUweWEaA%+%-]" then
  511.         --input to direction field, accepts +- and letters related to directions
  512.         if tEvent[2]:match"[%+%-]" and tCurs.x ~= 1
  513.         or tEvent[2]:match"[%+%-]" and #tInput[tCurs.y]["value"] > 1 then
  514.           -- + and - can only be at the end of single char directions
  515.         else
  516.           input = true
  517.         end
  518.       elseif tEvent[2]:match"[0-9%+%-]" then
  519.         --other fields, only accept numbers and +-
  520.         if tEvent[2]:match"[%+%-]" and tCurs.x ~= 0
  521.         or tInput[tCurs.y]["value"]:match"[%+%-]" and tCurs.x == 0 then
  522.           -- + and - can only be at start
  523.         else
  524.           input = true
  525.         end
  526.       end
  527.       if input then
  528.         --input accepted
  529.         tInput[tCurs.y]["value"] = string.sub(tInput[tCurs.y]["value"],1,tCurs.x)..tEvent[2]..string.sub(tInput[tCurs.y]["value"],tCurs.x+1,#tInput[tCurs.y]["value"])
  530.         tCurs.x = tCurs.x+1
  531.       end
  532.     elseif tEvent[1] == "timer" then
  533.       --timers
  534.       if tEvent[2] == timeOut then
  535.         break
  536.       end
  537.     end
  538.     --updates current field on user input
  539.     renderLine(tCurs.y)
  540.     term.setCursorPos(tInput[tCurs.y]["startX"]+tCurs.x,tCurs.y*2+1)
  541.     term.setCursorBlink(true)
  542.   end
  543.   --input complete, storing data
  544.   tPos.x = tonumber(tInput[1]["value"])
  545.   tPos.y = tonumber(tInput[2]["value"])
  546.   tPos.z = tonumber(tInput[3]["value"])
  547.   tPos.lastMove = dirStandardize(tInput[4]["value"])
  548.   tPos.dir = tPos.lastMove
  549.   term.setCursorBlink(false)
  550.   term.setTextColor(colors.white)
  551.   term.setBackgroundColor(colors.black)
  552.   term.clear()
  553.   term.setCursorPos(1,1)
  554. end
  555.  
  556. function locate(FORCE)
  557.   --gets turtle coordinates
  558.   local x,y,z,dir,x2,y2,z2
  559.   if _G.restarted or FORCE then
  560.     --re-gets coordinates
  561.     --via GPS
  562.     x,y,z = gps.locate(3)
  563.     if x and (not unlimitedFuel and turtle.getFuelLevel() > 0 or unlimitedFuel) then
  564.       --GPS sucess, getting direction by moving
  565.       local moved,down = false,false
  566.       while not moved do
  567.         while not unlimitedFuel and turtle.getFuelLevel() <= 0 do
  568.           if tSettings.enderFuel then
  569.             enderRestock(tSettings.enderFuel,true,tSettings.tEnderFuel)
  570.             os.startTimer(tTimer.enderFuel)
  571.           end
  572.           if tSettings.autoRefuel and turtle.getFuelLevel() <= 0 then
  573.             for i=1,16 do
  574.               turtle.refuel(64)
  575.             end
  576.           end
  577.           if turtle.getFuelLevel() <= 0 then
  578.             if eventHandler.refuel then
  579.               eventHandler.refuel()
  580.             else
  581.               renderBars("Fuel required! Press any key.")
  582.               while true do
  583.                 local e = os.pullEvent()
  584.                 if e == "key" then
  585.                   turtle.refuel(64)
  586.                   break
  587.                 elseif e == "timer" then
  588.                   break
  589.                 end
  590.               end
  591.             end
  592.           end
  593.         end
  594.         for i=1,4 do
  595.           if turtle.forward() then
  596.             moved = true
  597.             break
  598.           else
  599.             turtle.turnLeft()
  600.           end
  601.         end
  602.         if (turtle.detectUp() or down)
  603.         and not moved then
  604.           turtle.down()
  605.           down = true
  606.         elseif not moved then
  607.           turtle.up()
  608.         end
  609.       end
  610.     -- get new posistion
  611.       x2,y2,z2 = gps.locate(3)
  612.     -- calculate direction
  613.       if x2 and (x ~= x2 or z ~= z2) then --incase second GPS fails
  614.         if x < x2 then
  615.           dir = "X+"
  616.         elseif x > x2 then
  617.           dir = "X-"
  618.         elseif z < z2 then
  619.           dir = "Z+"
  620.         elseif z > z2 then
  621.           dir = "Z-"
  622.         end
  623.         tPos.x = x2
  624.         tPos.z = z2
  625.         tPos.y = y2
  626.         tPos.dir = tDirNum[dir]
  627.         tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
  628.       end
  629.     end
  630.     if not x
  631.     or not x2
  632.     or x == x2 and z == z2
  633.     or not dir then
  634.       --GPS failed, attempting player input
  635.       if fs.exists(tFile.data) then
  636.         loadData()
  637.       else
  638.         tPos.x = (x or 0)
  639.         tPos.y = (y or 0)
  640.         tPos.z = (z or 0)
  641.         tPos.dir = 1
  642.         tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
  643.       end
  644.       tDest.x = tPos.x
  645.       tDest.y = tPos.y
  646.       tDest.z = tPos.z
  647.       input()
  648.     end
  649.     saveData()
  650.     _G.restarted = false
  651.     local file = fs.open(tFile.restarted,"w")
  652.     file.writeLine"false"
  653.     file.close()
  654.   else
  655.     --non-restarted call to locate
  656.     loadData()
  657.   end
  658.   tDest.x = tPos.x
  659.   tDest.y = tPos.y
  660.   tDest.z = tPos.z
  661.   return tPos
  662. end
  663.  
  664. function renderBars(BOTTOMTEXT)
  665.   --render top and bottom bar
  666.   BOTTOMTEXT = BOTTOMTEXT or " "
  667.   paintutils.drawLine(1,1,tTerm.x,1,tColors.topBar)
  668.   paintutils.drawLine(1,tTerm.y,tTerm.x,tTerm.y,tColors.bottomBar)
  669.   term.setCursorPos(tTerm.xMid-(#tText.topBar/2),1)
  670.   term.setTextColor(tColors.topBarText)
  671.   term.setBackgroundColor(tColors.topBar)
  672.   term.write(tText.topBar)
  673.   term.setCursorPos(tTerm.xMid-(#BOTTOMTEXT/2),tTerm.y)
  674.   term.setTextColor(tColors.bottomBarText)
  675.   term.setBackgroundColor(tColors.bottomBar)
  676.   term.write(BOTTOMTEXT)
  677.   term.setTextColor(tColors.text)
  678.   term.setBackgroundColor(tColors.textBackground)
  679. end
  680.  
  681. function refreshLines()
  682.   term.setTextColor(tColors.text)
  683.   term.setBackgroundColor(tColors.textBackground)
  684.   local tFieldOrder = {"x","y","z","dir","fuel","x","y","z"}
  685.   for i,k in pairs(tFieldOrder) do
  686.     if i <= 5 then
  687.       --pos, dir and fuel bars
  688.       local value = tPos[k]
  689.       if k == "dir" and string.match(string.format(value),"%d") then
  690.         value = tNumDir[value]
  691.       end
  692.       paintutils.drawLine(2,i*2+1,tTerm.xMid-1,i*2+1,tColors.textBackground)
  693.       term.setCursorPos(2,i*2+1)
  694.       term.write(string.sub(k:upper(),1,1)..k:sub(2,#k)..": "..value)
  695.     else
  696.       --dest bars
  697.       local value = tDest[k]
  698.       paintutils.drawLine(tTerm.xMid+1,(i-5)*2+1,tTerm.x-1,(i-5)*2+1,tColors.textBackground)
  699.       term.setCursorPos(tTerm.xMid+1,(i-5)*2+1)
  700.       term.write("Dest"..k:upper()..": "..value)
  701.     end
  702.   end
  703. end
  704.  
  705. function renderLine(LINE)
  706.   --renders input lines
  707.   paintutils.drawLine(2,LINE*2+1,tTerm.xMid-1,LINE*2+1,tColors.textBackground)
  708.   term.setCursorPos(2,LINE*2+1)
  709.   term.write(tInput[LINE]["field"]..tInput[LINE]["value"])
  710. end
  711.  
  712. function loadData()
  713.   --loads position data from file
  714.   local file = fs.open(tFile.data,"r")
  715.   file.readLine() --skip first line
  716.   tPos.x = tonumber(string.sub(file.readLine(),4,10))
  717.   tPos.y = tonumber(string.sub(file.readLine(),4,10))
  718.   tPos.z = tonumber(string.sub(file.readLine(),4,10))
  719.   tPos.dir = tDirNum[string.sub(file.readLine(),6,7)]
  720.   tPos.fuel = tonumber(string.sub(file.readLine(),7,13))
  721.   tPos.lastMove = string.sub(file.readLine(),12,13)
  722.   if not unlimitedFuel and turtle.getFuelLevel() < tPos.fuel and _G.restarted then
  723.     --fuel missing, correcting co-ordinates accordingly
  724.     local lastMove = string.lower(tPos.lastMove)
  725.     tPos[lastMove:sub(1,1)] = tPos[lastMove:sub(1,1)] + tonumber(lastMove:sub(2,2)..1)
  726.     tPos.fuel = turtle.getFuelLevel()
  727.     tPos.lastMove = tDirNum[tPos.lastMove]
  728.     saveData()
  729.   else
  730.     tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
  731.     tPos.lastMove = tDirNum[tPos.lastMove]
  732.   end
  733.   return tPos
  734. end
  735.  
  736. function saveData(x,y,z,dir,fuel,path)
  737.   x = x or tPos.x
  738.   y = y or tPos.y
  739.   z = z or tPos.z
  740.   dir = dir or tPos.dir
  741.   fuel = fuel or tPos.fuel
  742.   path = path or tFile.data
  743.   --saves position data to file
  744.   local file = fs.open(path,"w")
  745.   file.writeLine"cTurtle data file."
  746.   file.writeLine("X: "..x)
  747.   file.writeLine("Y: "..y)
  748.   file.writeLine("Z: "..z)
  749.   file.writeLine("Dir: "..tNumDir[dir])
  750.   file.writeLine("Fuel: "..(tPos.fuel or unlimitedFuel and 9999 or turtle.getFuelLevel()))
  751.   file.writeLine("Last move: "..tNumDir[(tPos.lastMove or dir)])
  752.   file.close()
  753. end
  754.  
  755. function dirStandardize(DIRECTION)
  756.   --formats the given direction for usage
  757.   assert(DIRECTION,"No direction given!")
  758.   DIRECTION = string.format(string.lower(DIRECTION))
  759.   if tPos.dir then
  760.     updateDirs()
  761.     for k,v in pairs(tDirText) do
  762.       for i=1,#tDirText[k] do
  763.         if DIRECTION == tDirText[k][i] then
  764.         --checks for forward, left, right, etc.
  765.           return tonumber(tTextDir[k])
  766.         end
  767.       end
  768.     end
  769.   end
  770.   for k,v in pairs(tDir) do
  771.     --checks for x+,x-,z+,north,south etc.
  772.     for i=1,#tDir[k] do
  773.       if DIRECTION == tDir[k][i] then
  774.         return tonumber(tDirNum[k])
  775.       end
  776.     end
  777.   end
  778.   --failed to format direction
  779.   error("Failed to format the given direction: "..DIRECTION,2)
  780. end
  781.  
  782. function dirAdd(NUM,ADDNUM)
  783.   --direction num right turn
  784.   NUM = NUM+(ADDNUM or 1)
  785.   if NUM > 4 then
  786.     NUM = NUM-4
  787.   end
  788.   return NUM
  789. end
  790.    
  791. function dirSub(NUM,SUBNUM)
  792.   --direction num left turn
  793.   NUM = NUM-(SUBNUM or 1)
  794.   if NUM < 1 then
  795.     NUM = 4+NUM
  796.   end
  797.   return NUM
  798. end
  799.  
  800. function turn(DIRECTION)
  801.   DIRECTION = dirStandardize(DIRECTION)
  802.   if DIRECTION > 4 then
  803.     return false --unable to turn Y+, Y-
  804.   end
  805.   if tSettings.renderMove then
  806.     renderBars("Turning "..tNumDir[DIRECTION])
  807.   end
  808.   if DIRECTION == dirSub(tPos.dir,1) then
  809.     --left turn when turning one left
  810.     turnLeft(1)
  811.   else
  812.     local turns = 0
  813.     while dirAdd(tPos.dir,turns) ~= DIRECTION do
  814.       turns = turns+1
  815.     end
  816.     --otherwise turn right
  817.     turnRight(turns)
  818.   end
  819.   if tSettings.renderMove then
  820.     renderBars("Turn complete")
  821.   end
  822.   return true
  823. end
  824.  
  825. function detect(DIRECTION)
  826.   return getTurtleFunc("detect",DIRECTION,true)()
  827. end
  828.  
  829. function dig(DIRECTION,SLOT,LOOP)
  830.   if SLOT then
  831.     turtle.select(SLOT)
  832.   end
  833.   local digFunc = getTurtleFunc("dig",DIRECTION,true)
  834.   local detectFunc = getTurtleFunc("detect",DIRECTION,false)
  835.   if LOOP then
  836.     digFunc()
  837.     while detectFunc() do
  838.       digFunc()
  839.     end
  840.     return true
  841.   end
  842.   return digFunc()
  843. end
  844.  
  845. function compare(DIRECTION,SLOT)
  846.   if SLOT then
  847.     turtle.select(SLOT)
  848.   end
  849.   return getTurtleFunc("compare",DIRECTION,true)()
  850. end
  851.  
  852. function place(DIRECTION,SLOT,LOOP)
  853.   if SLOT then
  854.     turtle.select(SLOT)
  855.   end
  856.   local func = getTurtleFunc("place",DIRECTION,true)
  857.   if loop then
  858.     while not func() do
  859.     end
  860.     return true
  861.   else
  862.     return getTurtleFunc("place",DIRECTION,true)()
  863.   end
  864. end
  865.  
  866. function suck(DIRECTION,SLOT)
  867.   if SLOT then
  868.     turtle.select(SLOT)
  869.   end
  870.   return getTurtleFunc("suck",DIRECTION,true)()
  871. end
  872.  
  873. function drop(DIRECTION,SLOT,AMOUNT)
  874.   if SLOT then
  875.     turtle.select(SLOT)
  876.   end
  877.   return getTurtleFunc("drop",DIRECTION,true)(AMOUNT or 64)
  878. end
  879.  
  880. function select(SLOT)
  881.   return turtle.select(SLOT)
  882. end
  883.  
  884. function replace(DIRECTION,SLOT,DIGSLOT)
  885.   if not compare(DIRECTION,SLOT) then
  886.     dig(DIRECTION,DIGSLOT,true)
  887.     turtle.select(SLOT or tPos.selectedSlot)
  888.   end
  889.   place(DIRECTION,SLOT)
  890. end
  891.  
  892. function findSpace(place)
  893.   --finds an open space and returns direction, places slected block if place
  894.   if not turtle.detectUp() then
  895.     while place and not turtle.placeUp() do end
  896.     return 5,"top"
  897.   elseif not turtle.detectDown() then
  898.     while place and not turtle.placeDown() do end
  899.     return 6,"bottom"
  900.   elseif not turtle.detect() then
  901.     while place and not turtle.place() do end
  902.     return tPos.dir,"front"
  903.   else
  904.     for i=1,3 do
  905.       turn"right"
  906.       if not turtle.detect() then
  907.          while place and not turtle.place() do end
  908.          return tPos.dir,"front"
  909.       end
  910.     end
  911.   end
  912.   return false
  913. end
  914.  
  915. function enderInteract(enderSlot,tItemsOut,tItemsIn)
  916.   --[[puts down an ender chest from the slot (enderSlot), iteracts with it according to the tItems table
  917.     using the following structure
  918.     tItemsOut = { --push items into chest
  919.       "Coal","Charcoal", --you can either store the name of the item directly in an index
  920.       1,2,3,4,5 --or the number of specific slots
  921.       { --or use a table for more advanced options
  922.         name = "Stone", --display name of the desired item, slot numbers work aswell
  923.         amount = 128, --optional, amount of items to attempt to deposit
  924.         slot = 1, --optional, slot to deposit the items to
  925.         slot = {1,2,3,4,5} --or a table may be used for multiple slots
  926.       }
  927.     },
  928.     tItemsIn = { --pull in specific items
  929.       "Coal","Charcoal", --you can either store the name of the item directly in an index
  930.       1,2,3,4,5 --or the number of specific slots to pull from
  931.       { --or use a table for more advanced options
  932.         name = "Stone", --display name of the desired item, slot numbers work aswell
  933.         amount = 128, --optional,amount of items to attempt to retrieve
  934.         slot = 1, --optional,slot to store the items in
  935.         slot = {1,2,3,4,5} --or a table may be used for multiple slots
  936.       }
  937.     }
  938.   ]]
  939.   turtle.select(enderSlot)
  940.   local _dir,chestDir = findSpace(true)
  941.   sleep(0.05)
  942.   --locate open space for ender chest
  943.   if chestDir then
  944.     local pushDir = tNumHeading[dirStandardize("-"..chestDir)]
  945.     local selected
  946.     local amount = 0 -- amout of items pushed from chest
  947.     if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
  948.       tItemsOut = tItemsOut or {}
  949.       tItemsIn = tItemsIn or {}
  950.       for k,v in pairs(tItemsOut) do
  951.         local class = type(v)
  952.         if class == "number" then
  953.          
  954.         elseif class == "string" then
  955.          
  956.         else --class == "table"
  957.          
  958.         end
  959.       end
  960.       for k,v in pairs(tItemsIn) do
  961.         if class == "number" then
  962.          
  963.         elseif class == "string" then
  964.          
  965.         else --class == "table"
  966.          
  967.         end
  968.       end
  969.     end
  970.   end
  971. end
  972.  
  973. function enderRestock(enderSlot,tTurtleSlots,tEnderSlots)
  974.   --puts down an ender chest from the slot (enderSlot), then attempts to restock the
  975.   --turtle slots in the table tTurtleSlots with the chest slots in tEnderSlots
  976.   turtle.select(enderSlot)
  977.   local _dir,chestDir = findSpace(true)
  978.   sleep(0.05)
  979.   --locate open space for ender chest
  980.   if chestDir then
  981.     local pushDir = tNumHeading[dirStandardize("-"..chestDir)]
  982.     local tTurtleSlotsCopy
  983.     if type(tTurtleSlots) == "table" then
  984.       --create local copy of turtle slots table
  985.       tTurtleSlotsCopy = {}
  986.       for i=1,#tTurtleSlots do
  987.         tTurtleSlotsCopy[i] = tTurtleSlots[i]
  988.       end
  989.     else
  990.       tTurtleSlotsCopy = {enderSlot}
  991.     end
  992.     local tEnderSlotsCopy = {}
  993.     --create local copy of ender chest slots table
  994.     for i=1,#tEnderSlots do
  995.       tEnderSlotsCopy[i] = tEnderSlots[i]
  996.     end
  997.     local selected
  998.     local amount = 0 -- amout of items pushed from chest
  999.     if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
  1000.       local chest = peripheral.wrap(chestDir)
  1001.       local chestFunc = chest.pushItemIntoSlot or chest.pushIntoSlot
  1002.       for iTurtle=1,#tTurtleSlotsCopy do
  1003.         local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
  1004.         if turtleCount < 64 then -- only restocks the slot if there is space
  1005.           local iEnder = 1
  1006.           while iEnder <= #tEnderSlotsCopy do --dynamic for loop
  1007.             local stack = peripheral.call(chestDir,"getStackInSlot",tEnderSlotsCopy[iEnder]) or {["qty"] = 0}
  1008.             local removed = false
  1009.             if stack.qty > 0 then -- items in ender chest slot
  1010.               local pushed = chestFunc(pushDir,tEnderSlotsCopy[iEnder],tTurtleSlots == true and stack.qty-1 or stack.qty,tTurtleSlotsCopy[iTurtle])
  1011.               turtleCount = turtleCount+pushed
  1012.               amount = amount+pushed
  1013.               if pushed == (tTurtleSlots == true and stack.qty-1 or stack.qty) then
  1014.                 remove = true
  1015.               end
  1016.               if turtleCount >= 64 then --ends loop if turtle slot is full
  1017.                 break
  1018.               end
  1019.             else -- no items in ender chest slot, slot is removed
  1020.               remove = true
  1021.             end
  1022.             if remove then
  1023.               table.remove(tEnderSlotsCopy,iEnder)
  1024.             else
  1025.               iEnder = iEnder+1
  1026.             end
  1027.           end
  1028.         end
  1029.       end
  1030.     end
  1031.     if tTurtleSlots == true then
  1032.       turtle.select(enderSlot)
  1033.       turtle.refuel(64)
  1034.     end
  1035.     dig(chestDir,enderSlot,true) -- retrieves the ender chest
  1036.     if amount > 0 then
  1037.       return amount
  1038.     else
  1039.       return false --no items pushed
  1040.     end
  1041.   end
  1042.   return false --chest placement failed
  1043. end
  1044.  
  1045. function enderDropoff(enderSlot,tTurtleSlots)
  1046.   --puts down an ender chest from the slot (enderSlot), then empties the specified
  1047.   --turtle slots in the table tTurtleSlots into the chest slots in tEnderSlots
  1048.   turtle.select(enderSlot)
  1049.   local _dir,chestDir = findSpace(true)
  1050.   sleep(0.1)
  1051.   --locate open space for ender chest
  1052.   if chestDir then
  1053.     local pullDir = tNumHeading[dirStandardize("-"..chestDir)]
  1054.     local tTurtleSlotsCopy = {}
  1055.     --create local copy of turtle slots table
  1056.     for i=1,#tTurtleSlots do
  1057.       tTurtleSlotsCopy[i] = tTurtleSlots[i]
  1058.     end
  1059.     local selected
  1060.     local amount = 0 -- amout of items stored in chest
  1061.     if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
  1062.       local chest = peripheral.wrap(chestDir)
  1063.       local chestFunc = chest.pullItemIntoSlot or chest.pullIntoSlot
  1064.       for iTurtle=1,#tTurtleSlotsCopy do
  1065.         local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
  1066.         if turtleCount > 0 then -- only stores occupied slots
  1067.           for iChest=17,chest.getInventorySize() do
  1068.             local stack = chest.getStackInSlot(iChest)
  1069.             stack = stack and stack.qty and stack.maxSize or {["qty"] = 0,["maxSize"] = 64}
  1070.             if stack.qty < stack.maxSize then --space in enderSlot
  1071.               local pulled = chestFunc(pullDir,tTurtleSlotsCopy[iTurtle],64,iChest)
  1072.               turtleCount = turtleCount-pulled
  1073.               amount = amount+pulled
  1074.               if turtleCount < 1 then -- Ends loop if turtle slot is empty
  1075.                 break
  1076.               end
  1077.             end
  1078.           end
  1079.         end
  1080.       end
  1081.     else
  1082.       for iTurtle=1,#tTurtleSlotsCopy do
  1083.         local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
  1084.         if turtleCount > 0 then -- only stores occupied slots
  1085.           drop(chestDir, tTurtleSlotsCopy[iTurtle], turtleCount)
  1086.         end
  1087.       end
  1088.     end
  1089.     dig(chestDir,enderSlot,true) -- retrieves the ender chest
  1090.     if amount > 0 then
  1091.       return amount
  1092.     else
  1093.       return false --no items pulled
  1094.     end
  1095.   end
  1096.   return false --chest placement failed
  1097. end
  1098.  
  1099. function move(DIRECTION,AMOUNT,DIG)
  1100.   --moves turtle in DIRECTION, AMOUNT times, digs if DIG optional slot
  1101.   if AMOUNT == 0 then
  1102.     return
  1103.   end
  1104.   DIRECTION = dirStandardize(DIRECTION)
  1105.   AMOUNT = AMOUNT or 1
  1106.   if AMOUNT < 0 then
  1107.     DIRECTION = dirStandardize("-"..DIRECTION)
  1108.     AMOUNT = -AMOUNT
  1109.   end
  1110.   --function definitions
  1111.   local moveFunc
  1112.   if DIRECTION == tTextDir.backward and not DIG then
  1113.     moveFunc = turtle.back
  1114.   else
  1115.     moveFunc = getTurtleFunc("move",DIRECTION,true)
  1116.   end
  1117.   local detectFunc = getTurtleFunc("detect",DIRECTION)
  1118.   local internalDigFunc = getTurtleFunc("dig",DIRECTION)
  1119.   local digFunc = type(DIG) == "number" and function()
  1120.     turtle.select(DIG)
  1121.     internalDigFunc()
  1122.   end or internalDigFunc
  1123.   local attackFunc = getTurtleFunc("attack",DIRECTION)
  1124.   saveData()
  1125.   local symDir = tNumDir[DIRECTION]
  1126.   local posEntry = string.lower(symDir:sub(1,1))
  1127.   local mathOp = symDir:sub(2,2)
  1128.   --movement calc function
  1129.   local calcFunc = function()
  1130.     tPos[posEntry] = tPos[posEntry] + tonumber(mathOp.."1")
  1131.     tPos.fuel = not unlimitedFuel and tPos.fuel-1 or 9999
  1132.   end
  1133.   tDest[posEntry] = tPos[posEntry] + tonumber(mathOp..AMOUNT)
  1134.   for i=1,AMOUNT do
  1135.     while DIG and detectFunc() do
  1136.       while tSettings.swarm and peripheral.getType(tNumSide[DIRECTION]) == "turtle" do -- swarm turtles check for other turtles
  1137.         sleep(0.5)
  1138.       end
  1139.       digFunc()
  1140.       if DIRECTION == 5 then
  1141.         sleep(tTimer.dig)
  1142.       end
  1143.     end
  1144.     local res = moveFunc()
  1145.     while not res or res == "cancel" do
  1146.       if res == "cancel" then
  1147.         return
  1148.       elseif not unlimitedFuel and tPos.fuel <= 1 then
  1149.         if tSettings.enderFuel then
  1150.           enderRestock(tSettings.enderFuel,true,tSettings.tEnderFuel)
  1151.           tPos.fuel = turtle.getFuelLevel()
  1152.           os.startTimer(tTimer.enderFuel)
  1153.         end
  1154.         if tSettings.autoRefuel and tPos.fuel <= 1 then
  1155.           for i=1,16 do
  1156.             turtle.refuel(64)
  1157.           end
  1158.           tPos.fuel = turtle.getFuelLevel()
  1159.         end
  1160.         if tPos.fuel <= 0 then
  1161.           if eventHandler.refuel then
  1162.             eventHandler.refuel()
  1163.             tPos.fuel = turtle.getFuelLevel()
  1164.           else
  1165.             renderBars("Fuel required! Press any key.")
  1166.             while true do
  1167.               local e = os.pullEvent()
  1168.               if e == "key" then
  1169.                 turtle.refuel(64)
  1170.                 tPos.fuel = turtle.getFuelLevel()
  1171.                 break
  1172.               elseif e == "timer" then
  1173.                 break
  1174.               end
  1175.             end
  1176.           end
  1177.         end
  1178.       else
  1179.         turtle.attack()
  1180.         if DIG then
  1181.           digFunc()
  1182.           attackFunc()
  1183.         end
  1184.         if eventHandler.moveFail then
  1185.           eventHandler.moveFail(tNumDir[DIRECTION])
  1186.         elseif tSettings.renderMove then
  1187.           renderBars("Move "..tNumDir[DIRECTION].." failed. Retry in "..tTimer.moveFail)
  1188.         end
  1189.         sleep(tTimer.moveFail)
  1190.       end
  1191.       res = moveFunc()
  1192.     end
  1193.     calcFunc()
  1194.     tPos.lastMove = DIRECTION
  1195.     saveData()
  1196.     if tSettings.renderMove then
  1197.       refreshLines()
  1198.       renderBars("Moving "..symDir)
  1199.     end
  1200.   end
  1201.   if renderMove then
  1202.     renderBars("Moved "..symDir)
  1203.   end
  1204. end
  1205.  
  1206. function moveTo(COORDS,SYM,DIG)
  1207.   SYM = SYM:lower()
  1208.   tDest[SYM] = COORDS
  1209.   local mathOp
  1210.   local amount = tPos[SYM]-COORDS
  1211.   if amount > 0 then
  1212.     mathOp = "-"
  1213.   elseif amount == 0 then
  1214.     return
  1215.   else
  1216.     mathOp = "+"
  1217.     amount = -amount
  1218.   end
  1219.   move(SYM..mathOp,amount,DIG)
  1220. end
  1221.  
  1222. function moveToXYZ(x,y,z,faceDir,DIG)
  1223.   tDest.x = x
  1224.   tDest.y = y
  1225.   tDest.z = z
  1226.   if faceDir == true then
  1227.     DIG = faceDir
  1228.     faceDir = false
  1229.   end
  1230.   moveTo(x,"x",DIG)
  1231.   moveTo(z,"z",DIG)
  1232.   moveTo(y,"y",DIG)
  1233.   if faceDir then
  1234.     turn(faceDir)
  1235.   end
  1236. end
  1237.  
  1238. function deployGPS(tSlots,x,y,z)
  1239.   --builds a GPS tower at the given coordinates or the current coordinates
  1240.   --[[tSlots = {
  1241.     diskDrive = 1,
  1242.     disk = 2,
  1243.     computer = 3,
  1244.     modem = 4
  1245.   }]]
  1246.   assert(tSlots,"Slots table missing!")
  1247.   assert(tSlots.diskDrive,"diskDrive slot missing!")
  1248.   assert(turtle.getItemCount(tSlots.diskDrive) > 0,"No disk drive in slot "..tSlots.diskDrive.."!")
  1249.   assert(tSlots.disk,"disk slot missing!")
  1250.   assert(turtle.getItemCount(tSlots.disk) > 0,"No disk in slot "..tSlots.disk.."!")
  1251.   assert(tSlots.computer,"computer slot missing!")
  1252.   assert(turtle.getItemCount(tSlots.computer) > 3,"Missing "..(4-turtle.getItemCount(tSlots.computer).." computers in slot "..tSlots.computer.."!"))
  1253.   assert(tSlots.modem,"modem slot missing!")
  1254.   assert(turtle.getItemCount(tSlots.modem) > 3,"Missing "..(4-turtle.getItemCount(tSlots.modem).." modems in slot "..tSlots.modem.."!"))
  1255.   x = x or tPos.x
  1256.   y = math.min((y or tPos.y),252)
  1257.   z = z or tPos.z
  1258.   local function setupHost(hostX,hostZ)
  1259.     place("forward",tSlots.computer)
  1260.     move"up"
  1261.     place("forward",tSlots.modem)
  1262.     place("down",tSlots.diskDrive)
  1263.     assert(peripheral.getType"bottom" == "drive","The block on slot "..tSlots.diskDrive.." was not a disk drive!")
  1264.     drop("down",tSlots.disk)
  1265.     assert(fs.exists"/disk","Missing disk drive in slot "..tSlots.disk)
  1266.     if not fs.exists"/disk/startup" then
  1267.       local diskFile = fs.open("/disk/startup", "w")
  1268.       diskFile.writeLine[[
  1269. if not os.getComputerLabel() then
  1270.   os.setComputerLabel"GPS Host"
  1271. end
  1272. fs.copy("/disk/hostFile", "/startup")
  1273. shell.run"startup"]]
  1274.       diskFile.close()
  1275.     end
  1276.     local hostFile = fs.open("/disk/hostFile", "w")
  1277.       hostFile.writeLine('shell.run"gps host '..hostX..' '..(tPos.y-1)..' '..hostZ..'"')
  1278.     hostFile.close()
  1279.     move"left"
  1280.     move"right"
  1281.     move"down"
  1282.     turn"right"
  1283.     assert(peripheral.getType"front" == "computer","The block in slot "..tSlots.computer.." was not a computer!")
  1284.     peripheral.call("front", "turnOn")
  1285.     move"right"
  1286.     suck("left",tSlots.disk)
  1287.     turtle.select(tSlots.diskDrive)
  1288.     dig("forward")
  1289.   end
  1290.   moveTo(y,"y",true)
  1291.   moveToXYZ(x+4,y,z,"x+",true)
  1292.   setupHost(tPos.x+1,tPos.z)
  1293.   moveToXYZ(x-4,y,z,"x-",true)
  1294.   setupHost(tPos.x-1,tPos.z)
  1295.   moveToXYZ(x,y,z+4,"z+",true)
  1296.   setupHost(tPos.x,tPos.z+1)
  1297.   moveToXYZ(x,y+1,z-4,"z-",true)
  1298.   setupHost(tPos.x,tPos.z-1)
  1299.   move("down",2)
  1300. end
  1301.  
  1302. function deployTurtle(tSlots,tData)
  1303.   --[[tSlots = {
  1304.     diskDrive = 1,
  1305.     disk = 2,
  1306.     turtle = 3,
  1307.     enderChest = 4 -- optional, enables enderRefuel slot 16
  1308.   }
  1309.   tData = {
  1310.     label = "the turtle label derpface",
  1311.     type = "remote" --enables remote modem control on startup
  1312.     type = "swarm" --assigns swarm ID and enables remote control, enderRefuel slot 16 and disables renderMove.
  1313.     swarm = 1 --swarm ID number
  1314.     formation = "wall" or "floor" --swarm formation
  1315.   ]]--
  1316.   tData = tData or {}
  1317.   assert(tSlots,"Slots table missing!")
  1318.   assert(tSlots.diskDrive,"diskDrive slot missing!")
  1319.   assert(tSlots.disk,"disk slot missing!")
  1320.   assert(tSlots.turtle,"turtle slot missing!")
  1321.   place("down",tSlots.diskDrive,true)
  1322.   assert(peripheral.getType"bottom" == "drive", "The block in slot "..tSlots.diskDrive.." was not a diskDrive!")
  1323.   drop("down",tSlots.disk)
  1324.   assert(fs.exists"/disk", "The block in slot "..tSlots.disk.." was not a disk!")
  1325.   --setup cTurtle
  1326.   fs.delete("/disk/"..tFile.cTurtle)
  1327.   fs.copy(tFile.cTurtle,"disk/cTurtle")
  1328.   fs.delete("/disk"..tFile.directory)
  1329.   fs.makeDir("/disk"..tFile.directory)
  1330.   --setup disk bootup file
  1331.   local file = fs.open("/disk/startup","w")
  1332.   file.writeLine([[
  1333. os.setComputerLabel("]]..(tData.label or tData.type or "cTurtle")..[[")
  1334. fs.copy("disk/]]..tFile.cTurtle..[[", "/]]..tFile.cTurtle..[[")
  1335. fs.copy("disk/startupFile","/startup")
  1336. fs.copy("disk]]..tFile.directory..[[","]]..tFile.directory..[[")]]
  1337.   )
  1338.   file.close()
  1339.   if tSlots.enderChest then
  1340.     local file = fs.open("/disk/startup","a")
  1341.     file.writeLine([[
  1342. turtle.select(1)
  1343. turtle.transferTo(16)]]
  1344.     )
  1345.     file.close()
  1346.   end
  1347.   local file = fs.open("/disk/startup","a") --one time actions goes here
  1348.   file.writeLine([[
  1349. sleep(5)
  1350. _G.restarted = false
  1351. shell.run"/startup"
  1352. cTurtle.move"forward"
  1353. cTurtle.move"back"]]
  1354.   )
  1355.   file.close()
  1356.   --setup settings
  1357.   local tNewSettings = {}
  1358.   for k,v in pairs(tSettings) do
  1359.     tNewSettings[k] = v
  1360.   end
  1361.   if tSlots.enderChest then
  1362.     tNewSettings.enderFuel = 16
  1363.   end
  1364.   if tData.type == "swarm" then
  1365.     tNewSettings.renderMove = false
  1366.   end
  1367.   for k,v in pairs(tData) do
  1368.     if tNewSettings[k] ~= nil then
  1369.       tNewSettings[k] = v
  1370.     end
  1371.   end
  1372.   saveSettings("/disk"..tFile.settings,tNewSettings)
  1373.   --setup startup file
  1374.   local file = fs.open("/disk/startupFile","w")
  1375.   file.writeLine([[
  1376. local file = fs.open("]]..tFile.restarted..[[", "w")
  1377. file.writeLine"true"
  1378. file.close()
  1379. os.loadAPI"]]..tFile.cTurtle..[["]]
  1380.   )
  1381.   file.close()
  1382.   --setup remote turtles
  1383.   if tData.type == "remote" then
  1384.     local file = fs.open("/disk/startupFile","a")
  1385.     file.writeLine([[
  1386. cTurtle.modemControl(true)]]
  1387.     )
  1388.     file.close()
  1389.   --setup swarm turtles
  1390.   elseif tData.type == "swarm" then
  1391.     local file = fs.open("/disk/startupFile","a")
  1392.     file.writeLine([[
  1393. cTurtle.modemControl(true)]]
  1394.     )
  1395.     file.close()
  1396.   end
  1397.   --setup location info
  1398.   --locaiton offset correction
  1399.   local tOffs = {
  1400.     x = 0,
  1401.     y = -1,
  1402.     z = 0
  1403.   }
  1404.   local dir = string.lower(tNumDir[tPos.dir])
  1405.   tOffs[dir:sub(1,1)] = tOffs[dir:sub(1,1)] + tonumber(dir:sub(2,2)..1)
  1406.   --save new location
  1407.   saveData(tPos.x+tOffs.x,tPos.y+tOffs.y,tPos.z+tOffs.z,tPos.dir,0,"/disk"..tFile.data)
  1408.   --place turtle
  1409.   move"forward"
  1410.   place("down",tSlots.turtle,true)
  1411.   assert(peripheral.getType"bottom" == "turtle","The block in slot "..tSlots.turtle.." was not a turtle!")
  1412.   if tSlots.enderChest then
  1413.     drop("down",tSlots.enderChest,1)
  1414.   end
  1415.   peripheral.call("bottom","turnOn")
  1416.   --retrieve disk and drive
  1417.   move"back"
  1418.   suck("down",tSlots.disk)
  1419.   turtle.select(tSlots.diskDrive)
  1420.   dig"down"
  1421. end
  1422.  
  1423. function deploySwarm (tSlots,height,width,formation)
  1424.   --[[tSlots = {
  1425.     diskDrive = 1,
  1426.     disk = 2,
  1427.     turtle = 3,
  1428.     enderChest = 4
  1429. }]]--
  1430.   formation = formation or "wall"
  1431.   local frontDir = dirStandardize"forward"
  1432.   local sideDir = dirStandardize"right"
  1433.   local id = 1
  1434.   for h=1,height do
  1435.     for w=1,width do
  1436.       deployTurtle(tSlots,{
  1437.         label = "swarm cTurtle "..id,
  1438.         type = "swarm", --assigns swarm ID and enables remote control, enderRefuel slot 16 and disables renderMove.
  1439.         swarm = id, --swarm ID number
  1440.         formation = formation --swarm formation
  1441.       })
  1442.       id = id+1
  1443.       if w < width then
  1444.         if formation == "wall" then
  1445.           move(sideDir)
  1446.           turn(frontDir)
  1447.         elseif formation == "floor" then
  1448.           move(sideDir)
  1449.           turn(frontDir)
  1450.         end
  1451.       end
  1452.     end
  1453.     sideDir = cTurtle.dirStandardize("-"..sideDir)
  1454.     if formation == "wall" then
  1455.       move"up"
  1456.     elseif formation == "floor" then
  1457.       move"back"
  1458.     end
  1459.   end
  1460. end
  1461.  
  1462. tCommandQueue = {} -- table of queued commands
  1463. function modemControl(enable)
  1464.   --enables a very advanced remote control of the turtle
  1465.   if enable then
  1466.     if modem then
  1467.       if cTurtle.tSettings.swarm then
  1468.         _G.modem.open(tSettings.swarmChannel)
  1469.       else
  1470.         _G.modem.open(tSettings.controlChannel)
  1471.       end
  1472.     else
  1473.       return
  1474.     end
  1475.     oldPull = _G.os.pullEvent
  1476.     _G.os.pullEvent = function(filter) -- override original pullEvent
  1477.       while true do
  1478.         local tEvent = {oldPull()}
  1479.         if tEvent[1] == "modem_message"
  1480.         and tEvent[3] == tSettings.controlChannel
  1481.         or tEvent[3] == tSettings.swarmChannel
  1482.         and type(tEvent[5]) == "table"
  1483.         and (tEvent[5].id == "all" or tEvent[5].id == tSettings.swarm) then
  1484.           tCommandQueue[#tCommandQueue+1] = tEvent
  1485.           if not tCommandQueue[2]
  1486.           or tEvent[5] == "cancel" then
  1487.             while tCommandQueue[1] do
  1488.               local oldRenderMove = tSettings.renderMove
  1489.               tSettings.renderMove = false
  1490.               local tArg = {}
  1491.               if tEvent[5] == "cancel" then --remote cancel
  1492.                 os.queueEvent("cTurtle","cancel")
  1493.               elseif type(tEvent[5]) == "table" then --table commands
  1494.                 if tEvent[5][1] == "cTurtle update" then --update cTurtle through modem message
  1495.                   table.remove(tEvent[5],1)
  1496.                   fs.open(tFile.cTurtle,"w").writeLine(table.concat(tEvent[5],"\n"))
  1497.                   os.reboot()
  1498.                 else -- function command
  1499.                   tArg[1] = tEvent[5][1]
  1500.                   for i=2,#tEvent[5] do
  1501.                     if type(tEvent[5][i]) == "table" then
  1502.                       tArg[i] = "{"
  1503.                       for k,v in pairs(tEvent[5][i]) do
  1504.                         tArg[i] = tArg[i].." "..k.." = "..v..","
  1505.                       end
  1506.                       tArg[i] = tArg[i].."}"
  1507.                     elseif type(tEvent[5][i]) == "string" then
  1508.                       tArg[i] = '"'..tEvent[5][i]..'"' --add quotation marks to strings
  1509.                     elseif tEvent[5][i] == true then
  1510.                       tArg[i] = "true" --change boolean to string
  1511.                     elseif tEvent[5][i] == false then
  1512.                       tArg[i] = "false" --change boolean to string
  1513.                     else
  1514.                       tArg[i] = tEvent[5][i]  --numbers
  1515.                     end
  1516.                   end
  1517.                 end
  1518.               else --string function command
  1519.                 tArg[1] = tEvent[5]:match"^%S+"
  1520.                 tEvent[5] = tEvent[5]:sub(#tArg[1]+2,#tEvent[5])
  1521.                 for word in tEvent[5]:gmatch"%S+" do --split message into words
  1522.                   if word:match"^%d+$" then --convert numbers to number format
  1523.                     word = tonumber(word)
  1524.                   elseif word:match"true" then --convert booleans to string
  1525.                     word = "true"
  1526.                   elseif word:match"false" then
  1527.                     word = "false"
  1528.                   else
  1529.                     word = '"'..word..'"' --add quotation marks to strings
  1530.                   end
  1531.                   table.insert(tArg,word)
  1532.                 end
  1533.               end
  1534.               if tArg[1] then --command
  1535.                 local funcString = ""
  1536.                 funcString = "cTurtle."..table.remove(tArg,1).."(" --write function
  1537.                 for i=1,#tArg do
  1538.                   local arg = tArg[i]
  1539.                   if i > 1 then
  1540.                     funcString = funcString.."," --commas if multipe args
  1541.                   end
  1542.                   funcString = funcString..arg
  1543.                 end
  1544.                 funcString = funcString..")" --end function
  1545.                 local func = loadstring("setfenv(1,_G) "..funcString) -- create function
  1546.                 local tRes = {pcall(func)}
  1547.                 local str = funcString
  1548.                 if tRes[1] then -- no errors
  1549.                   table.remove(tRes,1) --remove error index
  1550.                   for i=1,#tRes do --convert return values to string
  1551.                     if tRes[i] == true then
  1552.                       str = str.."|True "
  1553.                     elseif tRes[i] == false then
  1554.                       str = str.."|False "
  1555.                     elseif type(tRes[i]) == "table" then
  1556.                       for k,v in pairs(tRes[i]) do
  1557.                         str = str.."|"..k.."="..v.." "
  1558.                       end
  1559.                     elseif tRes[i] then
  1560.                       str = str.."|"..tRes[i].." "
  1561.                     end
  1562.                   end
  1563.                 else --function failed
  1564.                   str = str.."|"..tRes[2]
  1565.                 end
  1566.                 if tEvent[3] == tSettings.controlChannel then
  1567.                   modem.transmit(tSettings.controlResponseChannel,tSettings.controlChannel,{
  1568.                     response = str,
  1569.                     tPos = tPos,
  1570.                     turtleId = os.getComputerID()
  1571.                   }) --send return values
  1572.                 end
  1573.                 renderMove = oldRenderMove
  1574.               end
  1575.               table.remove(tCommandQueue,1)
  1576.               if tCommandQueue[1] then -- command in queue
  1577.                 tEvent = tCommandQueue[1]
  1578.               end
  1579.             end
  1580.           end
  1581.         elseif not filter or filter == tEvent[1] then
  1582.           return unpack(tEvent) -- return event params
  1583.         end
  1584.       end
  1585.     end
  1586.     if tSettings.swarm then
  1587.       _G.modem.transmit(tSettings.controlResponseChannel,tSettings.swarmChannel,"Swarm turtle #"..tSettings.swarm.." online.")
  1588.     else
  1589.       _G.modem.transmit(tSettings.controlResponseChannel,tSettings.controlChannel,"Turtle #"..os.getComputerID().." online.")
  1590.     end
  1591.   else
  1592.     _G.modem.close(tSettings.swarmChannel)
  1593.     _G.modem.close(tSettings.controlChannel)
  1594.     _G.os.pullEvent = oldPullEvent --restore original pullEvent
  1595.   end
  1596. end
  1597.  
  1598. --API init
  1599. if not fs.exists(tFile.directory) then
  1600.   fs.makeDir(tFile.directory)
  1601. end
  1602. if not fs.exists(tFile.settings) then
  1603.   saveSettings()
  1604. end
  1605. if not fs.exists"/startup" then
  1606.   local file = fs.open("/startup","w")
  1607.   file.writeLine([[
  1608. local file = fs.open("]]..tFile.restarted..[[", "w")
  1609. file.writeLine"true"
  1610. file.close()
  1611. _G.restarted = true
  1612. ]])
  1613.   file.close()
  1614.   _G.restarted = true
  1615. else
  1616.   local file = fs.open("/startup","r")
  1617.   local sFile = file.readAll()
  1618.   file.close()
  1619.   if not sFile:match([[fs%.open%("]]..tFile.restarted..[[", "w"%)]]) then
  1620.     local file = fs.open("/startup","a")
  1621.     file.writeLine([[
  1622. local file = fs.open("]]..tFile.restarted..[[", "w")
  1623. file.writeLine"true"
  1624. file.close()
  1625. _G.restarted = true
  1626. ]])
  1627.     file.close()
  1628.     _G.restarted = true
  1629.   end
  1630.   local file = fs.open(tFile.restarted, "w")
  1631.   file.writeLine"true"
  1632.   file.close()
  1633. end
  1634.  
  1635. if not _G.restarted then
  1636.   local file = fs.open(tFile.restarted,"r")
  1637.   _G.restarted = file.readLine() == "true"
  1638.   file.close()
  1639. end
  1640.  
  1641. if not _G.modem then --auto wrap turtle modem, for ease of use
  1642.   for k,side in pairs(peripheral.getNames()) do
  1643.     if peripheral.getType(side) == "modem" then
  1644.       _G.modem = peripheral.wrap(side)
  1645.       _G.modem.open(gps.CHANNEL_GPS) --open GPS channel
  1646.       break
  1647.     end
  1648.   end
  1649. end
  1650. locate()
  1651. local tArg = {...}
  1652. if tArg[1] then
  1653.   --shell usage
  1654.   for i=2,#tArg do
  1655.     if tArg[i]:match"^%d+$" then
  1656.       tArg[i] = tonumber(tArg[i])
  1657.     else
  1658.       tArg[i] = '"'..tArg[i]..'"'
  1659.     end
  1660.   end
  1661.   local func = loadstring(table.remove(tArg,1).."("..table.concat(tArg,",")..")")
  1662.   setfenv(func,getfenv())
  1663.   term.setBackgroundColor(colors.black)
  1664.   term.clear()
  1665.   func()
  1666.   term.clear()
  1667.   term.setCursorPos(1,1)
  1668. end
Add Comment
Please, Sign In to add comment