CCCPaste

ArcChunkLoading PROGRAM

-- ArcChunkLoading PROGRAM

-- This program is designed to load chunks in a defined area
-- The original goal was to load the area chere turtles are mining (vertical mining program)
-- It calculates checkpoints location to go through to load the all area
-- It has regular runs depending on the time chunks stay loaded (see Advanced Preripherals settings)
-- Length corresponds to the forward dimension and width to the right(relatively to the turtle's starting orientation)
-- The first checkpoint will be at position 1,1
-- if the turtle encounters a block on its path, it will sop
-- The turtle can save his position and status at each move or status update to automatically resume his loading operation at game/server restart
-- It automatically saves it in case of unexpected interruption to make you able to resume the loading operation in the main menu telling you the interruption reason
-- The turtle can report his position and status to a distant computer to write it on a screen or send chat message (turtle watching program)
-- The report is sent when status is saved
-- If a GPS is setup and in range, it is used to send minecraft coordinates location in the report instead of turtle's own referential coordinates
-- The turtle can refuel from start position (down) storage, shulker (in slot 1) or inventory (with emergency refuel only)
-- It will try to refuel before moving to the next mine shaft, when running out of fuel and at the mining operation end
-- It will refuel in storage or shulker only if his fuel level is under the low fuel threshold
-- It will refuel from inventory only if it runs out of fuel and no other refuel type works
-- When using a shulker, the turtle will try to place it on all directions in the following order : up>down>forward>right>back>left
-- Shulkers can be replaced by modded ender chests

local ArcConst		=	require("ArcConstants")
local ArcBlocks		=	require("ArcBlocks")
local ArcTerm		=	require("ArcTerm")
local ArcMoving		=	require("ArcMoving")
local ArcUtilities	=	require("ArcUtilities")


-- GLOBAL VARIABLES

-- Operation Settings

local gnMaxLength			=	0						-- Length of the loading area (range from 1 up to 999)
local gnMaxWidth			=	0						-- Width of the loading area (range from 1 up to 999)

-- Global Settings

local gnRefuelType			=	3						-- Refueling method type (range from 1 up to 3)
local gbEmergencyRefuel		=	true					-- Allow refueling directly from inventory
local gnLowFuelThreshold	=	1000					-- Fuel limit below which refueling is done (range from 0 up to turtle's fuel limit)
local gnHighFuelThreshold	=	5000					-- Maximum fuel level allowed when refueling (range from low fuel threashold up to turtle's fuel limit)
local gnChunkLoadTime		=	600						-- Time a chunk stay loaded (check Advanced Peripherals settings)
local gbSavePosAndStatus	=	false					-- Save position and status at each move to resume loading operation on map reload
local gbReportPosAndStatus	=	false					-- Report position and status at each status modification
local gsReportProtocol		=	"ATW"					-- Report protocol code to send reports

-- Miscellaneous

local gnCPCount				=	0						-- Actual checkpoint index
local gtabCPLocations		=	{}						-- Locations of all checkpoints
local gsShulkerDirection	=	""						-- Direction of placed shulker to take if back after
local gnStatusType			=	0						-- Current turtle status type
local gsStatus				=	""						-- Current turtle status
local gbRepeatReport		=	false					-- Repeat report every few seconds
local gsSettingsFileName	=	"Settings.acl"			-- Global Settings file name
local gsStatusFileName		=	"Status.acl"			-- Saved status file name to resume interrupted mining operation
local gsPositionFileName	=	"Position.acl"			-- Saved position file name to resume interrupted mining operation
local gnRefreshTime			=	5						-- Status refresh countdown


-- FUNCTIONS

local function CalculateCheckpointsLocation()
	local tabPosCP		=	{}
	local nCPL			=	1
	local nCPW			=	1
	gtabCPLocations		=	{}
	if gnMaxLength > 1 then nCPL = nCPL+math.ceil((gnMaxLength-1)/16) end
	if gnMaxWidth > 1 then nCPW = nCPW+math.ceil((gnMaxWidth-1)/16) end
	for k=1,nCPW,1 do
		if k%2 == 1 then
			for j=1,nCPL,1 do
				tabPosCP = {16*(j-1)+1,16*(k-1)+1}
				if tabPosCP[1] > gnMaxLength then tabPosCP[1] = gnMaxLength end
				if tabPosCP[2] > gnMaxWidth then tabPosCP[2] = gnMaxWidth end
				table.insert(gtabCPLocations,tabPosCP)
			end
		else
			for j=nCPL,1,-1 do
				tabPosCP = {16*(j-1)+1,16*(k-1)+1}
				if tabPosCP[1] > gnMaxLength then tabPosCP[1] = gnMaxLength end
				if tabPosCP[2] > gnMaxWidth then tabPosCP[2] = gnMaxWidth end
				table.insert(gtabCPLocations,tabPosCP)
			end
		end
	end
end

local function ReportPositionAndStatus()
	local nX			=	nil
	local nY			=	nil
	local nZ			=	nil
	local nO			=	nil
	local TurtleData	=	nil
	local sTurtleData	=	nil
	local nLocationType	=	nil
	local fDelay		=	nil
	while true do
		TurtleData		=	{}
		nLocationType	=	ArcConst.TLT_MINECRAFT
		nX,nY,nZ = gps.locate()
		nO = ArcMoving.GetRealOrientation()
		if nX == nil then
			nLocationType	=	ArcConst.TLT_OWNREF
			nX				=	ArcMoving.GetPosL()
			nY				=	ArcMoving.GetPosW()
			nZ				=	ArcMoving.GetPosD()
			nO				=	ArcMoving.GetPosO()
		end
		table.insert(TurtleData,os.getComputerID())
		table.insert(TurtleData,ArcConst.TT_CHUNKLOADING)
		table.insert(TurtleData,nLocationType)
		table.insert(TurtleData,nX)
		table.insert(TurtleData,nY)
		table.insert(TurtleData,nZ)
		table.insert(TurtleData,nO)
		table.insert(TurtleData,gnCPCount)
		table.insert(TurtleData,#gtabCPLocations)
		table.insert(TurtleData,turtle.getFuelLevel())
		table.insert(TurtleData,turtle.getFuelLimit())
		table.insert(TurtleData,gnStatusType)
		table.insert(TurtleData,gsStatus)
		sTurtleData = textutils.serialise(TurtleData)
		rednet.broadcast({sTurtleData = sTurtleData},gsReportProtocol)
		if not gbRepeatReport then break end
		fDelay = math.random(5000,6000)
		fDelay = fDelay/100
		os.sleep(fDelay)
	end
end

local function TotalShulkerCount()
	local nShulkerCount		=	0
	if gnRefuelType == ArcConst.TRT_SHULKER then nShulkerCount = nShulkerCount+1 end
	return nShulkerCount
end

local function SaveAndReportStatus(pnStatusType,psStatus)
	local File		=	nil
	gnStatusType	=	pnStatusType
	if psStatus ~= "" then gsStatus = psStatus end
	if pnStatusType == ArcConst.TST_NEEDPLAYER or pnStatusType == ArcConst.TST_SHUTDOWN or gbSavePosAndStatus then
		File = fs.open(gsStatusFileName,"w+")
		File.writeLine(("%d;%s"):format(pnStatusType,gsStatus))
		File.writeLine(("%d;%d"):format(gnMaxLength,gnMaxWidth))
		File.writeLine(("%d;%s"):format(gnCPCount,gsShulkerDirection))
		File.close()
	end
	if gbReportPosAndStatus then ReportPositionAndStatus() end
end

local function Terminate(psErrorMessage)
	gbError = true
	gbRepeatReport = true
	ArcMoving.SavePosition(true)
	SaveAndReportStatus(ArcConst.TST_NEEDPLAYER,psErrorMessage)
end

local function SelectEmptySlot()
	for i=1,ArcConst.TM_MAXSLOTS,1 do
		if turtle.getItemCount(i) == 0 then
			turtle.select(i)
			return i
		end
	end
	turtle.select(1)
	return nil
end

local function PlaceShulker(pnSlot)
	local bReturn		=	nil
	local sMessage		=	nil
	gsShulkerDirection	=	""
	turtle.select(pnSlot)
	bReturn,sMessage = turtle.placeUp()
	if bReturn then
		gsShulkerDirection = ArcConst.TD_UP
		SaveAndReportStatus(ArcConst.TST_RUNNING,"")
		return true
	end
	bReturn,sMessage = turtle.placeDown()
	if bReturn then
		gsShulkerDirection = ArcConst.TD_DOWN
		SaveAndReportStatus(ArcConst.TST_RUNNING,"")
		return true
	end
	bReturn,sMessage = turtle.place()
	if bReturn then
		gsShulkerDirection = ArcConst.TD_FORWARD
		SaveAndReportStatus(ArcConst.TST_RUNNING,"")
		return true
	end
	return false
end

local function TakeShulker(pnSlot)
	local bReturn		=	nil
	local sMessage		=	nil
	turtle.select(pnSlot)
	if gsShulkerDirection == ArcConst.TD_FORWARD then
		bReturn,sMessage = turtle.dig()
		if not bReturn then Terminate("Unable to take forward shulker") end
	elseif gsShulkerDirection == ArcConst.TD_UP then
		bReturn,sMessage = turtle.digUp()
		if not bReturn then Terminate("Unable to take up shulker") end
	elseif gsShulkerDirection == ArcConst.TD_DOWN then
		bReturn,sMessage = turtle.digDown()
		if not bReturn then Terminate("Unable to take down shulker") end
	else
		Terminate("Unexpedted shulker direction")
	end
	gsShulkerDirection = ""
	SaveAndReportStatus(ArcConst.TST_RUNNING,"")
end

local function RefuelFromItem()
	local nFuelLevel	=	turtle.getFuelLevel()
	local nItemFuel		=	nil
	local bRefuel		=	nil
	local sMessage		=	nil
	local nItemCount	=	nil
	if nFuelLevel >= gnHighFuelThreshold then return false end
	bRefuel,sMessage = turtle.refuel(1)
	if not bRefuel then return false end
	nItemRefuel = turtle.getFuelLevel()-nFuelLevel
	nItemCount = math.ceil((gnHighFuelThreshold-turtle.getFuelLevel())/nItemRefuel)
	if nItemCount <= 0 then return false end
	if nItemCount >= turtle.getItemCount(turtle.getSelectedSlot()) then turtle.refuel()
	else turtle.refuel(nItemCount) end
	return (turtle.getFuelLevel() < gnHighFuelThreshold)
end

local function RefuelFromStorage(psDirection)
	local nSlot		=	nil
	local bRefuel	=	false
	nSlot = SelectEmptySlot()
	if nSlot == nil then Terminate("No slot for fuel") end
	repeat
		if psDirection == ArcConst.TD_FORWARD then turtle.suck()
		elseif psDirection == ArcConst.TD_UP then turtle.suckUp()
		elseif psDirection == ArcConst.TD_DOWN then turtle.suckDown()
		else Terminate("Unexpected storage direction")
		end
		bRefuel = RefuelFromItem()
	until not bRefuel
	if turtle.getItemCount() > 0 then
		if psDirection == ArcConst.TD_FORWARD then turtle.drop()
		elseif psDirection == ArcConst.TD_UP then turtle.dropUp()
		elseif psDirection == ArcConst.TD_DOWN then turtle.dropDown()
		end
	end
	turtle.select(1)
end

local function RefuelProtocol()	
	local nFuelLevel		=	turtle.getFuelLevel()
	local bMoved			=	nil
	local sDirection		=	nil
	local bFirstResolution	=	nil
	if nFuelLevel > 0 and nFuelLevel > gnLowFuelThreshold then return false end
	SaveAndReportStatus(ArcConst.TST_RUNNING,"Refueling...")
	if gnRefuelType == ArcConst.TRT_STARTING then
		if nFuelLevel > 0 then
			bFirstResolution = true
			repeat
				bMoved,sDirection = ArcMoving.MoveToStart(bFirstResolution)
				if not bMoved then
					ResolveMovingError(sDirection)
					bFirstResolution = false
				end
			until bMoved
			RefuelFromStorage(ArcConst.TD_DOWN)
			repeat
				bMoved,sDirection = ArcMoving.MoveToBackup()
				if not bMoved then ResolveMovingError(sDirection) end
			until bMoved
		end
	elseif gnRefuelType == ArcConst.TRT_SHULKER then
		if PlaceShulker(1) then
			RefuelFromStorage(gsShulkerDirection)
			TakeShulker(1)
		end
	else -- No Refuel (=1) / Unexpected value
		
	end
	-- No emergency refuel if standard protocol worked no matter fuel level
	if turtle.getFuelLevel() > nFuelLevel then return true end
	-- No emergency refuel if not out of fuel
	if turtle.getFuelLevel() > 0 then return false end
	if not gbEmergencyRefuel then return false end
	-- If no fuel items, wait until help
	repeat
		for i=TotalShulkerCount()+1,ArcConst.TM_MAXSLOTS,1 do
			turtle.select(i)
			RefuelFromItem()
		end
		Terminate("Out of fuel")
	until turtle.getFuelLevel() > 0
	turtle.select(1)
	return true
end

local function ResolveMovingError(psDirection)
	local bReturn	=	nil
	local sMessage	=	nil
	local sStatus	=	gsStatus
	-- Out of fuel
	if turtle.getFuelLevel() == 0 then
		if not RefuelProtocol() then Terminate("Out of fuel")
		else
			SaveAndReportStatus(ArcConst.TST_RUNNING,sStatus)
			return true
		end
	end
	-- Block on the way
	if psDirection == ArcConst.TD_FORWARD then Terminate("Block forward") end
	-- Unexpected error
	Terminate("Unexpected moving error")
end

local function RefreshDelay()
	os.sleep(gnRefreshTime)
end

local function ManualInterruption()
	if ArcTerm.ReadKey() ~= nil then
		while true do
			ArcTerm.Write(1,13,"1 | Confirm interruption - 2 | Cancel")
			nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
			if nKey == 1 then
				ArcMoving.SavePosition(true)
				SaveAndReportStatus(2,"Manual interruption")
				os.shutdown()
			elseif nKey == 2 then break end
		end
	end
end

local function Interruption()
	while true do
		if not gbError then
			ArcTerm.Write(1,13,ArcUtilities.Complete("Press any key to interrupt"," ",39,"R",false))
			parallel.waitForAny(ManualInterruption,RefreshDelay)
		else
			ArcTerm.Write(1,13,ArcUtilities.Complete("Press any key to exit"," ",39,"R",false))
			ArcTerm.ReadKey()
			gbRepeatReport = false
			SaveAndReportStatus(ArcConst.TST_SHUTDOWN,"")
			if not gbError then
				fs.delete(gsPositionFileName)
				fs.delete(gsStatusFileName)
			end
			os.shutdown()
		end
	end
end

local function ShowStatus()
	while true do
		ArcTerm.ResetLines({1,2,3,4,5,6,7,8,9,10,11,12})
		ArcTerm.Write(1,1,"<========= LOADING OPERATION =========>")
		ArcTerm.Write(1,3,("Position length : %d"):format(ArcMoving.GetPosL()))
		ArcTerm.Write(1,4,("Position width  : %d"):format(ArcMoving.GetPosW()))
		ArcTerm.Write(1,5,("Position depth  : %d"):format(ArcMoving.GetPosD()))
		ArcTerm.Write(1,6,("Orientation     : %d"):format(ArcMoving.GetPosO()))
		ArcTerm.Write(1,7,("Checkpoint      : %d/%d"):format(gnCPCount,#gtabCPLocations))
		ArcTerm.Write(1,8,("Fuel level      : %d/%d"):format(turtle.getFuelLevel(),turtle.getFuelLimit()))
		ArcTerm.Write(1,10,"Current status  :")
		ArcTerm.Write(1,11,gsStatus)
		RefreshDelay()
	end 
end

local function Timer()
	local nTimerId	=	nil
	local sEvent	=	nil
	local nId		=	nil
	nTimerId = os.startTimer(math.ceil(gnChunkLoadTime*0.98))
	repeat
		sEvent,nId = os.pullEvent("timer")
	until nId == nTimerId
end

local function Run()
	local bMoved		=	nil
	local sDirection	=	nil
	repeat
		gnCPCount = gnCPCount+1
		SaveAndReportStatus(ArcConst.TST_RUNNING,"Running...")
		repeat
			bMoved,sDirection = ArcMoving.MoveTo(gtabCPLocations[gnCPCount][1],gtabCPLocations[gnCPCount][2])
			if not bMoved then ResolveMovingError(sDirection) end
		until bMoved
	until gnCPCount == #gtabCPLocations
	gnCPCount=1
	SaveAndReportStatus(ArcConst.TST_RUNNING,"Running...")
	repeat
		bMoved,sDirection = ArcMoving.MoveTo(gtabCPLocations[gnCPCount][1],gtabCPLocations[gnCPCount][2])
		if not bMoved then ResolveMovingError(sDirection) end
	until bMoved
	ArcMoving.ChangeOrientation(1)
	SaveAndReportStatus(ArcConst.TST_RUNNING,"Waiting until next run...")
end

local function StartLoadingOperation()
	local bMoved		=	nil
	local sDirection	=	nil
	while true do
		parallel.waitForAll(Timer,Run)
	end
end

local function LoadStatus()
	local File			=	fs.open(gsStatusFileName,"r")
	local sLine			=	nil
	sLine				=	File.readLine()
	gnStatusType		=	tonumber(ArcUtilities.StringExtract(sLine,1,";"))
	gsStatus			=	ArcUtilities.StringExtract(sLine,2,";")
	sLine				=	File.readLine()
	gnMaxLength			=	tonumber(ArcUtilities.StringExtract(sLine,1,";"))
	gnMaxWidth			=	tonumber(ArcUtilities.StringExtract(sLine,2,";"))
	sLine				=	File.readLine()
	gnCPCount			=	tonumber(ArcUtilities.StringExtract(sLine,1,";"))
	gsShulkerDirection	=	ArcUtilities.StringExtract(sLine,2,";")
	File.close()
end

local function ResumeLoadingOperation()
	local bMoved		=	nil
	local sDirection	=	nil
	LoadStatus()
	ArcMoving.LoadPosition()
	CalculateCheckpointsLocation()
	if gnStatusType == ArcConst.TST_NEEDPLAYER then
		gbError = true
		gbRepeatReport = true
		parallel.waitForAll(Interruption,ShowStatus,ReportPositionAndStatus)
	end
	if gsShulkerDirection ~= "" then TakeShulker(1) end
	SaveAndReportStatus(ArcConst.TST_RUNNING,"Moving to starting position...")
	repeat
		bMoved,sDirection = ArcMoving.MoveToStart(false)
		if not bMoved then ResolveMovingError(sDirection) end
	until bMoved
	parallel.waitForAll(StartLoadingOperation,Interruption,ShowStatus)
end

local function LoadSettings()
	local File			=	nil
	local sSettings		=	nil
	local nCategory		=	nil
	local i				=	nil
	if not fs.exists(gsSettingsFileName) then return false end
	File					=	fs.open(gsSettingsFileName,"r")
	sSettings				=	File.readLine()
	gbSavePosAndStatus		=	(tonumber(ArcUtilities.StringExtract(sSettings,1,";")) == 1)
	gbReportPosAndStatus	=	(tonumber(ArcUtilities.StringExtract(sSettings,2,";")) == 1)
	gsReportProtocol		=	ArcUtilities.StringExtract(sSettings,3,";")
	gnChunkLoadTime			=	tonumber(ArcUtilities.StringExtract(sSettings,4,";"))
	sSettings				=	File.readLine()
	gnRefuelType			=	tonumber(ArcUtilities.StringExtract(sSettings,1,";"))
	gbEmergencyRefuel		=	(tonumber(ArcUtilities.StringExtract(sSettings,2,";")) == 1)
	gnLowFuelThreshold		=	tonumber(ArcUtilities.StringExtract(sSettings,3,";"))
	gnHighFuelThreshold		=	tonumber(ArcUtilities.StringExtract(sSettings,4,";"))
	File.close()
end

local function SaveSettings()
	local File		=	fs.open(gsSettingsFileName,"w+")
	File.writeLine(("%d;%d;%s;%d"):format(ArcUtilities.BooleanToInteger(gbSavePosAndStatus),ArcUtilities.BooleanToInteger(gbReportPosAndStatus),gsReportProtocol,gnChunkLoadTime))
	File.writeLine(("%d;%d;%d;%d"):format(gnRefuelType,ArcUtilities.BooleanToInteger(gbEmergencyRefuel),gnLowFuelThreshold,gnHighFuelThreshold))
	File.close()
end

local function CheckForSavedStatus()
	local File			=	nil
	local sLine			=	nil
	local nStatusType	=	nil
	local sStatus		=	nil
	if not fs.exists(gsStatusFileName) then return nil,nil end
	File			=	fs.open(gsStatusFileName,"r")
	sLine			=	File.readLine()
	nStatusType		=	tonumber(ArcUtilities.StringExtract(sLine,1,";"))
	sStatus			=	ArcUtilities.StringExtract(sLine,2,";")
	File.close()
	return nStatusType,sStatus
end

local function EstimatedFuelConsumption()
	local nFuelConsumption	=	nil
	if #gtabCPLocations == 1 then return 0 end
	nFuelConsumption = (#gtabCPLocations-1)*16
	nFuelConsumption = nFuelConsumption+(gtabCPLocations[#gtabCPLocations][1]-1)+(gtabCPLocations[#gtabCPLocations][2]-1)
	nFuelConsumption = nFuelConsumption*(math.ceil(3600/gnChunkLoadTime))
	return nFuelConsumption
end

local function OpenMenu()
	local nStatusType		=	nil
	local sStatus			=	nil
	local bExit				=	nil
	local nKey				=	nil
	local bSettingsOk		=	nil
	local nFuelEstimation	=	nil
	nStatusType,sStatus = CheckForSavedStatus()
	while true do
		ArcMoving.SetSavePosition(gbSavePosAndStatus)
		ArcMoving.SetPosFileName(gsPositionFileName)
		ArcMoving.SetStartPosL(1)
		ArcMoving.SetStartPosW(1)
		ArcMoving.SetStartPosD(1)
		ArcMoving.SetStartPosO(1)
		ArcTerm.Reset()
		ArcTerm.Write(1,1,"<========= ARC CHUNK LOADING =========>")
		ArcTerm.Write(1,2,"-- Loading Operation --")
		ArcTerm.Write(1,3,"- 1 | Start new")
		if nStatusType == 3 then
			ArcTerm.Write(1,4,"- 2 | Resume")
			ArcTerm.Write(1,5,"Last status :")
			ArcTerm.Write(1,6,sStatus)
		end
		ArcTerm.Write(1,8,"- 3 | Settings")
		ArcTerm.Write(1,9,"- 4 | Exit")
		bSettingsOk	=	false
		bExit		=	false
		nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
		if nKey == 1 then
			gnMaxLength		=	nil
			gnMaxWidth		=	nil
			repeat
				ArcTerm.Reset()
				while true do
					ArcTerm.Write(1,1,"<==== LOADING OPERATION SETTINGS =====>")
					ArcTerm.Write(1,3,"Loading area length : ")
					ArcTerm.Write(1,4,"Loading area width  : ")
					ArcTerm.Write(1,6,"Total chekpoints count : ")
					ArcTerm.Write(1,8,"Estimated fuel consumption (/h) : ")
					ArcTerm.Write(1,9,"Fuel level : ")
					if gnMaxLength == nil or gnMaxLength < 0 or gnMaxLength > 999 then gnMaxLength = tonumber(ArcTerm.Read(23,3));break
					else ArcTerm.Write(23,3,gnMaxLength) end
					if gnMaxWidth == nil or gnMaxWidth < 0 or gnMaxWidth > 999 then gnMaxWidth = tonumber(ArcTerm.Read(23,4));break
					else ArcTerm.Write(23,4,gnMaxWidth) end
					CalculateCheckpointsLocation()
					ArcTerm.Write(26,6,#gtabCPLocations)
					nFuelEstimation = EstimatedFuelConsumption()
					ArcTerm.Write(35,8,nFuelEstimation)
					ArcTerm.Write(14,9,("%d/%d"):format(turtle.getFuelLevel(),turtle.getFuelLimit()))
					ArcTerm.Write(1,13,"1 | Confirm - 2 | Cancel")
					nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
					if nKey == 1 then bSettingsOk = true
					elseif nKey == 2 then bExit = true end
					break
				end
			until bSettingsOk or bExit
			if bSettingsOk then
				gnCPCount = 1
				ArcMoving.TestOrientation()
				parallel.waitForAll(StartLoadingOperation,Interruption,ShowStatus)
				break
			end
		elseif nKey == 2 and nStatusType == ArcConst.TST_SHUTDOWN then
			ResumeLoadingOperation()
		elseif nKey == 3 then
			repeat
				ArcTerm.Reset()
				while true do
					ArcTerm.Write(1,1,"<========= GENERAL SETTINGS ==========>")
					ArcTerm.Write(1,2,"Refuel type : ")
					ArcTerm.Write(1,3,"- 1 | No refuel")
					ArcTerm.Write(1,4,"- 2 | Refuel at starting position")
					ArcTerm.Write(1,5,"- 3 | Refuel from shulker")
					ArcTerm.Write(1,6,"Emergency refuel : ")
					ArcTerm.Write(1,7,"Low fuel Threshold : ")
					ArcTerm.Write(1,8,"High fuel Threshold : ")
					ArcTerm.Write(1,9,"Chunk load time : ")
					ArcTerm.Write(1,10,"Save position and status : ")
					ArcTerm.Write(1,11,"Report position and status : ")
					if gbReportPosAndStatus == true then ArcTerm.Write(1,12,"Report protocol : ") end
					if gnRefuelType <= 0 or gnRefuelType > 3 then gnRefuelType = ArcUtilities.KeyToNumber(ArcTerm.ReadKey());break
					else ArcTerm.Write(1,2+gnRefuelType,"X") end
					if gbEmergencyRefuel == nil then gbEmergencyRefuel = ArcTerm.ReadYesNo(20,6);break
					else ArcTerm.WriteYesNo(20,6,gbEmergencyRefuel) end
					if gnLowFuelThreshold == nil or gnLowFuelThreshold < 0 or gnLowFuelThreshold > turtle.getFuelLimit() then gnLowFuelThreshold = tonumber(ArcTerm.Read(22,7));break
					else ArcTerm.Write(22,7,gnLowFuelThreshold) end
					if gnHighFuelThreshold == nil or gnHighFuelThreshold < 0 or gnHighFuelThreshold > turtle.getFuelLimit() or gnHighFuelThreshold < gnLowFuelThreshold then gnHighFuelThreshold = tonumber(ArcTerm.Read(23,8));break
					else ArcTerm.Write(23,8,gnHighFuelThreshold) end
					if gnChunkLoadTime == nil or gnChunkLoadTime <= 0 then gnChunkLoadTime = tonumber(ArcTerm.Read(19,9));break
					else ArcTerm.Write(19,9,gnChunkLoadTime) end
					if gbSavePosAndStatus == nil then gbSavePosAndStatus = ArcTerm.ReadYesNo(28,10);break
					else ArcTerm.WriteYesNo(28,10,gbSavePosAndStatus) end
					if gbReportPosAndStatus == nil then gbReportPosAndStatus = ArcTerm.ReadYesNo(30,11);break
					else ArcTerm.WriteYesNo(30,11,gbReportPosAndStatus) end
					if gbReportPosAndStatus == true then
						if gsReportProtocol == nil or gsReportProtocol == " " then gsReportProtocol = ArcTerm.Read(19,12);break
						else ArcTerm.Write(19,12,gsReportProtocol) end
					else gsReportProtocol = " " end
					SaveSettings()
					ArcTerm.Write(1,13,"1 | Edit - 2 | Exit")
					nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
					if nKey == 1 then
						gnRefuelType			=	0
						gbEmergencyRefuel		=	nil
						gnLowFuelThreshold		=	nil
						gnHighFuelThreshold		=	nil
						gnChunkLoadTime			=	nil
						gbSavePosAndStatus		=	nil
						gbReportPosAndStatus	=	nil
						gsReportProtocol		=	nil
					elseif nKey == 2 then bExit = true end
					break
				end
			until bExit
		elseif nKey == 4 then
			ArcTerm.Reset()
			break
		end
	end
end

local function Main()
	local nStatusType	=	nil
	local sStatus		=	nil
	local sBlockName	=	nil
	sBlockName = ArcBlocks.BlockName(turtle.getEquippedLeft())
	if sBlockName == "computercraft:wireless_modem_normal" or sBlockName == "computercraft:wireless_modem_advanced" then rednet.open("left") end
	sBlockName = ArcBlocks.BlockName(turtle.getEquippedRight())
	if sBlockName == "computercraft:wireless_modem_normal" or sBlockName == "computercraft:wireless_modem_advanced" then rednet.open("right") end
	LoadSettings()
	nStatusType,sStatus = CheckForSavedStatus()
	if nStatusType == ArcConst.TST_RUNNING or nStatusType == ArcConst.TST_NEEDPLAYER then
		ArcMoving.SetSavePosition(gbSavePosAndStatus)
		ArcMoving.SetPosFileName(gsPositionFileName)
		ArcMoving.SetStartPosL(1)
		ArcMoving.SetStartPosW(1)
		ArcMoving.SetStartPosD(1)
		ArcMoving.SetStartPosO(1)
		ResumeLoadingOperation()
	else
		OpenMenu()
	end
end

Main()
os.shutdown()