civilwargeeky

Quarry Receiver 3.5.4

Nov 1st, 2014
757
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --Quarry Receiver Version 3.5.4
  2. --Made by Civilwargeeky
  3. --[[
  4. Ideas:
  5.  
  6.    
  7. ]]
  8. --[[
  9. Recent Changes:
  10.   Completely Remade!
  11. ]]
  12.  
  13. local commandHelpParagraph = [[
  14. Stop: Stops the turtle where it is
  15. Return: The turtle will return to its starting point, drop off its load, and stop
  16. Drop: Turtle will immediately go and drop its inventory
  17. Pause: Pauses the turtle
  18. Resume: Resumes paused turtles
  19. Help: This :D
  20. ]]
  21.  
  22.  
  23. --Config
  24. local doDebug = false --For testing purposes
  25. local ySizes = 3 --There are 3 different Y Screen Sizes right now
  26.  
  27. --Initializing Program-Wide Variables
  28. local expectedMessage = "Civil's Quarry" --Expected initial message
  29. local expectedFingerprint = "quarry"
  30. local replyMessage = "Turtle Quarry Receiver" --Message to respond to  handshake with
  31. local replyFingerprint = "quarryReceiver"
  32. local stopMessage = "stop"
  33. local expectedFingerprint = "quarry"
  34. local themeFolder = "quarryResources/receiverThemes/"
  35. local modemSide --User can specify a modem side, but it is not necessary
  36. local modem --This will be the table for the modem
  37. local computer --The main screen is special. It gets defined first :3
  38. local continue = true --This keeps the main while loop going
  39. --These two are used by controller in main loop
  40. local commandString = "" --This will be a command string sent to turtle. This var is stored for display
  41. local queuedMessage --If a command needs to be sent, this gets set
  42. local defaultSide
  43. local defaultCommand
  44.  
  45. local keyMap = {[57] = " ", [11] = "0", [12] = "_", [52] = "."} --This is for command string
  46. for i=2,10 do keyMap[i] = tostring(i-1) end --Add numbers
  47. for a,b in pairs(keys) do --Add all letters from keys api
  48.   if #a == 1 then
  49.     keyMap[b] = a:upper()
  50.   end
  51. end
  52. keyMap[keys.enter] = "enter"
  53. keyMap[keys.backspace] = "backspace"
  54.  
  55. --Generic Functions--
  56. local function debug(...)
  57.   --if doDebug then return print(...) end --Basic
  58.   if doDebug then
  59.     print("\nDEBUG: ",...)
  60.     os.pullEvent("char")
  61.   end
  62. end
  63. local function clearScreen(x,y, periph)
  64.   periph, x, y = periph or term, x or 1, y or 1
  65.   periph.clear()
  66.   periph.setCursorPos(x,y)
  67. end
  68.  
  69. local function swapKeyValue(tab)
  70.   for a,b in pairs(tab) do
  71.     tab[b] = a
  72.   end
  73.   return tab
  74. end
  75. local function copyTable(tab)
  76.   local toRet = {}
  77.   for a,b in pairs(tab) do
  78.     toRet[a] = b
  79.   end
  80.   return toRet
  81. end
  82. local function checkChannel(num)
  83.   num = tonumber(num)
  84.   if not num then return false end
  85.   if 1 <= num and num <= 65535 then
  86.     return num
  87.   end
  88.   return false
  89. end
  90. local function align(text, xDim, direction)
  91.   text = tostring(text or "None")
  92.   if #text >= xDim then return text end
  93.   for i=1, xDim-#text do
  94.     if direction == "right" then
  95.       text = " "..text
  96.     elseif direction == "left" then
  97.       text = text.." "
  98.     end
  99.   end
  100.   return text
  101. end
  102. local function alignR(text, xDim)
  103.   return align(text, xDim, "right")
  104. end
  105. local function alignL(text, xDim)
  106.   return align(text, xDim, "left")
  107. end
  108. local function center(text, xDim)
  109.   xDim = xDim or dim[1] --Temp fix
  110.   local a = (xDim-#text)/2
  111.   for i=1, a do
  112.     text = " "..text.." "
  113.   end
  114.   return text  
  115. end
  116. local function roundNegative(num) --Rounds numbers up to 0
  117.   if num >= 0 then return num else return 0 end
  118. end
  119. local function displayHelp()
  120.   print("I am help!")
  121.   print("This will be done later")
  122.   print("For now just check the forum")
  123.   print("Press any key")
  124.   os.pullEvent("key")
  125.   error("",0)
  126. end
  127.  
  128.  
  129. local function testPeripheral(periph, periphFunc)
  130.   if type(periph) ~= "table" then return false end
  131.   if type(periph[periphFunc]) ~= "function" then return false end
  132.   if periph[periphFunc]() == nil then --Expects string because the function could access nil
  133.     return false
  134.   end
  135.   return true
  136. end
  137.  
  138. local function initModem() --Sets up modem, returns true if modem exists
  139.   if not testPeripheral(modem, "isWireless") then
  140.     if peripheral.getType(modemSide or "") == "modem" then
  141.       modem = peripheral.wrap(modemSide)    
  142.       if not modem.isWireless() then --Apparently this is a thing
  143.         modem = nil
  144.         return false
  145.       end
  146.       return true
  147.     end
  148.     if peripheral.find then
  149.       modem = peripheral.find("modem", function(side, obj) return obj.isWireless() end)
  150.     end
  151.     return modem and true or false
  152.   end
  153.   return true
  154. end
  155.  
  156. --COLOR/THEME RELATED
  157. for a, b in pairs(colors) do --This is so commands color commands can be entered in one case
  158.   colors[a:lower()] = b
  159. end
  160.  
  161. local themes = {} --Loaded themes, gives each one a names
  162. local function newTheme(name)
  163.   name = name:lower() or "none"
  164.   local self = {name = name}
  165.   self.addColor = function(self, name, text, back) --Background is optional. Will not change if nil
  166.     name = name or "none"
  167.     self[name] = {text = text, background = back}
  168.     return self --Allows for chaining :)
  169.   end
  170.   themes[name] = self
  171.   return self
  172. end
  173.  
  174. local requiredColors = {"title", "subtitle", "pos", "dim", "extra", "error", "info", "inverse", "command", "help", "background"}
  175.  
  176. --This is how adding colors will work
  177. newTheme("default")
  178.   :addColor("title", colors.green, colors.gray)
  179.   :addColor("subtitle", colors.white, colors.black)
  180.   :addColor("pos", colors.green, colors.black)
  181.   :addColor("dim", colors.lightBlue, colors.black)
  182.   :addColor("extra", colors.lightGray, colors.black)
  183.   :addColor("error", colors.red, colors.white)
  184.   :addColor("info", colors.blue, colors.lightGray)
  185.   :addColor("inverse", colors.yellow, colors.lightGray)
  186.   :addColor("command", colors.lightBlue, colors.black)
  187.   :addColor("help", colors.red, colors.white)
  188.   :addColor("background", colors.white, colors.black)
  189.  
  190. newTheme("random")
  191.   :addColor("title", colors.pink, colors.blue)
  192.   :addColor("subtitle", colors.black, colors.white)
  193.   :addColor("pos", colors.green, colors.black)
  194.   :addColor("dim", colors.lightBlue, colors.black)
  195.   :addColor("extra", colors.lightGray, colors.lightBlue)
  196.   :addColor("error", colors.white, colors.yellow)
  197.   :addColor("info", colors.blue, colors.lightGray)
  198.   :addColor("inverse", colors.yellow, colors.lightGray)
  199.   :addColor("command", colors.green, colors.lightGray)
  200.   :addColor("help", colors.white, colors.yellow)
  201.   :addColor("background", colors.white, colors.red)
  202.  
  203. --If you modify a theme a bunch and want to save it
  204. local function saveTheme(theme, fileName)
  205.   if not theme or not type(fileName) == "string" then return false end
  206.   local file = fs.open(fileName,"w")
  207.   if not file then return false end
  208.   file.writeLine(fileName)
  209.   for a,b in pairs(theme) do
  210.     if type(b) == "table" then --If it contains color objects
  211.       file.writeLine(a.." "..tostring(b.text).." "..tostring(b.background))
  212.     end
  213.   end
  214.   file.close()
  215.   return true
  216. end
  217.  
  218.  
  219. --==SCREEN CLASS FUNCTIONS==
  220. local screenClass = {} --This is the class for all monitor/screen objects
  221. screenClass.screens = {} --A simply numbered list of screens
  222. screenClass.sides = {} --A mapping of screens by their side attached
  223. screenClass.channels = {} --A mapping of receiving channels that have screens attached. Used for the receiver part
  224. screenClass.sizes = {{7,18,29,39,50}, {5,12,19} , computer = {51, 19}, turtle = {39,13}, pocket = {26,20}}
  225.  
  226. screenClass.setTextColor = function(self, color) --Accepts raw color
  227.   if color and self.term.isColor() then
  228.     self.textColor = color
  229.     self.term.setTextColor(color)
  230.     return true
  231.   end
  232.   return false
  233. end
  234. screenClass.setBackgroundColor = function(self, color) --Accepts raw color
  235.   if color and self.term.isColor() then
  236.     self.backgroundColor = color
  237.     self.term.setBackgroundColor(color)
  238.     return true
  239.   end
  240.   return false
  241. end
  242. screenClass.setColor = function(self, color) --Wrapper, accepts themecolor objects
  243.   if type(color) ~= "table" then error("Set color received a non-table",2) end
  244.   return self:setTextColor(color.text) and self:setBackgroundColor(color.background)
  245. end
  246.  
  247. screenClass.themeName = "default" --Setting super for fallback
  248. screenClass.theme = themes.default
  249.  
  250.  
  251. screenClass.new = function(side, receive, themeFile)
  252.   local self = {}
  253.   setmetatable(self, {__index = screenClass}) --Establish Hierarchy
  254.   self.side = side
  255.   if side == "computer" then
  256.     self.term = term
  257.   else
  258.     self.term = peripheral.wrap(side)
  259.     if not (self.term and peripheral.getType(side) == "monitor") then --Don't create an object if it doesn't exist
  260.       if doDebug then
  261.         error("No monitor on side "..tostring(side))
  262.       end
  263.       self = nil --Save memory?
  264.       return false
  265.     end
  266.   end
  267.  
  268.   --Channels and ids
  269.   self.receive = tonumber(receive) --Receive Channel
  270.   self.send = nil --Reply Channel, obtained in handshake
  271.   self.id = #screenClass.screens+1
  272.   --Colors
  273.   self.themeName = nil --Will be set by setTheme
  274.   self.theme = nil
  275.   self.isColor = self.term.isColor() --Just for convenience
  276.   --Other Screen Properties
  277.   self.dim = {self.term.getSize()} --Raw dimensions
  278.   --Initializations
  279.   self.isDone = false --Flag for when the turtle is done transmitting
  280.   self.size = {} --Screen Size, assigned in setSize
  281.   self.textColor = colors.white --Just placeholders until theme is loaded and run
  282.   self.backColor = colors.black
  283.   self.toPrint = {}
  284.   self.isComputer = false
  285.   self.isTurtle = false
  286.   self.isPocket = false
  287.   self.acceptsInput = false
  288.   self.legacy = false --Whether it expects tables or strings
  289.   self.rec = { --Initial values for all displayed numbers
  290.     label = "Quarry Bot",
  291.     id = 1,
  292.     percent = 0,
  293.     relxPos = 0,
  294.     zPos = 0,
  295.     layersDone = 0,
  296.     x = 0,
  297.     z = 0,
  298.     layers = 0,
  299.     openSlots = 0,
  300.     mined = 0,
  301.     moved = 0,
  302.     chestFull = false,
  303.     isAtChest = false,
  304.     isGoingToNextLayer = false,
  305.     foundBedrock = false,
  306.     fuel = 0,
  307.     volume = 0,
  308.     distance = 0,
  309.     yPos = 0
  310.     --Maybe add in some things like if going to then add a field
  311.   }
  312.  
  313.   screenClass.screens[self.id] = self
  314.   screenClass.sides[self.side] = self
  315.   if self.receive then
  316.     modem.open(self.receive) --Modem should be defined by the time anything is open
  317.     screenClass.channels[self.receive] = self --If anyone ever asked, you could have multiple screens per channel, but its silly if no one ever needs it
  318.   end
  319.   self:setSize() --Finish Initialization
  320.   self:setTheme(themeFile)
  321.   return self
  322. end
  323.  
  324. screenClass.remove = function(tab) --Cleanup function
  325.   if type(tab) == "number" then --Expects table, can take id (for no apparent reason)
  326.     tab = screenClass.screens[tab]
  327.   end
  328.   if tab.side == "REMOVED" then return end
  329.   if tab.side == "computer" then error("Tried removing computer screen",2) end --This should never happen
  330.   tab:reset() --Clear screen
  331.   tab:say("Removed", tab.theme.info, 1) --Let everyone know whats up
  332.   screenClass.screens[tab.id] = {side = "REMOVED"} --Not nil because screw up len()
  333.   screenClass.sides[tab.side] = nil
  334.   screenClass.channels[tab.receive] = nil
  335.   if modem and modem.isOpen(tab.receive) then
  336.     modem.close(tab.receive)
  337.   end
  338. end
  339.  
  340. --Init Functions
  341. screenClass.setSize = function(self) --Sets screen size
  342.   if self.side ~= "computer" and not self.term then self.term = peripheral.wrap(self.side) end
  343.   if not self.term.getSize() then --If peripheral is having problems/not there. Don't go further than term, otherwise index nil (maybe?)
  344.     debug("There is no term...")
  345.     self.updateDisplay = function() end --Do nothing on screen update, overrides class
  346.     return true
  347.   elseif not self.receive then
  348.     self:setBrokenDisplay() --This will prompt user to set channel
  349.   elseif self.send then --This allows for class inheritance
  350.     self:init() --In case objects have special updateDisplay methods --Remove function in case it exists, defaults to super
  351.   else --If the screen needs to have a handshake display
  352.     self:setHandshakeDisplay()
  353.   end
  354.   self.dim = { self.term.getSize()}
  355.   local tab = screenClass.sizes
  356.   for a=1, 2 do --Want x and y dim
  357.     for b=1, #tab[a] do --Go through all normal sizes, x and y individually
  358.       if tab[a][b] <= self.dim[a] then --This will set size higher until false
  359.         self.size[a] = b
  360.       end
  361.     end
  362.   end
  363.   local function isThing(toCheck, thing) --E.G. isThing(self.dim,"computer")
  364.     return toCheck[1] == tab[thing][1] and toCheck[2] == tab[thing][2]
  365.   end
  366.   self.isComputer = isThing(self.dim, "computer")
  367.   self.isTurtle = isThing(self.dim, "turtle")
  368.   self.isPocket = isThing(self.dim, "pocket")
  369.   self.acceptsInput = self.isComputer or self.isTurtle or self.isPocket
  370.   return self
  371. end
  372.  
  373. screenClass.setTheme = function(self, themeName, stopReset)
  374.   if not themes[themeName] then --If we don't have it already, try to load it
  375.     local fileName = themeName or ".." --.. returns false and I don't think you can name a file this
  376.     if fs.exists(themeFolder) then fileName = themeFolder..fileName end
  377.     if fs.exists(fileName) then
  378.       debug("Loading theme: ",fileName)
  379.       local file = fs.open(fileName, "r")
  380.       if not file then debug("Could not load theme '",themeName,"' file not found") end
  381.       local addedTheme = newTheme(file.readLine() or "newTheme") --Initializes the new theme
  382.       for line in function() return file.readLine() end do --Go through all the color lines (hopefully not the first one. Requires testing)
  383.         local args = {}
  384.         for word in line:gmatch("%S+") do
  385.           table.insert(args,word)
  386.         end
  387.         addedTheme:addColor(args[1]:match("%a+") or "nothing", tonumber(args[2]) or colors[args[2]], tonumber(args[3]) or colors[args[3]]) --"nothing" will never get used, so its just lazy error prevention
  388.       end
  389.       file.close()
  390.       local flag = true --Make sure a theme has all required elements
  391.       for a,b in ipairs(requiredColors) do
  392.         if not addedTheme[b] then
  393.           flag = false
  394.           debug("Theme is missing color '",b,"'")
  395.         end
  396.       end
  397.       if not flag then
  398.         themes[addedTheme.name] = nil
  399.         debug("Failed to load theme")
  400.         return false
  401.       end
  402.       self.themeName = themeName:lower() --We can now set our themeName to the fileName
  403.     else
  404.       --Resets theme to super
  405.       if not stopReset then --This exists so its possible to set default theme without breaking world
  406.         self.themeName = nil
  407.         self.theme = nil
  408.       end
  409.       return false
  410.     end
  411.    else
  412.     self.themeName = themeName:lower()
  413.    end
  414.    self.theme = themes[self.themeName] --Now the theme is loaded or the function doesn't get here
  415.    return true
  416. end
  417.  
  418. --Adds text to the screen buffer
  419. screenClass.tryAddRaw = function(self, line, text, color, ...) --This will try to add text if Y dimension is a certain size
  420.   local doAdd = {...} --booleans for small, medium, and large
  421.   if type(text) ~= "string" then error("tryAddRaw got "..type(text)..", expected string",2) end
  422.   text = text or "NIL"
  423.   if type(color) ~= "table" then error("tryAddRaw did not get a color",2) end
  424.   --color = color or {text = colors.white}
  425.   for i=1, ySizes do --As of now there are 3 Y sizes
  426.     local test = doAdd[i]
  427.     if test == nil then test = doAdd[#doAdd] end --Set it to the last known setting if doesn't exist
  428.     if test and self.size[2] == i then --If should add this text for this screen size and the monitor is this size
  429.       if #text <= self.dim[1] then
  430.         self.toPrint[line] = {text = text, color = color}
  431.         return true
  432.       else
  433.         debug("Tried adding ",text," on line ",line," but was too long")
  434.       end
  435.     end
  436.   end
  437.   return false
  438. end
  439. screenClass.tryAdd = function(self, text, color,...) --Just a wrapper
  440.   return self:tryAddRaw(#self.toPrint+1, text, color, ...)
  441. end
  442. screenClass.tryAddC = function(self, text, color, ...) --Centered text
  443.   return self:tryAdd(center(text, self.dim[1]), color, ...)
  444. end
  445.  
  446. screenClass.reset = function(self,color)
  447.   color = color or self.theme.background
  448.   self:setColor(color)
  449.   self.term.clear()
  450.   self.term.setCursorPos(1,1)
  451. end
  452. screenClass.say = function(self, text, color, line)
  453.   local currColor = self.backgroundColor
  454.   color = color or debug("Printing ",text," but had no themeColor: ",self.theme.name) or {} --Set default for nice error, alert that errors occur
  455.   self:setColor(color)
  456.   local line = line or ({self.term.getCursorPos()})[2] or self:setSize() or 1 --If current yPos not found, sets screen size and moves cursor to 1
  457.   if doDebug and #text > self.dim[1] then error("Tried printing: '"..text.."', but was too big") end
  458.   self.term.setCursorPos(1,line)
  459.   for i=1, self.dim[1]-#text do --This is so the whole line's background gets filled.
  460.     text = text.." "
  461.   end
  462.   self.term.write(text)
  463.   self.term.setCursorPos(1, line+1)
  464. end
  465. screenClass.pushScreenUpdates = function(self)
  466.   for i=1, self.dim[2] do
  467.     local tab = self.toPrint[i]
  468.     if tab then
  469.       self:say(tab.text, tab.color, i)
  470.     end
  471.   end
  472.   self.term.setCursorPos(1,self.dim[2]) --So we can see errors
  473. end
  474.  
  475. screenClass.updateNormal = function(self) --This is the normal updateDisplay function
  476.   local str = tostring
  477.   self.toPrint = {} --Reset table
  478.   local message, theme = self.rec, self.theme
  479.  
  480.   if not self.isDone then --Normally
  481.     if self.size[1] == 1 then --Small Width Monitor
  482.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  483.         self:tryAdd("Quarry!", theme.title, false, false, true)
  484.       end
  485.      
  486.       self:tryAdd("-Fuel-", theme.subtitle , false, true, true)
  487.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  488.         self:tryAdd("A lot", theme.extra, false, true, true)
  489.       end
  490.      
  491.       self:tryAdd("--%%%--", theme.subtitle, false, true, true)
  492.       self:tryAdd(alignR(str(message.percent).."%", 7), theme.pos , false, true, true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
  493.       self:tryAdd(center(str(message.percent).."%", self.dim[1]), theme.pos, true, false) --I want it to be centered on 1x1
  494.      
  495.       self:tryAdd("--Pos--", theme.subtitle, false, true, true)
  496.       self:tryAdd("X:"..alignR(str(message.relxPos), 5), theme.pos, true)
  497.       self:tryAdd("Z:"..alignR(str(message.zPos), 5), theme.pos , true)
  498.       self:tryAdd("Y:"..alignR(str(message.layersDone), 5), theme.pos , true)
  499.      
  500.       if not self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true, false) then --If you can't display the y, then don't
  501.         self:tryAdd(str(message.x).."x"..str(message.z), theme.dim , true, false)
  502.       end
  503.       self:tryAdd("--Dim--", theme.subtitle, false, true, true)
  504.       self:tryAdd("X:"..alignR(str(message.x), 5), theme.dim, false, true, true)
  505.       self:tryAdd("Z:"..alignR(str(message.z), 5), theme.dim, false, true, true)
  506.       self:tryAdd("Y:"..alignR(str(message.layers), 5), theme.dim, false, true, true)
  507.      
  508.       self:tryAdd("-Extra-", theme.subtitle, false, false, true)
  509.       self:tryAdd(alignR(textutils.formatTime(os.time()):gsub(" ","").."", 7), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  510.       self:tryAdd("Open:"..alignR(str(message.openSlots),2), theme.extra, false, false, true)
  511.       self:tryAdd("Dug"..alignR(str(message.mined), 4), theme.extra, false, false, true)
  512.       self:tryAdd("Mvd"..alignR(str(message.moved), 4), theme.extra, false, false, true)
  513.       if message.chestFull then
  514.         self:tryAdd("ChstFll", theme.error, false, false, true)
  515.       end
  516.      
  517.     end
  518.     if self.size[1] == 2 then --Medium Monitor
  519.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  520.         self:tryAdd("Quarry!", theme.title, false, false, true)
  521.       end
  522.      
  523.       self:tryAdd("-------Fuel-------", theme.subtitle , false, true, true)
  524.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  525.         self.toPrint[#self.toPrint] = nil
  526.         self:tryAdd("A lot", theme.extra, false, true, true)
  527.       end
  528.      
  529.       self:tryAdd(str(message.percent).."% Complete", theme.pos , true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
  530.      
  531.       self:tryAdd("-------Pos--------", theme.subtitle, false, true, true)
  532.       self:tryAdd("X Coordinate:"..alignR(str(message.relxPos), 5), theme.pos, true)
  533.       self:tryAdd("Z Coordinate:"..alignR(str(message.zPos), 5), theme.pos , true)
  534.       self:tryAdd("On Layer:"..alignR(str(message.layersDone), 9), theme.pos , true)
  535.      
  536.       if not self:tryAdd("Size: "..str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true, false) then --This is already here... I may as well give an alternative for those people with 1000^3quarries
  537.         self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true, false)
  538.       end
  539.       self:tryAdd("-------Dim--------", theme.subtitle, false, true, true)
  540.       self:tryAdd("Total X:"..alignR(str(message.x), 10), theme.dim, false, true, true)
  541.       self:tryAdd("Total Z:"..alignR(str(message.z), 10), theme.dim, false, true, true)
  542.       self:tryAdd("Total Layers:"..alignR(str(message.layers), 5), theme.dim, false, true, true)
  543.       self:tryAdd("Volume"..alignR(str(message.volume),12), theme.dim, false, false, true)
  544.      
  545.       self:tryAdd("------Extras------", theme.subtitle, false, false, true)
  546.       self:tryAdd("Time: "..alignR(textutils.formatTime(os.time()):gsub(" ","").."", 12), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  547.       self:tryAdd("Used Slots:"..alignR(str(16-message.openSlots),7), theme.extra, false, false, true)
  548.       self:tryAdd("Blocks Mined:"..alignR(str(message.mined), 5), theme.extra, false, false, true)
  549.       self:tryAdd("Spaces Moved:"..alignR(str(message.moved), 5), theme.extra, false, false, true)
  550.       if message.chestFull then
  551.         self:tryAdd("Chest Full, Fix It", theme.error, false, true, true)
  552.       end
  553.     end
  554.     if self.size[1] >= 3 then --Large or larger screens
  555.       if not self:tryAdd(message.label..alignR(" Turtle #"..str(message.id),self.dim[1]-#message.label), theme.title, true) then
  556.         self:tryAdd("Your turtle's name is long...", theme.title, true)
  557.       end
  558.       self:tryAdd("Fuel: "..alignR(str(message.fuel),self.dim[1]-6), theme.extra, true)
  559.      
  560.       self:tryAdd("Percentage Done: "..alignR(str(message.percent).."%",self.dim[1]-17), theme.pos, true)
  561.      
  562.       local var1 = math.max(#str(message.x), #str(message.z), #str(message.layers))
  563.       local var2 = (self.dim[1]-6-var1+3)/3
  564.       self:tryAdd("Pos: "..alignR(" X:"..alignR(str(message.relxPos),var1),var2)..alignR(" Z:"..alignR(str(message.zPos),var1),var2)..alignR(" Y:"..alignR(str(message.layersDone),var1),var2), theme.pos, true)
  565.       self:tryAdd("Size:"..alignR(" X:"..alignR(str(message.x),var1),var2)..alignR(" Z:"..alignR(str(message.z),var1),var2)..alignR(" Y:"..alignR(str(message.layers),var1),var2), theme.dim, true)
  566.       self:tryAdd("Volume: "..str(message.volume), theme.dim, false, true, true)
  567.       self:tryAdd("",{}, false, false, true)
  568.       self:tryAdd(center("____---- EXTRAS ----____",self.dim[1]), theme.subtitle, false, false, true)
  569.       self:tryAdd(center("Time:"..alignR(textutils.formatTime(os.time()),8), self.dim[1]), theme.extra, false, true, true)
  570.       self:tryAdd(center("Current Day: "..str(os.day()), self.dim[1]), theme.extra, false, false, true)
  571.       self:tryAdd("Used Inventory Slots: "..alignR(str(16-message.openSlots),self.dim[1]-22), theme.extra, false, true, true)
  572.       self:tryAdd("Blocks Mined: "..alignR(str(message.mined),self.dim[1]-14), theme.extra, false, true, true)
  573.       self:tryAdd("Blocks Moved: "..alignR(str(message.moved),self.dim[1]-14), theme.extra, false, true, true)
  574.       self:tryAdd("Distance to Turtle: "..alignR(str(message.distance), self.dim[1]-20), theme.extra, false, false, true)
  575.       self:tryAdd("Actual Y Pos (Not Layer): "..alignR(str(message.yPos), self.dim[1]-26), theme.extra, false, false, true)
  576.      
  577.       if message.chestFull then
  578.         self:tryAdd("Dropoff is Full, Please Fix", theme.error, false, true, true)
  579.       end
  580.       if message.foundBedrock then
  581.         self:tryAdd("Found Bedrock! Please Check!!", theme.error, false, true, true)
  582.       end
  583.       if message.isAtChest then
  584.         self:tryAdd("Turtle is at home chest", theme.info, false, true, true)
  585.       end
  586.       if message.isGoingToNextLayer then
  587.         self:tryAdd("Turtle is going to next layer", theme.info, false, true, true)
  588.       end
  589.     end
  590.   else --If is done
  591.     if self.size[1] == 1 then --Special case for small monitors
  592.       self:tryAdd("Done", theme.title, true)
  593.       self:tryAdd("Dug"..alignR(str(message.mined),4), theme.pos, true)
  594.       self:tryAdd("Fuel"..alignR(str(message.fuel),3), theme.pos, true)
  595.       self:tryAdd("-------", theme.subtitle, false,true,true)
  596.       self:tryAdd("Turtle", theme.subtitle, false, true, true)
  597.       self:tryAdd(center("is", self.dim[1]), theme.subtitle, false, true, true)
  598.       self:tryAdd(center("Done!", self.dim[1]), theme.subtitle, false, true, true)
  599.     else
  600.       self:tryAdd("Done!", theme.title, true)
  601.       self:tryAdd("Blocks Dug: "..str(message.mined), theme.inverse, true)
  602.       self:tryAdd("Cobble Dug: "..str(message.cobble), theme.pos, false, true, true)
  603.       self:tryAdd("Fuel Dug: "..str(message.fuelblocks), theme.pos, false, true, true)
  604.       self:tryAdd("Others Dug: "..str(message.other), theme.pos, false, true, true)
  605.       self:tryAdd("Curr Fuel: "..str(message.fuel), theme.inverse, true)
  606.     end
  607.   end
  608. end
  609. screenClass.updateHandshake = function(self)
  610.   self.toPrint = {}
  611.   local half = math.ceil(self.dim[2]/2)
  612.   if self.size[1] == 1 then --Not relying on the parameter system because less calls
  613.     self:tryAddRaw(half-2, "Waiting", self.theme.error, true)
  614.     self:tryAddRaw(half-1, "For Msg", self.theme.error, true)
  615.     self:tryAddRaw(half, "On Chnl", self.theme.error, true)
  616.     self:tryAddRaw(half+1, tostring(self.receive), self.theme.error, true)
  617.   else
  618.     local str = "for"
  619.     if self.size[1] == 2 then str = "4" end--Just a small grammar change
  620.     self:tryAddRaw(half-2, "", self.theme.error, true) --Filler
  621.     self:tryAddRaw(half-1, center("Waiting "..str.." Message", self.dim[1]), self.theme.error, true)
  622.     self:tryAddRaw(half, center("On Channel "..tostring(self.receive), self.dim[1]), self.theme.error, true)
  623.     self:tryAddRaw(half+1, "",self.theme.error, true)
  624.   end
  625. end
  626. screenClass.updateBroken = function(self) --If screen needs channel
  627.   self.toPrint = {}
  628.   if self.size[1] == 1 then
  629.     self:tryAddC("No Rec", self.theme.pos, false, true, true)
  630.     self:tryAddC("Channel", self.theme.pos, false, true, true)
  631.     self:tryAddC("-------", self.theme.title, false, true, true)
  632.     self:tryAddC("On Comp", self.theme.info, true)
  633.     self:tryAddC("Type:", self.theme.info, true)
  634.     self:tryAddC("RECEIVE", self.theme.command, true)
  635.     if not self:tryAddC(self.side:upper(), self.theme.command, true) then --If we can't print the full side
  636.       self:tryAddC("[side]",self.theme.command, true)
  637.     end
  638.     self:tryAddC("[Chnl]", self.theme.command, true)
  639.   else
  640.     self:tryAddC("No receiving", self.theme.pos, false, true, true)
  641.     self:tryAddC("channel for", self.theme.pos, false, true, true)
  642.     self:tryAddC("this screen", self.theme.pos, false, true, true)
  643.     self:tryAddC("-----------------", self.theme.title, false, true, true)
  644.     self:tryAddC("On main computer,", self.theme.info, true)
  645.     self:tryAddC("Type:", self.theme.info, true)
  646.     self:tryAdd("", self.theme.command, false, true, true)
  647.     self:tryAddC('"""', self.theme.command, false, true, true)
  648.     self:tryAddC("RECEIVE", self.theme.command, true)
  649.     if not self:tryAddC(self.side:upper(), self.theme.command, true) then --If we can't print the full side
  650.       self:tryAddC("[side]",self.theme.command, true)
  651.     end
  652.     self:tryAddC("[desired channel]", self.theme.command, true)
  653.     self:tryAddC('"""', self.theme.command, false, true, true)
  654.   end
  655. end
  656.  
  657. screenClass.updateDisplay = screenClass.updateNormal --Update screen method is normally this one
  658.  
  659. --Misc
  660. screenClass.init = function(self) --Currently used by computer screen to replace its original method
  661.   self.updateDisplay = self.updateNormal --This defaults to super if doesn't exist
  662. end
  663. screenClass.setHandshakeDisplay = function(self)
  664.   self.updateDisplay = self.updateHandshake --Sets update to handshake version, defaults to super if doesn't exist
  665. end
  666. screenClass.setBrokenDisplay = function(self)
  667.   self.updateDisplay = self.updateBroken
  668. end
  669.  
  670.  
  671. local function wrapPrompt(prefix, str, dim) --Used to wrap the commandString
  672.   return prefix..str:sub(roundNegative(#str+#prefix-computer.dim[1]+2), -1).."_" --it is str + 2 because we add in the "_"
  673. end
  674.  
  675. local function updateAllScreens()
  676.   for a, b in pairs(screenClass.sides) do
  677.     b:updateDisplay()
  678.     b:reset()
  679.     b:pushScreenUpdates()
  680.   end
  681. end
  682. --Rednet
  683. local function newMessageID()
  684.   return math.random(1,2000000000) --1 through 2 billion. Good enough solution
  685. end
  686. local function transmit(send, receive, message, legacy, fingerprint)
  687.   fingerprint = fingerprint or replyFingerprint
  688.   if legacy then
  689.     modem.transmit(send, receive, message)
  690.   else
  691.     modem.transmit(send, receive, {message = message, id = newMessageID(), fingerprint = fingerprint})
  692.   end
  693. end
  694.  
  695. --==SET UP==
  696. clearScreen()
  697. print("Welcome to Quarry Receiver!")
  698. sleep(1)
  699.  
  700. while not initModem() do
  701.   clearScreen()
  702.   print("No modem is connected, please attach one")
  703.   if not peripheral.find then
  704.     print("What side was that on?")
  705.     modemSide = read()
  706.   else
  707.     os.pullEvent("peripheral")
  708.   end
  709. end
  710. debug("Modem successfully connected!")
  711.  
  712. --==ARGUMENTS==
  713.  
  714. --[[
  715. Parameters:
  716.   -help/-?/help/?
  717.   -receiveChannel [channel] --For only the main screen
  718.   -theme --Sets a default theme
  719.   -screen [side] [channel] [theme]
  720.   -station
  721.   -auto --Prompts for all sides, or you can supply a list of receive channels for random assignment!
  722.   -colorEditor
  723. ]]
  724.  
  725. --tArgs and peripheral list init
  726. local tArgs = {...}
  727. local parameters = {} --Each command is stored with arguments
  728. local parameterIndex = 0 --So we can add new commands to the right table
  729. for a,b in ipairs(tArgs) do
  730.   val = b:lower()
  731.   if val == "help" or val == "-help" or val == "?" or val == "-?" then
  732.     displayHelp() --To make
  733.     error("The End of Help",0)
  734.   end
  735.   if val:match("^%-") then
  736.     parameterIndex = parameterIndex + 1
  737.     parameters[parameterIndex] = {val:sub(2)} --Starts a chain with the command. Can be unpacked later
  738.     parameters[val:sub(2)] = {} --Needed for force/before/after parameters
  739.   elseif parameterIndex ~= 0 then
  740.     table.insert(parameters[parameterIndex], b) --b because arguments should be case sensitive for filenames
  741.     table.insert(parameters[parameters[parameterIndex][1]], b) --Needed for force/after parameters
  742.   end
  743. end
  744.  
  745. for i=1,#parameters do
  746.   debug("Parameter: ",parameters[i][1])
  747. end
  748.  
  749. --Options before screen loads
  750. if parameters.theme then
  751.   screenClass:setTheme(parameters.theme[1])
  752. end
  753.  
  754. --Init Computer Screen Object (was defined at top)
  755. computer = screenClass.new("computer", parameters.receivechannel and parameters.receivechannel[1])--This sets channel, checking if parameter exists
  756.  
  757.  
  758. computer.displayCommand = function(self)
  759.   local sideString = ((defaultSide and " (") or "")..(defaultSide or "")..((defaultSide and ")") or "")
  760.   if self.size == 1 then
  761.     self:tryAddRaw(self.dim[2], wrapPrompt("Cmd"..sideString:sub(2,-2)..": ", commandString, self.dim[1]), self.theme.command, true)
  762.   else
  763.     self:tryAddRaw(self.dim[2], wrapPrompt("Command"..sideString..": ",commandString, self.dim[1]), self.theme.command, true) --This displays the last part of a string.
  764.   end
  765. end
  766. --Technically, you could have any screen be the station, but oh well.
  767. --Initializing the computer screen
  768. if parameters.station then --This will set the screen update to display stats on all other monitors. For now it does little
  769.   if computer.receive then
  770.     screenClass.receiveChannels[computer.receive] = nil --Because it doesn't have a channel
  771.   end
  772.   computer.receive = nil --So it doesn't receive messages
  773.   computer.send = nil
  774.   computer.isStation = true --For updating
  775.    
  776.   computer.updateNormal = function(self)--This gets set in setSize
  777.     self.toPrint = {}
  778.     local part = math.floor(self.dim[1]/4)-1
  779.     self:tryAdd(alignL(" ID",part).."| "..alignL("Side",part).."| "..alignL("Channel",part).."| "..alignL("Theme",part), self.theme.title, false, true, true) --Headings
  780.     local line = ""
  781.     for i=1, self.dim[1] do line = line.."-" end
  782.     self:tryAdd(line, self.theme.title, false, true, true)
  783.     for a,b in ipairs(screenClass.screens) do
  784.       self:tryAdd(" "..alignL(b.id,part-1).."| "..alignL(b.side,part).."| "..alignL(b.receive, part).."| "..alignL(b.theme.name,part), self.theme.info, false, true, true) --Prints info about all screens
  785.     end
  786.     self:displayCommand()
  787.   end
  788.   computer.setHandshakeDisplay = computer.init --Handshake is same as regular
  789.   computer.setBrokenDisplay = computer.init
  790. elseif parameters.coloreditor then
  791.  
  792.   if computer.receive then --This part copied from above
  793.     screenClass.receiveChannels[computer.receive] = nil --Because it doesn't have a channel
  794.   end
  795.   computer.receive = nil --So it doesn't receive messages
  796.   computer.send = nil
  797.   computer.isStation = true --So we can't assign a channel
  798.  
  799.   computer.updateNormal = function(self) --This is only for editing colors
  800.     self.toPrint = {}
  801.     for a,b in pairs(self.theme) do
  802.       if type(b) == "table" then
  803.         self:tryAdd(a, b, true)
  804.       end
  805.     end
  806.     self:displayCommand()
  807.   end
  808.   computer.updateHandshake = computer.updateNormal
  809.   computer.updateBroken = computer.updateNormal
  810. else --If computer is a regular screen
  811.   computer.updateNormal = function(self)
  812.     screenClass.updateDisplay(self)
  813.     computer:displayCommand()
  814.   end
  815.   computer.updateHandshake = function(self) --Not in setHandshake because that func checks object updateHandshake
  816.     screenClass.updateHandshake(self)
  817.     computer:displayCommand()
  818.   end
  819.   computer.updateBroken = function(self)
  820.     screenClass.updateBroken(self)
  821.     computer:displayCommand()
  822.   end
  823. end
  824. computer:setSize() --Update changes made to display functions
  825.  
  826.  
  827. for i=1, #parameters do --Do actions for parameters that can be used multiple times
  828.   local command, args = parameters[i][1], parameters[i] --For ease
  829.   if command == "screen" then
  830.     local a = screenClass.new(args[2], args[3], args[4])
  831.     debug(type(a))
  832.   end
  833.  
  834. end
  835.  
  836. for a,b in pairs(screenClass.sides) do debug(a) end
  837.  
  838. local function autoDetect(channels)
  839.   if type(channels) ~= "table" then channels = {} end
  840.   local tab = peripheral.getNames()
  841.   local index = 1
  842.   for i=1, #tab do
  843.     if peripheral.getType(tab[i]) == "monitor" and not screenClass.sides[tab[i]] then
  844.       screenClass.new(tab[i], channels[index]) --You can specify a list of channels in "auto" parameter
  845.       index = index+1
  846.     end
  847.   end
  848. end
  849. if parameters.auto then autoDetect(parameters.auto) end
  850.  
  851.  
  852. --==FINAL CHECKS==
  853.  
  854. --Updating all screen for first time and making sure channels are open
  855. for a, b in pairs(screenClass.sides) do
  856.   if b.receive then --Because may not have channel
  857.     if not modem.isOpen(b.receive) then modem.open(b.receive) end
  858.   end
  859.   b:updateDisplay()--Finish initialization process
  860.   b:reset()
  861.   b:pushScreenUpdates()
  862. end
  863.  
  864. --Handshake will be handled in main loop
  865.  
  866. --[[Workflow
  867.   Wait for events
  868.   modem_message
  869.     if valid channel and valid message, update appropriate screen
  870.   key
  871.     if any letter, add to command string if room.
  872.     if enter key
  873.       if valid self command, execute command. Commands:
  874.         command [side] [command] --If only one screen, then don't need channel. Send a command to a turtle
  875.         screen [side] [channel] [theme] --Links a new screen to use.
  876.         remove [side] --Removes a screen
  877.         theme [themeName] --Sets the default theme
  878.         theme [side] [themeName] --Changes this screen's theme
  879.         savetheme [new name] [themeName]
  880.         color [side/theme] [colorName] [textColor] [backgroundColor]
  881.         side [side] --Sets a default side, added to prompts
  882.         set [string] --Sets a default command, added to display immediately
  883.         receive [side] [newChannel] --Changes the channel of the selected screen
  884.         send [side] [newChannel]
  885.         auto --Automatically adds screens not connected
  886.         exit/quit/end
  887.   peripheral_detach
  888.     check what was lost, if modem, set to nil. If screen side, do screen:setSize()
  889.   peripheral
  890.     check if screen side already added
  891.       reset screen size
  892.   monitor_resize
  893.     resize proper screen
  894.   monitor_touch
  895.     if screen already added
  896.       select screen on main computer
  897.     else
  898.       add screen
  899.  
  900. ]]
  901.  
  902. --Modes: 1 - Sided, 2 - Not Sided, 3 - Both sided and not
  903. local validCommands = {command = 1, screen = 2, remove = 1, theme = 3, exit = 2, quit = 2, ["end"] = 2, color = 3, side = 2, set = 2, receive = 1, send = 1, savetheme = 2, auto = 2}
  904. while continue do
  905.   local event, par1, par2, par3, par4, par5 = os.pullEvent()
  906.   ----MESSAGE HANDLING----
  907.   if event == "modem_message" and screenClass.channels[par2] then --If we got a message for a screen that exists
  908.     local screen = screenClass.channels[par2] --For convenience
  909.     if not screen.send then --This is the handshake
  910.       debug("\nChecking handshake. Received: ",par4)
  911.       local flag = false
  912.       if par4 == expectedMessage then
  913.         screen.legacy = true --Accepts serialized tables
  914.         flag = true
  915.       elseif type(par4) == "table" and par4.message == expectedMessage and par4.fingerprint == expectedFingerprint then
  916.         screen.legacy = false
  917.         flag = true
  918.       end
  919.      
  920.       if flag then
  921.         debug("Screen ",screen.side," received a handshake")
  922.         screen.send = par3
  923.         screen:setSize() --Resets update method to proper since channel is set
  924.         debug("Sending back on ",screen.send)
  925.         transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  926.       end
  927.    
  928.     else --Everything else is for regular messages
  929.      
  930.       local rec
  931.       if screen.legacy then --We expect strings here
  932.         if type(par4) == "string" then --Otherwise its not ours
  933.           if par4 == "stop" then --This is the stop message. All other messages will be ending ones
  934.             screen.isDone = true
  935.           elseif par4 == expectedMessage then --We support dropping in mid-run
  936.             debug("Screen ",screen.side," received mid-run handshake")
  937.             transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  938.           elseif textutils.unserialize(par4) then
  939.             rec = textutils.unserialize(par4)
  940.             rec.distance = par5
  941.           end
  942.         end
  943.       elseif type(par4) == "table" and par4.fingerprint == expectedFingerprint then --Otherwise, we check if it is valid message
  944.        
  945.         if type(par4.message) == "table" then
  946.           rec = par4.message
  947.           if not par4.distance then --This is cool because it can add distances from the repeaters
  948.             rec.distance = par5
  949.           else
  950.             rec.distance = par4.distance + par5
  951.           end
  952.           if rec.isDone then
  953.             screen.isDone = true
  954.           end
  955.         elseif par4.message == expectedMessage then
  956.           debug("Screen ",screen.side," received mid-run handshake")
  957.           transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  958.         else
  959.           debug("Message received did not contain table")
  960.         end
  961.       end
  962.        
  963.       if rec then
  964.         rec.distance = math.floor(rec.distance)
  965.         rec.label = rec.label or "Quarry!"
  966.         screen.rec = rec --Set the table
  967.         --Updating screen occurs outside of the if
  968.         local toSend
  969.         if queuedMessage then
  970.           toSend = queuedMessage
  971.           queuedMessage = nil
  972.         else
  973.           toSend = replyMessage
  974.         end
  975.         transmit(screen.send,screen.receive, toSend, screen.legacy) --Send reply message for turtle
  976.       end
  977.      
  978.     end
  979.    
  980.     screen:updateDisplay() --isDone is queried inside this
  981.     screen:reset(screen.theme.background)
  982.     screen:pushScreenUpdates() --Actually write things to screen
  983.    
  984.  
  985.   ----KEY HANDLING----
  986.   elseif event == "key" and keyMap[par1] then
  987.     local key = par1
  988.     if key ~= keys.enter then --If we aren't submitting a command
  989.       if key == keys.backspace then
  990.         if #commandString > 0 then
  991.           commandString = commandString:sub(1,-2)
  992.         end
  993.       else
  994.         commandString = commandString..keyMap[key]
  995.       end
  996.     else --If we are submitting a command
  997.       local args = {}
  998.       for a in commandString:gmatch("%S+") do
  999.         args[#args+1] = a:lower()
  1000.       end
  1001.       local command = args[1]
  1002.       if validCommands[command] then --If it is a valid command...
  1003.         local commandType = validCommands[command]
  1004.         if commandType == 1 or commandType == 3 then --If the command requires a "side" like transmitting commands, versus setting a default
  1005.           if defaultSide then table.insert(args, 2, defaultSide) end
  1006.           local screen = screenClass.sides[args[2]]
  1007.           if screen then --If the side exists
  1008.             if command == "command" and screen.send then --If sending command to the turtle
  1009.               queuedMessage = table.concat(args," ", 3) --Tells message handler to send appropriate message
  1010.               --transmit(screen.send, screen.receive, table.concat(args," ", 3), screen.legacy) --This transmits all text in the command with spaces. Duh this is handled when we get message
  1011.             end
  1012.  
  1013.             if command == "color" then
  1014.               screen.theme:addColor(args[3],colors[args[4] or ""],colors[args[5] or ""])
  1015.               updateAllScreens() --Because we are changing a theme color which others may have
  1016.             end
  1017.             if command == "theme" then
  1018.               screen:setTheme(args[3])
  1019.             end
  1020.             if command == "send" then --This changes a send channel, and can also revert to handshake
  1021.               local chan = checkChannel(tonumber(args[3]) or -1)
  1022.               if chan then screen.send = chan else screen.send = nil end
  1023.               screen:setSize() --If on handshake, resets screen
  1024.             end
  1025.             if command == "receive" and not screen.isStation then
  1026.               local chan = checkChannel(tonumber(args[3]) or -1)
  1027.               if chan and not screenClass.channels[chan] then
  1028.                 if screen.receive then --Computer may not have a channel yet
  1029.                   modem.close(screen.receive)
  1030.                   screenClass.channels[screen.receive] = nil
  1031.                 end
  1032.                 modem.open(chan)
  1033.                 screen.receive = chan
  1034.                 screenClass.channels[chan] = screen
  1035.                 screen:setSize() --Update broken status
  1036.               end
  1037.             end
  1038.            
  1039.             if command == "remove" and screen.side ~= "computer" then --We don't want to remove the main display!
  1040.               print()
  1041.               screen:remove()
  1042.             else --Because if removed it does stupid things
  1043.               screen:reset()
  1044.               screen:updateDisplay()
  1045.               screen:pushScreenUpdates()
  1046.             end
  1047.           end
  1048.         end
  1049.         if commandType == 2 or commandType == 3 then--Does not require a screen side
  1050.           if command == "screen" and peripheral.getType(args[2]) == "monitor" then  --Makes sure there is a monitor on the screen side
  1051.             if not args[3] or not screenClass.channels[tonumber(args[3])] then --Make sure the channel doesn't already exist
  1052.               local mon = screenClass.new(args[2], args[3], args[4])
  1053.                 --args[3] is the channel  and will set broken display if it doesn't exist
  1054.                 --args[4] is the theme, and will default if doesn't exists.
  1055.               mon:updateDisplay()
  1056.               mon:reset()
  1057.               mon:pushScreenUpdates()
  1058.             end
  1059.           end
  1060.           if command == "theme" then
  1061.             screenClass:setTheme(args[2], true) --Otherwise this would set base theme to nil, erroring
  1062.             updateAllScreens()
  1063.           end
  1064.           if command == "color" and themes[args[2]] then
  1065.             themes[args[2]]:addColor(args[3],colors[args[4]],colors[args[5]])
  1066.             updateAllScreens() --Because any screen could have this theme
  1067.           end
  1068.           if command == "side" then
  1069.             if screenClass.sides[args[2]] then
  1070.               defaultSide = args[2]
  1071.             else
  1072.               defaultSide = nil
  1073.             end
  1074.           end
  1075.           if command == "set" then
  1076.             if args[2] then
  1077.               defaultCommand = table.concat(args," ",2)
  1078.               defaultCommand = defaultCommand:upper()
  1079.             else
  1080.               defaultCommand = nil
  1081.             end
  1082.           end
  1083.           if command == "savetheme" then
  1084.             if saveTheme(themes[args[2]], args[3]) then
  1085.               computer:tryAddRaw(computer.dim[2]-1, "Save Theme Succeeded!", computer.theme.inverse, true)
  1086.             else
  1087.               computer:tryAddRaw(computer.dim[2]-1, "Save Theme Failed!", computer.theme.inverse, true)
  1088.             end
  1089.             computer:reset()
  1090.             computer:pushScreenUpdates()
  1091.             sleep(1)
  1092.           end
  1093.           if command == "auto" then
  1094.             autoDetect()
  1095.             updateAllScreens()
  1096.           end
  1097.           if command == "quit" or command == "exit" or command == "end" then
  1098.             continue = false
  1099.           end
  1100.         end
  1101.       else
  1102.         debug("\nInvalid Command")
  1103.       end
  1104.       if defaultCommand then commandString = defaultCommand.." " else commandString = "" end --Reset command string because it was sent
  1105.     end
  1106.    
  1107.    
  1108.     --Update computer display (computer is only one that displays command string
  1109.     computer:updateDisplay() --Note: Computer's method automatically adds commandString to last line
  1110.     if not continue then computer:tryAddRaw(computer.dim[2]-1,"Program Exiting", computer.theme.inverse, false, true, true) end
  1111.     computer:reset()
  1112.     computer:pushScreenUpdates()
  1113.    
  1114.   elseif event == "monitor_resize" then
  1115.     local screen = screenClass.sides[par1]
  1116.     if screen then
  1117.       screen:setSize()
  1118.       screen:updateDisplay()
  1119.       screen:reset()
  1120.       screen:pushScreenUpdates()
  1121.     end
  1122.   elseif event == "monitor_touch" then
  1123.     local screen = screenClass.sides[par1]
  1124.     debug("Side: ",par1," touched")
  1125.     if screen then --This part is copied from the "side" command
  1126.       if defaultSide ~= par1 then
  1127.         defaultSide = par1
  1128.       else
  1129.         defaultSide = nil
  1130.       end
  1131.     else
  1132.       debug("Adding Screen")
  1133.       local mon = screenClass.new(par1)
  1134.       mon:reset()
  1135.       mon:updateDisplay()
  1136.       mon:pushScreenUpdates()
  1137.      
  1138.     end
  1139.  
  1140.   elseif event == "peripheral_detach" then
  1141.     local screen = screenClass.sides[par1]
  1142.     if screen then
  1143.       screen:setSize()
  1144.     end
  1145.     --if screen then
  1146.     --  screen:remove()
  1147.     --end
  1148.    
  1149.   elseif event == "peripheral" then
  1150.     local screen = screenClass.sides[par1]
  1151.     if screen then
  1152.       screen:setSize()
  1153.     end
  1154.     --Maybe prompt to add a new screen? I don't know
  1155.  
  1156.   end
  1157.  
  1158.   local flag = false --Saying all screens are done, must disprove
  1159.   local count = 0 --We want it to wait if no screens have channels
  1160.   for a,b in pairs(screenClass.channels) do
  1161.     count = count + 1
  1162.     if not b.isDone then
  1163.       flag = true
  1164.     end
  1165.   end
  1166.   if continue and count > 0 then --If its not already false from something else
  1167.     continue = flag
  1168.   end
  1169.  
  1170.   if computer.isStation and event ~= "key" then --So screen is properly updated
  1171.     computer:reset()
  1172.     computer:updateDisplay()
  1173.     computer:pushScreenUpdates()
  1174.   end
  1175.  
  1176.  
  1177. end
  1178.  
  1179. sleep(1.5)
  1180. for a in pairs(screenClass.channels) do
  1181.   modem.close(a)
  1182. end
  1183. for a, b in pairs(screenClass.sides) do
  1184.   if not b.isDone then --Otherwise we want it display the ending stats
  1185.     b:setTextColor(colors.white)
  1186.     b:setBackgroundColor(colors.black)
  1187.     b.term.clear()
  1188.     b.term.setCursorPos(1,1)
  1189.   end
  1190. end
  1191.  
  1192. local text --Fun :D
  1193. if computer.isComputer then text = "SUPER COMPUTER OS 9000"
  1194. elseif computer.isTurtle then text = "SUPER DIAMOND-MINING OS XXX"
  1195. elseif computer.isPocket then text = "PoCkEt OOS AMAYZE 65"
  1196. end
  1197. if text and not computer.isDone then
  1198.   computer:say(text, computer.theme.title,1)
  1199. else
  1200.   computer.term.setCursorPos(1,computer.dim[2])
  1201.   computer.term.clearLine()
  1202. end
  1203. --Down here shut down all the channels, remove the saved file, other cleanup stuff
RAW Paste Data