Skip to content

Instantly share code, notes, and snippets.

@Pyeroh
Last active October 24, 2021 20:49
Show Gist options
  • Save Pyeroh/81e88be13c819d612530f59b7d173c20 to your computer and use it in GitHub Desktop.
Save Pyeroh/81e88be13c819d612530f59b7d173c20 to your computer and use it in GitHub Desktop.
cTurtle
assert(turtle,"This API may only be used on turtles!")
local ver = 2.5
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
local tFile = {
--file path table
["cTurtle"] = "cTurtle"
}
tFile.directory = "/"..tFile.cTurtle.."Files"
tFile.data = tFile.directory.."/data"
tFile.restarted = tFile.directory.."/restarted"
tFile.settings = tFile.directory.."/settings"
local unlimitedFuel = turtle.getFuelLevel() == "unlimited"
tSettings = {}
function saveSettings(path,tTable)
path = path or tFile.settings
tTable = tTable or tSettings
local file = fs.open(path,"w")
for k,v in pairs(tTable) do
if type(v) == "table" then
v = textutils.serialize(v):gsub("\n%s-","")
elseif v == true then
v = "true"
elseif v == false then
v = "false"
end
file.writeLine(k.." = "..v)
end
file.close()
end
local function loadSettings(path,tTable)
path = path or tFile.settings
tTable = tTable or tSettings
local file = fs.open(path,"r")
local line = file.readLine()
while line do
local k = line:match"^%S+"
local v = line:sub(#k+4,#line)
if v:match"^%d+$" then --number
v = tonumber(v)
elseif v == "true" then --true
v = true
elseif v == "false" then --false
v = false
elseif v:match"^{.-}$" then --table
v = textutils.unserialize(v)
end
tTable[k] = v
line = file.readLine()
end
file.close()
end
--settings, configure these from external programs using cTurtle.tSettings.setting = setting then cTurtle.saveSettings()
if fs.exists(tFile.settings) then
loadSettings()
else
tSettings.renderMove = true --wether to render info page on movement or not
tSettings.enderFuel = false --slot of enderchest containing fuel
tSettings.autoRefuel = false --wether to check inventory for fuel before stopping when out of fuel
tSettings.controlChannel = 9280 --channel for external modem control, modemControl(true) must be called prior to usage
tSettings.controlResponseChannel = 9281 --channel for modem responses
tSettings.swarmChannel = 9290 --channel for swarm controls
tSettings.swarmResponseChannel = 9291 --channel for swarm responses
tSettings.swarm = false --swarm number. Swarm turtles will not break other turtles.
tSettings.formation = false --swarm formation
tSettings.tEnderFuel = {} -- slots to check enderchest for fuel
for i=17,54 do
tSettings.tEnderFuel[#tSettings.tEnderFuel+1] = i
end
end
tTimer = {
--timer table
["inputCoords"] = 20, --time to cancel coordinate input
["dig"] = 0.1, --time to wait after digging a block, incase of sand or gravel
["moveFail"] = 0.5, --time to wait if not digging and path is blocked
["enderFuel"] = 60 --time between each enderchest fuel check.
}
--init tables
local tColors
--color table
if term.isColor() then
tColors = {
["screen"] = colors.lightBlue,
["text"] = colors.white,
["textBackground"] = colors.black,
["topBar"] = colors.blue,
["bottomBar"] = colors.blue,
["topBarText"] = colors.yellow,
["bottomBarText"] = colors.yellow
}
else
tColors = {
--black & white color table
["screen"] = colors.white,
["text"] = colors.white,
["textBackground"] = colors.black,
["topBar"] = colors.black,
["bottomBar"] = colors.black,
["topBarText"] = colors.white,
["bottomBarText"] = colors.white
}
end
local tText = {
--string table
["topBar"] = "cTurtle ver: "..ver,
["x"] = "X: ",
["y"] = "Y: ",
["z"] = "Z: ",
["dir"] = "Dir: ",
["fuel"] = "Fuel: ",
["failedGPS"] = "No GPS, please input co-ordinates"
}
local tTerm = {}
--terminal size table
tTerm.x,tTerm.y = term.getSize()
tTerm.xMid = tTerm.x/2
tTerm.yMid = tTerm.y/2
local tCurs = {
--cursor table
["x"] = 1,
["y"] = 1
}
tDir = {
--direction conversion table
["X+"] = {
"x+","-x-","1","+1","-3","east","-west","e","-w"
},
["X-"] = {
"x-","-x+","3","+3","-1","west","-east","w","-e"
},
["Z+"] = {
"z+","-z-","2","+2","-4","south","-north","s","-n"
},
["Z-"] = {
"z-","-z+","4","+4","-2","north","-south","n","-s"
},
["Y+"] = {
"y+","-y-","up","-down","top","-bottom","5","+5","-6"
},
["Y-"] = {
"y-","-y+","down","-up","bottom","-top","6","+6","-5"
}
}
tDirText = {
--text to direction conversion table
["forward"] = {
"forward","front","ahead","-back","-backward","-backwards","-behind"
},
["backward"] = {
"back","backward","backwards","behind","around","-front","-forward","-ahead"
},
["left"] = {
"left","-right"
},
["right"] = {
"right","-left"
}
}
tNumDir = {
--number to symbol conversion table
"X+",
"Z+",
"X-",
"Z-",
"Y+",
"Y-"
}
tNumHeading = {
--number to heading conversion table
"east",
"south",
"west",
"north",
"up",
"down"
}
tDirNum = {
--symbol to number conversion table
["X+"] = 1,
["X-"] = 3,
["Z+"] = 2,
["Z-"] = 4,
["Y+"] = 5,
["Y-"] = 6
}
tNumSide = {
--number to turtle side conversion table
[1] = "front",
[2] = "front",
[3] = "front",
[4] = "front",
[5] = "top",
[6] = "bottom"
}
tPos = {} --location table
tDest = {} --destination table
--custom native turtle functions
tPos.selectedSlot = 1
turtle.select(1)
local turtleSelect = _G.turtle.select
_G.turtle.select = function(slot)
turtleSelect(slot)
tPos.selectedSlot = slot
end
native = turtle.native or turtle
eventHandler = {} --stores custom event handler functions
function clearEventHandler(skipcTurtle) --idfk why i can't just redef it....
local tKey = {}
for k in pairs(eventHandler) do
if not skipcTurtle
or (k ~= "refuel" and k ~= "moveFail") then
tKey[#tKey+1] = k
end
end
for i=1,#tKey do --fuck you next
eventHandler[tKey[i]] = nil
end
end
local function waitForResponse(id) --modified to not throw away non-turtle events and handle cTurtle events
local resBool,resString
while true do
local tEvent = {os.pullEvent()}
if tEvent[1] == "turtle_response" then
if tEvent[2] == id then
if tEvent[3] then
resBool = true
else
resBool,resString = false, tEvent[4]
end
return resBool,resString
end
elseif tEvent[1] == "cTurtle" then
return tEvent[2]
elseif eventHandler[tEvent[1]] then
eventHandler[tEvent[1]](tEvent)
end
end
end
local function wrap(sCommand) --get native function
return turtleOS >= 1.6 and native[sCommand]
or function(...)
local id = native[sCommand](...)
if id == -1 then
return false
end
return waitForResponse(id)
end
end
-- Wrap standard commands
local turtle = {}
turtle["getItemCount"] = native.getItemCount
turtle["getItemSpace"] = native.getItemSpace
turtle["getFuelLevel"] = native.getFuelLevel
for k,v in pairs( native ) do
if type( k ) == "string" and type( v ) == "function" and turtle[k] == nil then
turtle[k] = wrap( k )
end
end
-- Wrap peripheral commands
if peripheral.getType("left") == "workbench" then
turtle["craft"] = function( ... )
local id = peripheral.call( "left", "craft", ... )
return waitForResponse( id )
end
elseif peripheral.getType("right") == "workbench" then
turtle["craft"] = function( ... )
local id = peripheral.call( "right", "craft", ... )
return waitForResponse( id )
end
end
--internal API functions
tTextDir = {}
local function updateDirs()
--updates table containing current directions
tTextDir.forward = tPos.dir
tTextDir.right = dirAdd(tPos.dir)
tTextDir.left = dirSub(tPos.dir)
tTextDir.backward = dirAdd(tPos.dir,2)
return tTextDir
end
local function getTurtleFunc(FUNCTION,DIRECTION,oneTurn)
--simplified function for acessing all turtle functions with cTurtle's direction capabilities
assert(DIRECTION,"No direction given for function "..FUNCTION)
DIRECTION = dirStandardize(DIRECTION)
if DIRECTION < 5 then -- not Y+ or Y-
if FUNCTION == "move" then
FUNCTION = "forward"
end
if oneTurn == true then
turn(DIRECTION)
return turtle[FUNCTION]
elseif oneTurn == false then
return turtle[FUNCTION]
else
return function()
turn(DIRECTION)
return turtle[FUNCTION]()
end
end
elseif DIRECTION == 5 then --Y+
if FUNCTION == "move" then
return turtle.up
end
return turtle[FUNCTION.."Up"]
elseif DIRECTION == 6 then -- Y-
if FUNCTION == "move" then
return turtle.down
end
return turtle[FUNCTION.."Down"]
end
end
local function turnRight(AMOUNT)
--proper right turn
AMOUNT = AMOUNT or 1
if AMOUNT < 1 then
AMOUNT = -AMOUNT
end
for i=1,AMOUNT do
tPos.dir = dirAdd(tPos.dir,1)
saveData()
--updates data prior to turn, to compensate for crash during turn
turtle.turnRight()
if renderMove then
refreshLines()
end
end
updateDirs()
end
local function turnLeft(AMOUNT)
--proper left turn
AMOUNT = AMOUNT or 1
if AMOUNT < 1 then
AMOUNT = -AMOUNT
end
for i=1,AMOUNT do
tPos.dir = dirSub(tPos.dir,1)
saveData()
--updates data prior to turn, to compensate for crash during turn
turtle.turnLeft()
if renderMove then
refreshLines()
end
end
updateDirs()
end
local function sleep(time)
-- modified to not throw away events and cancel cTurtle events if c is pressed
local timerId = os.startTimer(time or 0)
while true do
local tEvent = {os.pullEvent()}
if tEvent[1] == "timer" and tEvent[2] == timerId then
return time
elseif tEvent[1] == "key" and tEvent[2] == keys.c then --c
os.queueEvent("cTurtle","cancel")
else
os.queueEvent(unpack(tEvent))
end
end
end
--API functions
function update(swarm)
if swarm then --update swarm turtles through rednet
local cTurtleFile = {
id = "all",
[1] = "cTurtle update"
}
for line in fs.open(tFile.cTurtle,"r").readLine do
cTurtleFile[#cTurtleFile+1] = line
end
_G.modem.transmit(tSettings.swarmChannel,tSettings.swarmResponseChannel,cTurtleFile)
else --update self
local response,paste = http.get("https://gist.github.com/Pyeroh/81e88be13c819d612530f59b7d173c20/raw/cTurtle.lua")
if response then
paste = response.readAll()
response.close()
--save to file
local file = fs.open(tFile.cTurtle,"w")
file.writeLine(paste)
file.close()
os.reboot()
else
return false
end
end
end
tInput = {}
function input()
--enables co-ordinate input
local tFieldOrder = {"x","y","z","dir"}
for i=1,#tFieldOrder do
--creates input field table
local key,value = tFieldOrder[i],tPos[tFieldOrder[i]]
if key == "dir" and string.match(string.format(value),"%d") then
value = tNumDir[value]
end
tInput[i] = {}
tInput[i]["value"] = string.format(value)
tInput[i]["field"] = tText[key]
tInput[i]["startX"] = #tInput[i]["field"]+2
end
term.setBackgroundColor(tColors.screen)
term.clear()
refreshLines()
renderBars(tText.failedGPS)
tCurs.x,tCurs.Y = 0,1
term.setCursorPos(tInput[tCurs.y]["startX"],tCurs.y*2+1)
term.setCursorBlink(true)
local timeOut = os.startTimer(tTimer.inputCoords)
while true do
--user input begins
local tEvent = {os.pullEvent()}
if timeOut
and (tEvent[1] == "key" or tEvent[1] == "mouse_click" or tEvent[1] == "char") then
--cancel timeout
timeOut = nil
end
if tEvent[1] == "mouse_click" then
if tEvent[2] == 1 then
local clickY = (tEvent[4]-1)/2
if tFieldOrder[clickY] and tEvent[3] < tTerm.xMid-1 then
local clickX = tEvent[3]-#tInput[clickY]["field"]-1
if clickX > 0 then
tCurs.y = clickY
tCurs.x = math.min(#tInput[clickY]["value"],clickX-1)
end
end
end
elseif tEvent[1] == "key" then
--special keyes
if tEvent[2] == keys.up then --up arrow
tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]
tCurs.y = tCurs.y-1
tCurs.x = tCurs.x-#tInput[math.max(1,tCurs.y)]["field"]
elseif tEvent[2] == keys.down then --down arrow
tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]
tCurs.y = tCurs.y+1
tCurs.x= tCurs.x-#tInput[math.min(#tInput,tCurs.y)]["field"]
elseif tEvent[2] == keys.left then --left arrow
tCurs.x = tCurs.x-1
elseif tEvent[2] == keys.right then --right arrow
tCurs.x = tCurs.x+1
elseif tEvent[2] == keys.enter then --enter
if tCurs.y >= #tInput then
--input complete
if dirStandardize(tInput[4]["value"]) then
break
else
renderBars"Faulty direction."
end
else
tCurs.x = tCurs.x+#tInput[tCurs.y]["field"]-#tInput[tCurs.y+1]["field"]
tCurs.y = tCurs.y+1
end
elseif tEvent[2] == keys.backspace and tCurs.x > 0 then --backspace
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"])
tCurs.x = tCurs.x-1
elseif tEvent[2] == keys["end"] then --end
tCurs.x = #tInput[tCurs.y]["value"]
elseif tEvent[2] == keys.home then --home
tCurs.x = 1
elseif tEvent[2] == keys.delete then --delete
if #tInput[tCurs.y]["value"] == 1 and tCurs.x <= 0 then
tInput[tCurs.y]["value"] = ""
else
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"])
end
end
--cursor check
tCurs.y = math.min(#tInput,tCurs.y)
tCurs.y = math.max(1,tCurs.y)
tCurs.x = math.min(#tInput[tCurs.y]["value"],tCurs.x)
tCurs.x = math.max(0,tCurs.x)
elseif tEvent[1] == "char" then
local input
if #tInput[tCurs.y]["value"]+#tInput[tCurs.y]["field"] >= tTerm.xMid-3 then
--limits amount of characters to bar size
elseif tEvent[2]:match"[%+%-]" and tInput[tCurs.y]["value"]:match"[%+%-]" then
--only one of either + or -
elseif tInput[tCurs.y]["field"]:match"Dir:" and tEvent[2]:match"[XZxznorthNORTHsuSUweWEaA%+%-]" then
--input to direction field, accepts +- and letters related to directions
if tEvent[2]:match"[%+%-]" and tCurs.x ~= 1
or tEvent[2]:match"[%+%-]" and #tInput[tCurs.y]["value"] > 1 then
-- + and - can only be at the end of single char directions
else
input = true
end
elseif tEvent[2]:match"[0-9%+%-]" then
--other fields, only accept numbers and +-
if tEvent[2]:match"[%+%-]" and tCurs.x ~= 0
or tInput[tCurs.y]["value"]:match"[%+%-]" and tCurs.x == 0 then
-- + and - can only be at start
else
input = true
end
end
if input then
--input accepted
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"])
tCurs.x = tCurs.x+1
end
elseif tEvent[1] == "timer" then
--timers
if tEvent[2] == timeOut then
break
end
end
--updates current field on user input
renderLine(tCurs.y)
term.setCursorPos(tInput[tCurs.y]["startX"]+tCurs.x,tCurs.y*2+1)
term.setCursorBlink(true)
end
--input complete, storing data
tPos.x = tonumber(tInput[1]["value"])
tPos.y = tonumber(tInput[2]["value"])
tPos.z = tonumber(tInput[3]["value"])
tPos.lastMove = dirStandardize(tInput[4]["value"])
tPos.dir = tPos.lastMove
term.setCursorBlink(false)
term.setTextColor(colors.white)
term.setBackgroundColor(colors.black)
term.clear()
term.setCursorPos(1,1)
end
function locate(FORCE)
--gets turtle coordinates
local x,y,z,dir,x2,y2,z2
if _G.restarted or FORCE then
--re-gets coordinates
--via GPS
x,y,z = gps.locate(3)
if x and (not unlimitedFuel and turtle.getFuelLevel() > 0 or unlimitedFuel) then
--GPS sucess, getting direction by moving
local moved,down = false,false
while not moved do
while not unlimitedFuel and turtle.getFuelLevel() <= 0 do
if tSettings.enderFuel then
enderRestock(tSettings.enderFuel,true,tSettings.tEnderFuel)
os.startTimer(tTimer.enderFuel)
end
if tSettings.autoRefuel and turtle.getFuelLevel() <= 0 then
for i=1,16 do
turtle.refuel(64)
end
end
if turtle.getFuelLevel() <= 0 then
if eventHandler.refuel then
eventHandler.refuel()
else
renderBars("Fuel required! Press any key.")
while true do
local e = os.pullEvent()
if e == "key" then
turtle.refuel(64)
break
elseif e == "timer" then
break
end
end
end
end
end
for i=1,4 do
if turtle.forward() then
moved = true
break
else
turtle.turnLeft()
end
end
if (turtle.detectUp() or down)
and not moved then
turtle.down()
down = true
elseif not moved then
turtle.up()
end
end
-- get new posistion
x2,y2,z2 = gps.locate(3)
-- calculate direction
if x2 and (x ~= x2 or z ~= z2) then --incase second GPS fails
if x < x2 then
dir = "X+"
elseif x > x2 then
dir = "X-"
elseif z < z2 then
dir = "Z+"
elseif z > z2 then
dir = "Z-"
end
tPos.x = x2
tPos.z = z2
tPos.y = y2
tPos.dir = tDirNum[dir]
tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
end
end
if not x
or not x2
or x == x2 and z == z2
or not dir then
--GPS failed, attempting player input
if fs.exists(tFile.data) then
loadData()
else
tPos.x = (x or 0)
tPos.y = (y or 0)
tPos.z = (z or 0)
tPos.dir = 1
tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
end
tDest.x = tPos.x
tDest.y = tPos.y
tDest.z = tPos.z
input()
end
saveData()
_G.restarted = false
local file = fs.open(tFile.restarted,"w")
file.writeLine"false"
file.close()
else
--non-restarted call to locate
loadData()
end
tDest.x = tPos.x
tDest.y = tPos.y
tDest.z = tPos.z
return tPos
end
function renderBars(BOTTOMTEXT)
--render top and bottom bar
BOTTOMTEXT = BOTTOMTEXT or " "
paintutils.drawLine(1,1,tTerm.x,1,tColors.topBar)
paintutils.drawLine(1,tTerm.y,tTerm.x,tTerm.y,tColors.bottomBar)
term.setCursorPos(tTerm.xMid-(#tText.topBar/2),1)
term.setTextColor(tColors.topBarText)
term.setBackgroundColor(tColors.topBar)
term.write(tText.topBar)
term.setCursorPos(tTerm.xMid-(#BOTTOMTEXT/2),tTerm.y)
term.setTextColor(tColors.bottomBarText)
term.setBackgroundColor(tColors.bottomBar)
term.write(BOTTOMTEXT)
term.setTextColor(tColors.text)
term.setBackgroundColor(tColors.textBackground)
end
function refreshLines()
term.setTextColor(tColors.text)
term.setBackgroundColor(tColors.textBackground)
local tFieldOrder = {"x","y","z","dir","fuel","x","y","z"}
for i,k in pairs(tFieldOrder) do
if i <= 5 then
--pos, dir and fuel bars
local value = tPos[k]
if k == "dir" and string.match(string.format(value),"%d") then
value = tNumDir[value]
end
paintutils.drawLine(2,i*2+1,tTerm.xMid-1,i*2+1,tColors.textBackground)
term.setCursorPos(2,i*2+1)
term.write(string.sub(k:upper(),1,1)..k:sub(2,#k)..": "..value)
else
--dest bars
local value = tDest[k]
paintutils.drawLine(tTerm.xMid+1,(i-5)*2+1,tTerm.x-1,(i-5)*2+1,tColors.textBackground)
term.setCursorPos(tTerm.xMid+1,(i-5)*2+1)
term.write("Dest"..k:upper()..": "..value)
end
end
end
function renderLine(LINE)
--renders input lines
paintutils.drawLine(2,LINE*2+1,tTerm.xMid-1,LINE*2+1,tColors.textBackground)
term.setCursorPos(2,LINE*2+1)
term.write(tInput[LINE]["field"]..tInput[LINE]["value"])
end
function loadData()
--loads position data from file
local file = fs.open(tFile.data,"r")
file.readLine() --skip first line
tPos.x = tonumber(string.sub(file.readLine(),4,10))
tPos.y = tonumber(string.sub(file.readLine(),4,10))
tPos.z = tonumber(string.sub(file.readLine(),4,10))
tPos.dir = tDirNum[string.sub(file.readLine(),6,7)]
tPos.fuel = tonumber(string.sub(file.readLine(),7,13))
tPos.lastMove = string.sub(file.readLine(),12,13)
if not unlimitedFuel and turtle.getFuelLevel() < tPos.fuel and _G.restarted then
--fuel missing, correcting co-ordinates accordingly
local lastMove = string.lower(tPos.lastMove)
tPos[lastMove:sub(1,1)] = tPos[lastMove:sub(1,1)] + tonumber(lastMove:sub(2,2)..1)
tPos.fuel = turtle.getFuelLevel()
tPos.lastMove = tDirNum[tPos.lastMove]
saveData()
else
tPos.fuel = unlimitedFuel and 9999 or turtle.getFuelLevel()
tPos.lastMove = tDirNum[tPos.lastMove]
end
return tPos
end
function saveData(x,y,z,dir,fuel,path)
x = x or tPos.x
y = y or tPos.y
z = z or tPos.z
dir = dir or tPos.dir
fuel = fuel or tPos.fuel
path = path or tFile.data
--saves position data to file
local file = fs.open(path,"w")
file.writeLine"cTurtle data file."
file.writeLine("X: "..x)
file.writeLine("Y: "..y)
file.writeLine("Z: "..z)
file.writeLine("Dir: "..tNumDir[dir])
file.writeLine("Fuel: "..(tPos.fuel or unlimitedFuel and 9999 or turtle.getFuelLevel()))
file.writeLine("Last move: "..tNumDir[(tPos.lastMove or dir)])
file.close()
end
function dirStandardize(DIRECTION)
--formats the given direction for usage
assert(DIRECTION,"No direction given!")
DIRECTION = string.format(string.lower(DIRECTION))
if tPos.dir then
updateDirs()
for k,v in pairs(tDirText) do
for i=1,#tDirText[k] do
if DIRECTION == tDirText[k][i] then
--checks for forward, left, right, etc.
return tonumber(tTextDir[k])
end
end
end
end
for k,v in pairs(tDir) do
--checks for x+,x-,z+,north,south etc.
for i=1,#tDir[k] do
if DIRECTION == tDir[k][i] then
return tonumber(tDirNum[k])
end
end
end
--failed to format direction
error("Failed to format the given direction: "..DIRECTION,2)
end
function dirAdd(NUM,ADDNUM)
--direction num right turn
NUM = NUM+(ADDNUM or 1)
if NUM > 4 then
NUM = NUM-4
end
return NUM
end
function dirSub(NUM,SUBNUM)
--direction num left turn
NUM = NUM-(SUBNUM or 1)
if NUM < 1 then
NUM = 4+NUM
end
return NUM
end
function turn(DIRECTION)
DIRECTION = dirStandardize(DIRECTION)
if DIRECTION > 4 then
return false --unable to turn Y+, Y-
end
if tSettings.renderMove then
renderBars("Turning "..tNumDir[DIRECTION])
end
if DIRECTION == dirSub(tPos.dir,1) then
--left turn when turning one left
turnLeft(1)
else
local turns = 0
while dirAdd(tPos.dir,turns) ~= DIRECTION do
turns = turns+1
end
--otherwise turn right
turnRight(turns)
end
if tSettings.renderMove then
renderBars("Turn complete")
end
return true
end
function detect(DIRECTION)
return getTurtleFunc("detect",DIRECTION,true)()
end
function dig(DIRECTION,SLOT,LOOP)
if SLOT then
turtle.select(SLOT)
end
local digFunc = getTurtleFunc("dig",DIRECTION,true)
local detectFunc = getTurtleFunc("detect",DIRECTION,false)
if LOOP then
digFunc()
while detectFunc() do
digFunc()
end
return true
end
return digFunc()
end
function compare(DIRECTION,SLOT)
if SLOT then
turtle.select(SLOT)
end
return getTurtleFunc("compare",DIRECTION,true)()
end
function place(DIRECTION,SLOT,LOOP)
if SLOT then
turtle.select(SLOT)
end
local func = getTurtleFunc("place",DIRECTION,true)
if loop then
while not func() do
end
return true
else
return getTurtleFunc("place",DIRECTION,true)()
end
end
function suck(DIRECTION,SLOT)
if SLOT then
turtle.select(SLOT)
end
return getTurtleFunc("suck",DIRECTION,true)()
end
function drop(DIRECTION,SLOT,AMOUNT)
if SLOT then
turtle.select(SLOT)
end
return getTurtleFunc("drop",DIRECTION,true)(AMOUNT or 64)
end
function select(SLOT)
return turtle.select(SLOT)
end
function replace(DIRECTION,SLOT,DIGSLOT)
if not compare(DIRECTION,SLOT) then
dig(DIRECTION,DIGSLOT,true)
turtle.select(SLOT or tPos.selectedSlot)
end
place(DIRECTION,SLOT)
end
function findSpace(place)
--finds an open space and returns direction, places slected block if place
if not turtle.detectUp() then
while place and not turtle.placeUp() do end
return 5,"top"
elseif not turtle.detectDown() then
while place and not turtle.placeDown() do end
return 6,"bottom"
elseif not turtle.detect() then
while place and not turtle.place() do end
return tPos.dir,"front"
else
for i=1,3 do
turn"right"
if not turtle.detect() then
while place and not turtle.place() do end
return tPos.dir,"front"
end
end
end
return false
end
function enderInteract(enderSlot,tItemsOut,tItemsIn)
--[[puts down an ender chest from the slot (enderSlot), iteracts with it according to the tItems table
using the following structure
tItemsOut = { --push items into chest
"Coal","Charcoal", --you can either store the name of the item directly in an index
1,2,3,4,5 --or the number of specific slots
{ --or use a table for more advanced options
name = "Stone", --display name of the desired item, slot numbers work aswell
amount = 128, --optional, amount of items to attempt to deposit
slot = 1, --optional, slot to deposit the items to
slot = {1,2,3,4,5} --or a table may be used for multiple slots
}
},
tItemsIn = { --pull in specific items
"Coal","Charcoal", --you can either store the name of the item directly in an index
1,2,3,4,5 --or the number of specific slots to pull from
{ --or use a table for more advanced options
name = "Stone", --display name of the desired item, slot numbers work aswell
amount = 128, --optional,amount of items to attempt to retrieve
slot = 1, --optional,slot to store the items in
slot = {1,2,3,4,5} --or a table may be used for multiple slots
}
}
]]
turtle.select(enderSlot)
local _dir,chestDir = findSpace(true)
sleep(0.05)
--locate open space for ender chest
if chestDir then
local pushDir = tNumHeading[dirStandardize("-"..chestDir)]
local selected
local amount = 0 -- amout of items pushed from chest
if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
tItemsOut = tItemsOut or {}
tItemsIn = tItemsIn or {}
for k,v in pairs(tItemsOut) do
local class = type(v)
if class == "number" then
elseif class == "string" then
else --class == "table"
end
end
for k,v in pairs(tItemsIn) do
if class == "number" then
elseif class == "string" then
else --class == "table"
end
end
end
end
end
function enderRestock(enderSlot,tTurtleSlots,tEnderSlots)
--puts down an ender chest from the slot (enderSlot), then attempts to restock the
--turtle slots in the table tTurtleSlots with the chest slots in tEnderSlots
turtle.select(enderSlot)
local _dir,chestDir = findSpace(true)
sleep(0.05)
--locate open space for ender chest
if chestDir then
local pushDir = tNumHeading[dirStandardize("-"..chestDir)]
local tTurtleSlotsCopy
if type(tTurtleSlots) == "table" then
--create local copy of turtle slots table
tTurtleSlotsCopy = {}
for i=1,#tTurtleSlots do
tTurtleSlotsCopy[i] = tTurtleSlots[i]
end
else
tTurtleSlotsCopy = {enderSlot}
end
local tEnderSlotsCopy = {}
--create local copy of ender chest slots table
for i=1,#tEnderSlots do
tEnderSlotsCopy[i] = tEnderSlots[i]
end
local selected
local amount = 0 -- amout of items pushed from chest
if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
local chest = peripheral.wrap(chestDir)
local chestFunc = chest.pushItemIntoSlot or chest.pushIntoSlot
for iTurtle=1,#tTurtleSlotsCopy do
local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
if turtleCount < 64 then -- only restocks the slot if there is space
local iEnder = 1
while iEnder <= #tEnderSlotsCopy do --dynamic for loop
local stack = peripheral.call(chestDir,"getStackInSlot",tEnderSlotsCopy[iEnder]) or {["qty"] = 0}
local removed = false
if stack.qty > 0 then -- items in ender chest slot
local pushed = chestFunc(pushDir,tEnderSlotsCopy[iEnder],tTurtleSlots == true and stack.qty-1 or stack.qty,tTurtleSlotsCopy[iTurtle])
turtleCount = turtleCount+pushed
amount = amount+pushed
if pushed == (tTurtleSlots == true and stack.qty-1 or stack.qty) then
remove = true
end
if turtleCount >= 64 then --ends loop if turtle slot is full
break
end
else -- no items in ender chest slot, slot is removed
remove = true
end
if remove then
table.remove(tEnderSlotsCopy,iEnder)
else
iEnder = iEnder+1
end
end
end
end
end
if tTurtleSlots == true then
turtle.select(enderSlot)
turtle.refuel(64)
end
dig(chestDir,enderSlot,true) -- retrieves the ender chest
if amount > 0 then
return amount
else
return false --no items pushed
end
end
return false --chest placement failed
end
function enderDropoff(enderSlot,tTurtleSlots)
--puts down an ender chest from the slot (enderSlot), then empties the specified
--turtle slots in the table tTurtleSlots into the chest slots in tEnderSlots
turtle.select(enderSlot)
local _dir,chestDir = findSpace(true)
sleep(0.1)
--locate open space for ender chest
if chestDir then
local pullDir = tNumHeading[dirStandardize("-"..chestDir)]
local tTurtleSlotsCopy = {}
--create local copy of turtle slots table
for i=1,#tTurtleSlots do
tTurtleSlotsCopy[i] = tTurtleSlots[i]
end
local selected
local amount = 0 -- amout of items stored in chest
if peripheral.getType(chestDir) == "ender_chest" then -- confirms ender chest peripheral
local chest = peripheral.wrap(chestDir)
local chestFunc = chest.pullItemIntoSlot or chest.pullIntoSlot
for iTurtle=1,#tTurtleSlotsCopy do
local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
if turtleCount > 0 then -- only stores occupied slots
for iChest=17,chest.getInventorySize() do
local stack = chest.getStackInSlot(iChest)
stack = stack and stack.qty and stack.maxSize or {["qty"] = 0,["maxSize"] = 64}
if stack.qty < stack.maxSize then --space in enderSlot
local pulled = chestFunc(pullDir,tTurtleSlotsCopy[iTurtle],64,iChest)
turtleCount = turtleCount-pulled
amount = amount+pulled
if turtleCount < 1 then -- Ends loop if turtle slot is empty
break
end
end
end
end
end
else
for iTurtle=1,#tTurtleSlotsCopy do
local turtleCount = turtle.getItemCount(tTurtleSlotsCopy[iTurtle])
if turtleCount > 0 then -- only stores occupied slots
drop(chestDir, tTurtleSlotsCopy[iTurtle], turtleCount)
end
end
end
dig(chestDir,enderSlot,true) -- retrieves the ender chest
if amount > 0 then
return amount
else
return false --no items pulled
end
end
return false --chest placement failed
end
function move(DIRECTION,AMOUNT,DIG)
--moves turtle in DIRECTION, AMOUNT times, digs if DIG optional slot
if AMOUNT == 0 then
return
end
DIRECTION = dirStandardize(DIRECTION)
AMOUNT = AMOUNT or 1
if AMOUNT < 0 then
DIRECTION = dirStandardize("-"..DIRECTION)
AMOUNT = -AMOUNT
end
--function definitions
local moveFunc
if DIRECTION == tTextDir.backward and not DIG then
moveFunc = turtle.back
else
moveFunc = getTurtleFunc("move",DIRECTION,true)
end
local detectFunc = getTurtleFunc("detect",DIRECTION)
local internalDigFunc = getTurtleFunc("dig",DIRECTION)
local digFunc = type(DIG) == "number" and function()
turtle.select(DIG)
internalDigFunc()
end or internalDigFunc
local attackFunc = getTurtleFunc("attack",DIRECTION)
saveData()
local symDir = tNumDir[DIRECTION]
local posEntry = string.lower(symDir:sub(1,1))
local mathOp = symDir:sub(2,2)
--movement calc function
local calcFunc = function()
tPos[posEntry] = tPos[posEntry] + tonumber(mathOp.."1")
tPos.fuel = not unlimitedFuel and tPos.fuel-1 or 9999
end
tDest[posEntry] = tPos[posEntry] + tonumber(mathOp..AMOUNT)
for i=1,AMOUNT do
while DIG and detectFunc() do
while tSettings.swarm and peripheral.getType(tNumSide[DIRECTION]) == "turtle" do -- swarm turtles check for other turtles
sleep(0.5)
end
digFunc()
if DIRECTION == 5 then
sleep(tTimer.dig)
end
end
local res = moveFunc()
while not res or res == "cancel" do
if res == "cancel" then
return
elseif not unlimitedFuel and tPos.fuel <= 1 then
if tSettings.enderFuel then
enderRestock(tSettings.enderFuel,true,tSettings.tEnderFuel)
tPos.fuel = turtle.getFuelLevel()
os.startTimer(tTimer.enderFuel)
end
if tSettings.autoRefuel and tPos.fuel <= 1 then
for i=1,16 do
turtle.refuel(64)
end
tPos.fuel = turtle.getFuelLevel()
end
if tPos.fuel <= 0 then
if eventHandler.refuel then
eventHandler.refuel()
tPos.fuel = turtle.getFuelLevel()
else
renderBars("Fuel required! Press any key.")
while true do
local e = os.pullEvent()
if e == "key" then
turtle.refuel(64)
tPos.fuel = turtle.getFuelLevel()
break
elseif e == "timer" then
break
end
end
end
end
else
turtle.attack()
if DIG then
digFunc()
attackFunc()
end
if eventHandler.moveFail then
eventHandler.moveFail(tNumDir[DIRECTION])
elseif tSettings.renderMove then
renderBars("Move "..tNumDir[DIRECTION].." failed. Retry in "..tTimer.moveFail)
end
sleep(tTimer.moveFail)
end
res = moveFunc()
end
calcFunc()
tPos.lastMove = DIRECTION
saveData()
if tSettings.renderMove then
refreshLines()
renderBars("Moving "..symDir)
end
end
if renderMove then
renderBars("Moved "..symDir)
end
end
function moveTo(COORDS,SYM,DIG)
SYM = SYM:lower()
tDest[SYM] = COORDS
local mathOp
local amount = tPos[SYM]-COORDS
if amount > 0 then
mathOp = "-"
elseif amount == 0 then
return
else
mathOp = "+"
amount = -amount
end
move(SYM..mathOp,amount,DIG)
end
function moveToXYZ(x,y,z,faceDir,DIG)
tDest.x = x
tDest.y = y
tDest.z = z
if faceDir == true then
DIG = faceDir
faceDir = false
end
moveTo(x,"x",DIG)
moveTo(z,"z",DIG)
moveTo(y,"y",DIG)
if faceDir then
turn(faceDir)
end
end
function deployGPS(tSlots,x,y,z)
--builds a GPS tower at the given coordinates or the current coordinates
--[[tSlots = {
diskDrive = 1,
disk = 2,
computer = 3,
modem = 4
}]]
assert(tSlots,"Slots table missing!")
assert(tSlots.diskDrive,"diskDrive slot missing!")
assert(turtle.getItemCount(tSlots.diskDrive) > 0,"No disk drive in slot "..tSlots.diskDrive.."!")
assert(tSlots.disk,"disk slot missing!")
assert(turtle.getItemCount(tSlots.disk) > 0,"No disk in slot "..tSlots.disk.."!")
assert(tSlots.computer,"computer slot missing!")
assert(turtle.getItemCount(tSlots.computer) > 3,"Missing "..(4-turtle.getItemCount(tSlots.computer).." computers in slot "..tSlots.computer.."!"))
assert(tSlots.modem,"modem slot missing!")
assert(turtle.getItemCount(tSlots.modem) > 3,"Missing "..(4-turtle.getItemCount(tSlots.modem).." modems in slot "..tSlots.modem.."!"))
x = x or tPos.x
y = math.min((y or tPos.y),252)
z = z or tPos.z
local function setupHost(hostX,hostZ)
place("forward",tSlots.computer)
move"up"
place("forward",tSlots.modem)
place("down",tSlots.diskDrive)
assert(peripheral.getType"bottom" == "drive","The block on slot "..tSlots.diskDrive.." was not a disk drive!")
drop("down",tSlots.disk)
assert(fs.exists"/disk","Missing disk drive in slot "..tSlots.disk)
if not fs.exists"/disk/startup" then
local diskFile = fs.open("/disk/startup", "w")
diskFile.writeLine[[
if not os.getComputerLabel() then
os.setComputerLabel"GPS Host"
end
fs.copy("/disk/hostFile", "/startup")
shell.run"startup"]]
diskFile.close()
end
local hostFile = fs.open("/disk/hostFile", "w")
hostFile.writeLine('shell.run"gps host '..hostX..' '..(tPos.y-1)..' '..hostZ..'"')
hostFile.close()
move"left"
move"right"
move"down"
turn"right"
assert(peripheral.getType"front" == "computer","The block in slot "..tSlots.computer.." was not a computer!")
peripheral.call("front", "turnOn")
move"right"
suck("left",tSlots.disk)
turtle.select(tSlots.diskDrive)
dig("forward")
end
moveTo(y,"y",true)
moveToXYZ(x+4,y,z,"x+",true)
setupHost(tPos.x+1,tPos.z)
moveToXYZ(x-4,y,z,"x-",true)
setupHost(tPos.x-1,tPos.z)
moveToXYZ(x,y,z+4,"z+",true)
setupHost(tPos.x,tPos.z+1)
moveToXYZ(x,y+1,z-4,"z-",true)
setupHost(tPos.x,tPos.z-1)
move("down",2)
end
function deployTurtle(tSlots,tData)
--[[tSlots = {
diskDrive = 1,
disk = 2,
turtle = 3,
enderChest = 4 -- optional, enables enderRefuel slot 16
}
tData = {
label = "the turtle label derpface",
type = "remote" --enables remote modem control on startup
type = "swarm" --assigns swarm ID and enables remote control, enderRefuel slot 16 and disables renderMove.
swarm = 1 --swarm ID number
formation = "wall" or "floor" --swarm formation
]]--
tData = tData or {}
assert(tSlots,"Slots table missing!")
assert(tSlots.diskDrive,"diskDrive slot missing!")
assert(tSlots.disk,"disk slot missing!")
assert(tSlots.turtle,"turtle slot missing!")
place("down",tSlots.diskDrive,true)
assert(peripheral.getType"bottom" == "drive", "The block in slot "..tSlots.diskDrive.." was not a diskDrive!")
drop("down",tSlots.disk)
assert(fs.exists"/disk", "The block in slot "..tSlots.disk.." was not a disk!")
--setup cTurtle
fs.delete("/disk/"..tFile.cTurtle)
fs.copy(tFile.cTurtle,"disk/cTurtle")
fs.delete("/disk"..tFile.directory)
fs.makeDir("/disk"..tFile.directory)
--setup disk bootup file
local file = fs.open("/disk/startup","w")
file.writeLine([[
os.setComputerLabel("]]..(tData.label or tData.type or "cTurtle")..[[")
fs.copy("disk/]]..tFile.cTurtle..[[", "/]]..tFile.cTurtle..[[")
fs.copy("disk/startupFile","/startup")
fs.copy("disk]]..tFile.directory..[[","]]..tFile.directory..[[")]]
)
file.close()
if tSlots.enderChest then
local file = fs.open("/disk/startup","a")
file.writeLine([[
turtle.select(1)
turtle.transferTo(16)]]
)
file.close()
end
local file = fs.open("/disk/startup","a") --one time actions goes here
file.writeLine([[
sleep(5)
_G.restarted = false
shell.run"/startup"
cTurtle.move"forward"
cTurtle.move"back"]]
)
file.close()
--setup settings
local tNewSettings = {}
for k,v in pairs(tSettings) do
tNewSettings[k] = v
end
if tSlots.enderChest then
tNewSettings.enderFuel = 16
end
if tData.type == "swarm" then
tNewSettings.renderMove = false
end
for k,v in pairs(tData) do
if tNewSettings[k] ~= nil then
tNewSettings[k] = v
end
end
saveSettings("/disk"..tFile.settings,tNewSettings)
--setup startup file
local file = fs.open("/disk/startupFile","w")
file.writeLine([[
local file = fs.open("]]..tFile.restarted..[[", "w")
file.writeLine"true"
file.close()
os.loadAPI"]]..tFile.cTurtle..[["]]
)
file.close()
--setup remote turtles
if tData.type == "remote" then
local file = fs.open("/disk/startupFile","a")
file.writeLine([[
cTurtle.modemControl(true)]]
)
file.close()
--setup swarm turtles
elseif tData.type == "swarm" then
local file = fs.open("/disk/startupFile","a")
file.writeLine([[
cTurtle.modemControl(true)]]
)
file.close()
end
--setup location info
--locaiton offset correction
local tOffs = {
x = 0,
y = -1,
z = 0
}
local dir = string.lower(tNumDir[tPos.dir])
tOffs[dir:sub(1,1)] = tOffs[dir:sub(1,1)] + tonumber(dir:sub(2,2)..1)
--save new location
saveData(tPos.x+tOffs.x,tPos.y+tOffs.y,tPos.z+tOffs.z,tPos.dir,0,"/disk"..tFile.data)
--place turtle
move"forward"
place("down",tSlots.turtle,true)
assert(peripheral.getType"bottom" == "turtle","The block in slot "..tSlots.turtle.." was not a turtle!")
if tSlots.enderChest then
drop("down",tSlots.enderChest,1)
end
peripheral.call("bottom","turnOn")
--retrieve disk and drive
move"back"
suck("down",tSlots.disk)
turtle.select(tSlots.diskDrive)
dig"down"
end
function deploySwarm (tSlots,height,width,formation)
--[[tSlots = {
diskDrive = 1,
disk = 2,
turtle = 3,
enderChest = 4
}]]--
formation = formation or "wall"
local frontDir = dirStandardize"forward"
local sideDir = dirStandardize"right"
local id = 1
for h=1,height do
for w=1,width do
deployTurtle(tSlots,{
label = "swarm cTurtle "..id,
type = "swarm", --assigns swarm ID and enables remote control, enderRefuel slot 16 and disables renderMove.
swarm = id, --swarm ID number
formation = formation --swarm formation
})
id = id+1
if w < width then
if formation == "wall" then
move(sideDir)
turn(frontDir)
elseif formation == "floor" then
move(sideDir)
turn(frontDir)
end
end
end
sideDir = cTurtle.dirStandardize("-"..sideDir)
if formation == "wall" then
move"up"
elseif formation == "floor" then
move"back"
end
end
end
tCommandQueue = {} -- table of queued commands
function modemControl(enable)
--enables a very advanced remote control of the turtle
if enable then
if modem then
if cTurtle.tSettings.swarm then
_G.modem.open(tSettings.swarmChannel)
else
_G.modem.open(tSettings.controlChannel)
end
else
return
end
oldPull = _G.os.pullEvent
_G.os.pullEvent = function(filter) -- override original pullEvent
while true do
local tEvent = {oldPull()}
if tEvent[1] == "modem_message"
and tEvent[3] == tSettings.controlChannel
or tEvent[3] == tSettings.swarmChannel
and type(tEvent[5]) == "table"
and (tEvent[5].id == "all" or tEvent[5].id == tSettings.swarm) then
tCommandQueue[#tCommandQueue+1] = tEvent
if not tCommandQueue[2]
or tEvent[5] == "cancel" then
while tCommandQueue[1] do
local oldRenderMove = tSettings.renderMove
tSettings.renderMove = false
local tArg = {}
if tEvent[5] == "cancel" then --remote cancel
os.queueEvent("cTurtle","cancel")
elseif type(tEvent[5]) == "table" then --table commands
if tEvent[5][1] == "cTurtle update" then --update cTurtle through modem message
table.remove(tEvent[5],1)
fs.open(tFile.cTurtle,"w").writeLine(table.concat(tEvent[5],"\n"))
os.reboot()
else -- function command
tArg[1] = tEvent[5][1]
for i=2,#tEvent[5] do
if type(tEvent[5][i]) == "table" then
tArg[i] = "{"
for k,v in pairs(tEvent[5][i]) do
tArg[i] = tArg[i].." "..k.." = "..v..","
end
tArg[i] = tArg[i].."}"
elseif type(tEvent[5][i]) == "string" then
tArg[i] = '"'..tEvent[5][i]..'"' --add quotation marks to strings
elseif tEvent[5][i] == true then
tArg[i] = "true" --change boolean to string
elseif tEvent[5][i] == false then
tArg[i] = "false" --change boolean to string
else
tArg[i] = tEvent[5][i] --numbers
end
end
end
else --string function command
tArg[1] = tEvent[5]:match"^%S+"
tEvent[5] = tEvent[5]:sub(#tArg[1]+2,#tEvent[5])
for word in tEvent[5]:gmatch"%S+" do --split message into words
if word:match"^%d+$" then --convert numbers to number format
word = tonumber(word)
elseif word:match"true" then --convert booleans to string
word = "true"
elseif word:match"false" then
word = "false"
else
word = '"'..word..'"' --add quotation marks to strings
end
table.insert(tArg,word)
end
end
if tArg[1] then --command
local funcString = ""
funcString = "cTurtle."..table.remove(tArg,1).."(" --write function
for i=1,#tArg do
local arg = tArg[i]
if i > 1 then
funcString = funcString.."," --commas if multipe args
end
funcString = funcString..arg
end
funcString = funcString..")" --end function
local func = loadstring("setfenv(1,_G) "..funcString) -- create function
local tRes = {pcall(func)}
local str = funcString
if tRes[1] then -- no errors
table.remove(tRes,1) --remove error index
for i=1,#tRes do --convert return values to string
if tRes[i] == true then
str = str.."|True "
elseif tRes[i] == false then
str = str.."|False "
elseif type(tRes[i]) == "table" then
for k,v in pairs(tRes[i]) do
str = str.."|"..k.."="..v.." "
end
elseif tRes[i] then
str = str.."|"..tRes[i].." "
end
end
else --function failed
str = str.."|"..tRes[2]
end
if tEvent[3] == tSettings.controlChannel then
modem.transmit(tSettings.controlResponseChannel,tSettings.controlChannel,{
response = str,
tPos = tPos,
turtleId = os.getComputerID()
}) --send return values
end
renderMove = oldRenderMove
end
table.remove(tCommandQueue,1)
if tCommandQueue[1] then -- command in queue
tEvent = tCommandQueue[1]
end
end
end
elseif not filter or filter == tEvent[1] then
return unpack(tEvent) -- return event params
end
end
end
if tSettings.swarm then
_G.modem.transmit(tSettings.controlResponseChannel,tSettings.swarmChannel,"Swarm turtle #"..tSettings.swarm.." online.")
else
_G.modem.transmit(tSettings.controlResponseChannel,tSettings.controlChannel,"Turtle #"..os.getComputerID().." online.")
end
else
_G.modem.close(tSettings.swarmChannel)
_G.modem.close(tSettings.controlChannel)
_G.os.pullEvent = oldPullEvent --restore original pullEvent
end
end
--API init
if not fs.exists(tFile.directory) then
fs.makeDir(tFile.directory)
end
if not fs.exists(tFile.settings) then
saveSettings()
end
if not fs.exists"/startup" then
local file = fs.open("/startup","w")
file.writeLine([[
local file = fs.open("]]..tFile.restarted..[[", "w")
file.writeLine"true"
file.close()
_G.restarted = true
]])
file.close()
_G.restarted = true
else
local file = fs.open("/startup","r")
local sFile = file.readAll()
file.close()
if not sFile:match([[fs%.open%("]]..tFile.restarted..[[", "w"%)]]) then
local file = fs.open("/startup","a")
file.writeLine([[
local file = fs.open("]]..tFile.restarted..[[", "w")
file.writeLine"true"
file.close()
_G.restarted = true
]])
file.close()
_G.restarted = true
end
local file = fs.open(tFile.restarted, "w")
file.writeLine"true"
file.close()
end
if not _G.restarted then
local file = fs.open(tFile.restarted,"r")
_G.restarted = file.readLine() == "true"
file.close()
end
if not _G.modem then --auto wrap turtle modem, for ease of use
for k,side in pairs(peripheral.getNames()) do
if peripheral.getType(side) == "modem" then
_G.modem = peripheral.wrap(side)
_G.modem.open(gps.CHANNEL_GPS) --open GPS channel
break
end
end
end
locate()
local tArg = {...}
if tArg[1] then
--shell usage
for i=2,#tArg do
if tArg[i]:match"^%d+$" then
tArg[i] = tonumber(tArg[i])
else
tArg[i] = '"'..tArg[i]..'"'
end
end
local func = loadstring(table.remove(tArg,1).."("..table.concat(tArg,",")..")")
setfenv(func,getfenv())
term.setBackgroundColor(colors.black)
term.clear()
func()
term.clear()
term.setCursorPos(1,1)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment