dadragon84

QuarryReceiver

Feb 18th, 2025 (edited)
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 71.32 KB | Source Code | 0 0
  1. --Quarry Receiver Version 3.6.5
  2. --Made by MrJohnDowe
  3. --[[
  4. Recent Changes:
  5.   Fixed bugs with hardcoded keys for CC: Tweaked
  6. ]]
  7. if fs.exists("QuarryReceiver")then
  8.     shell.run("rename QuarryReceiver startup.lua")
  9. end
  10.  
  11. --Config
  12. local doDebug = false --For testing purposes
  13. local ySizes = 3 --There are 3 different Y Screen Sizes right now
  14. local quadEnabled = false --This is for the quadrotors mod by Lyqyd
  15. local autoRestart = true --If true, will reset screens instead of turning them off. For when reusing turtles.
  16.  
  17. --Initializing Program-Wide Variables
  18. local expectedMessage = "Dowe's Quarry" --Expected initial message
  19. local expectedFingerprint = "quarry"
  20. local replyMessage = "Turtle Quarry Receiver" --Message to respond to  handshake with
  21. local replyFingerprint = "quarryReceiver"
  22. local stopMessage = "stop"
  23. local expectedFingerprint = "quarry"
  24. local themeFolder = "quarryResources/receiverThemes/"
  25. local modemSide --User can specify a modem side, but it is not necessary
  26. local modem --This will be the table for the modem
  27. local computer --The main screen is special. It gets defined first :3
  28. local continue = true --This keeps the main while loop going
  29. local quadDirection = "north"
  30. local quadDirections = {n = "north", s = "south", e = "east", w = "west"}
  31. local quadBase, computerLocation
  32. local tArgs = {...}
  33. --These two are used by controller in main loop
  34. local commandString = "" --This will be a command string sent to turtle. This var is stored for display
  35. local lastCommand --If a command needs to be sent, this gets set
  36. local defaultSide
  37. local defaultCommand
  38. local stationsList = {}
  39.  
  40. for i=1, #tArgs do --Parameters that must be set before rest of program for proper debugging
  41.   local val = tArgs[i]:lower()
  42.   if val == "-v" or val == "-verbose" then
  43.     doDebug = true
  44.   end
  45.   if val == "-q" or val == "-quiet" then
  46.     doDebug = false
  47.   end
  48. end
  49.  
  50. local keyMap = {[keys.space] = " ", [keys.minus] = "_", [keys.period] = ".", [keys.numPadDecimal] = "."} --This is for command string
  51. keyMap[keys.numPad0] = "0"
  52. keyMap[keys.numPad1] = "1"
  53. keyMap[keys.numPad2] = "2"
  54. keyMap[keys.numPad3] = "3"
  55. keyMap[keys.numPad4] = "4"
  56. keyMap[keys.numPad5] = "5"
  57. keyMap[keys.numPad6] = "6"
  58. keyMap[keys.numPad7] = "7"
  59. keyMap[keys.numPad8] = "8"
  60. keyMap[keys.numPad9] = "9"
  61.  
  62. keyMap[keys.zero] = "0"
  63. keyMap[keys.one] = "1"
  64. keyMap[keys.two] = "2"
  65. keyMap[keys.three] = "3"
  66. keyMap[keys.four] = "4"
  67. keyMap[keys.five] = "5"
  68. keyMap[keys.six] = "6"
  69. keyMap[keys.seven] = "7"
  70. keyMap[keys.eight] = "8"
  71. keyMap[keys.nine] = "9"
  72.  
  73. for a,b in pairs(keys) do --Add all letters from keys api
  74.   if #a == 1 then
  75.     keyMap[b] = a:upper()
  76.   end
  77. end
  78. keyMap[keys.enter] = "enter"
  79. keyMap[keys.numPadEnter] = "enter"
  80. keyMap[keys.backspace] = "backspace"
  81. keyMap[keys.up] = "up"
  82. keyMap[keys.down] = "down"
  83. keyMap[keys.left] = "left"
  84. keyMap[keys.right] = "right"
  85.  
  86. local helpResources = { --$$ is a new page
  87. main = [[$$Hello and welcome to Quarry Receiver Help!
  88.  
  89. This goes over everything there is to know about the receiver
  90.  
  91. Use the arrow keys to navigate!
  92. Press '0' to come back here!
  93. Press 'q' to quit!
  94.  
  95. Press a section number at any time to go the beginning of that section
  96.  1. Basic Use
  97.  2. Parameters
  98.  3. Commands
  99.  4. Turtle Commands
  100.  
  101. $$A secret page!
  102. You found it! Good job :)
  103. ]],
  104. [[$$Your turtle and you!
  105.  
  106. To use this program, you need a wireless modem on both this computer and the turtle
  107.  
  108. Make sure they are attached to both the turtle and this computer
  109. $$Using your new program!
  110.  
  111. Once you have done that, start the turtle and when it says "Rednet?", say "Yes"
  112.   Optionally, you can use the parameter "-rednet true"
  113. Then remember the channel it tells you to open.
  114.  
  115. Come back to this computer, and run the program. Follow onscreen directions.
  116.   Optionally, you can use the parameter "-receiveChannel"
  117.  
  118. Check out the other help sections for more parameters
  119. $$Adding Screens!
  120. You can add screens with the "-screen" parameter or the "SCREEN" command
  121. An example would be "SCREEN LEFT 2 BLUE" for a screen on the left side with channel 2 and blue theme
  122.  
  123. You can connect screens over wired modems. Attach a modem to the computer and screen, then right click the modem attached to the screen.
  124. Then you can say "SCREEN MONITOR_0" or whatever it says
  125.  
  126. ]],
  127. [[$$Parameters!
  128.   note: <> means required, [] means optional
  129.  
  130. -help/help/-?/?/-usage/usage: That's this!
  131.  
  132. -autoRestart [t/f]: If true, the receiver will not exit when all quarries are done and will automagically reconnect to new quarries
  133.  With no argument, this is set to true.
  134.  
  135. -receiveChannel/channel <channel>: Sets the main screen's receive channel
  136.  
  137. -theme <name>: sets the "default" theme that screens use when they don't have a set theme
  138. $$Parameters!
  139.  note: <> means required, [] means optional
  140.  
  141. -screen <side> [channel] [theme]: makes a new screen on the given side with channel and theme
  142.  example: -screen left 10 blue    This adds a new screen on the left receiving channel 10 with a blue theme.
  143.  
  144. -station [side]: makes the screen a "station" that monitors all screens.
  145.  if no side, uses computer
  146.  
  147. -auto [channel list]: This finds all attached monitors and initializes them (with channels)
  148.  example: -auto 1 2 5 9   finds screens and gives them channels
  149. $$Parameters!
  150.  note: <> means required, [] means optional
  151.  
  152. -colorEditor: makes the main screen a color editor that just prints the current colors. Good for theme making
  153. current typeColors: default title, subtitle, pos, dim, extra, error, info, inverse, command, help, background
  154.  
  155. -modem <side>: Sets the modem side to side
  156.  
  157. -v/-verbose: turns on debug
  158.  
  159. -q/-quiet: turns off debug
  160. ]],
  161. [[$$Commands!
  162.  
  163. COMMAND [screen] [text]: Sends text to the connected turtle. See turtle commands for valid commands
  164.  
  165. SCREEN [side] [channel] [theme]: Adds a screen. You can also specify the channel and theme of the screen.
  166.  
  167. REMOVE [screen]: Removes the selected screen (cannot remove the main screen)
  168.  
  169. THEME [screen] [name]: Sets the theme of the given screen. THEME [screen] resets the screen to default theme
  170. $$Commands!
  171.  
  172. THEME [name]: Sets the default theme.
  173.  
  174. RECEIVE [screen] [channel]: Changes the receive channel of the given screen
  175.  
  176. SEND [screen] [channel]: Changes the send channel of the given screen (for whatever reason)
  177.  
  178. STATION [screen] [channel]: Sets the given screen to/from a station. If changing from a station, will set the screen's channel
  179. $$Commands!
  180.  
  181. SET [text]: Sets a default command that can be backspaced. Useful for color editing or command sending
  182.   Use SET with nothing after to remove text
  183.  
  184. SIDE [screen]: Sets a default screen for "sided" commands.
  185.   Any command that takes a [screen] is sided
  186.  
  187. EXIT/QUIT: Quits the program gracefully
  188. $$Commands!
  189.  
  190. COLOR [themeName] [typeColor] [textColor] [backColor]: Sets the the text and background colors of the given typeColor of the given theme. See notes on "colorEditor" parameter for more info
  191.  
  192. SAVETHEME [themeName] [fileName]: Saves the given theme as fileName for later use
  193.  
  194. SAVETHEME [screen] [fileName]: Same as above but for a screen's theme
  195.  
  196. AUTO [channelList]: Automatically searches for nearby screens, providing them sequentially with channels if a channel list is given
  197.  Example Use: AUTO 1 2 5 9
  198. $$Commands!
  199.  
  200. HELP: Displays this again!
  201.  
  202. VERBOSE: Turns debug on
  203.  
  204. QUIET: Turns debug off
  205.  
  206. ]],
  207. [[$$Turtle Commands!
  208.  
  209. Stop: Stops the turtle where it is
  210.  
  211. Return: The turtle will return to its starting point, drop off its load, and stop
  212.  
  213. Drop: Turtle will immediately go and drop its inventory
  214.  
  215. Pause: Pauses the turtle
  216.  
  217. Resume: Resumes paused turtles
  218.  
  219. Refuel: Turtle will schedule an emergency refuel
  220.  This could take from fuelChest, or quadCopter
  221.  or fuel in inventory (in that order)
  222. ]]
  223. }
  224.  
  225. --Generic Functions--
  226. local function debug(...)
  227.  --if doDebug then return print(...) end --Basic
  228.  if doDebug then
  229.    print("\nDEBUG: ",...)
  230.    os.pullEvent("char")
  231.  end
  232. end
  233. local function clearScreen(x,y, periph)
  234.  periph, x, y = periph or term, x or 1, y or 1
  235.  periph.clear()
  236.  periph.setCursorPos(x,y)
  237. end
  238.  
  239. local function swapKeyValue(tab)
  240.  for a,b in pairs(tab) do
  241.    tab[b] = a
  242.  end
  243.  return tab
  244. end
  245. local function copyTable(tab)
  246.  local toRet = {}
  247.  for a,b in pairs(tab) do
  248.    toRet[a] = b
  249.  end
  250.  return toRet
  251. end
  252. local function checkChannel(num)
  253.  num = tonumber(num)
  254.  if not num then return false end
  255.  if 1 <= num and num <= 65535 then
  256.    return num
  257.  end
  258.  return false
  259. end
  260. local function truncate(text, xDim)
  261.  if #text <= xDim then return text end
  262.  return #text >= 4 and text:sub(1,xDim-3).."..." or text:sub(1,3)
  263. end
  264. local function align(text, xDim, direction, trunc)
  265.  text = tostring(text or "None")
  266.  if trunc == nil then trunc = true end
  267.  if #text >= xDim and trunc then return truncate(text,xDim) end
  268.  for i=1, xDim-#text do
  269.    if direction == "right" then
  270.      text = " "..text
  271.    elseif direction == "left" then
  272.      text = text.." "
  273.    end
  274.  end
  275.  return text
  276. end
  277. local function alignR(text, xDim, trunc)
  278.  return align(text, xDim, "right", trunc)
  279. end
  280. local function alignL(text, xDim, trunc)
  281.  return align(text, xDim, "left", trunc)
  282. end
  283. local function center(text, xDim, char)
  284.  if not xDim then error("Center: No dim given",2) end
  285.  char = char or " "
  286.  local a = (xDim-#text)/2
  287.  for i=1, a do
  288.    text = char..text..char
  289.  end
  290.  return #text == xDim and text or text..char --If not full length, add a space
  291. end
  292. local function leftRight(first, second, dim)
  293.  return alignL(tostring(first),dim-#tostring(second))..tostring(second)
  294. end
  295. local function roundNegative(num) --Rounds numbers up to 0
  296.  if num >= 0 then return num else return 0 end
  297. end
  298.  
  299.  
  300. local function testPeripheral(periph, periphFunc)
  301.  if type(periph) ~= "table" then return false end
  302.  if type(periph[periphFunc]) ~= "function" then return false end
  303.  if periph[periphFunc]() == nil then --Expects string because the function could access nil
  304.    return false
  305.  end
  306.  return true
  307. end
  308.  
  309. local function initModem() --Sets up modem, returns true if modem exists
  310.  if not testPeripheral(modem, "isWireless") then
  311.    if modemSide then
  312.      if peripheral.getType(modemSide) == "modem" then
  313.        modem = peripheral.wrap(modemSide)    
  314.        if modem.isWireless and not modem.isWireless() then --Apparently this is a thing
  315.          modem = nil
  316.          return false
  317.        end
  318.        return true
  319.      end
  320.    end
  321.    if peripheral.find then
  322.      modem = peripheral.find("modem", function(side, obj) return obj.isWireless() end)
  323.    end
  324.    return modem and true or false
  325.  end
  326.  return true
  327. end
  328.  
  329. --COLOR/THEME RELATED
  330. for a, b in pairs(colors) do --This is so commands color commands can be entered in one case
  331.  colors[a:lower()] = b
  332. end
  333. colors.none = 0 --For adding things
  334.  
  335. local requiredColors = {"default","title", "subtitle", "pos", "dim", "extra", "error", "info", "inverse", "command", "help", "background"}
  336.  
  337. local function checkColor(name, text, back) --Checks if a given color works
  338.  local flag = false
  339.  for a, b in ipairs(requiredColors) do
  340.    if b == name then
  341.      flag = true
  342.      break
  343.    end
  344.  end
  345.  if not flag or not (tonumber(text) or colors[text]) or not (tonumber(back) or colors[back]) then return false end
  346.  return true
  347. end
  348.  
  349.  
  350. local themes = {} --Loaded themes, gives each one a names
  351. local function newTheme(name)
  352.  name = name:lower() or "none"
  353.  local self = {name = name}
  354.  self.addColor = function(self, colorName, text, back) --Colors are optional. Will default to "default" value. Make sure default is a color
  355.    if colorName == "default" and (not text or not back) then return self end
  356.    if not text then text = 0 end
  357.    if not back then back = 0 end
  358.    if not checkColor(colorName, text, back) then debug("Color check failed: ",name," ",text," ",back); return self end --Back or black because optional
  359.    colorName = colorName or "none"
  360.    self[colorName] = {text = text, background = back}
  361.    return self --Allows for chaining :)
  362.  end
  363.  themes[name] = self
  364.  return self
  365. end
  366.  
  367. local function parseTheme(file)
  368.  local addedTheme = newTheme(file:match("^.-\n") or "newTheme") --Initializes the new theme to the first line
  369.  file:sub(file:find("\n") or 1) --If there is a newLine, this cuts everything before it. I don't care that the newLine is kept
  370.   for line in file:gmatch("[^\n]+\n") do --Go through all the color lines (besides first one)
  371.     local args = {}
  372.     for word in line:gmatch("%S+") do
  373.       table.insert(args,word)
  374.     end
  375.     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
  376.   end
  377.   local flag = true --Make sure a theme has all required elements
  378.   for a,b in ipairs(requiredColors) do
  379.     if not addedTheme[b] then
  380.       flag = false
  381.       debug("Theme is missing color '",b,"'")
  382.     end
  383.   end
  384.   if not flag then
  385.     themes[addedTheme.name] = nil
  386.     debug("Failed to load theme")
  387.     return false
  388.   end
  389.   return addedTheme
  390. end
  391. --This is how adding colors will work
  392. --regex for adding from file:
  393. --(\w+) (\w+) (\w+)
  394. --  \:addColor\(\"\1\"\, \2\, \3\)
  395.  
  396.  
  397. newTheme("default")
  398.   :addColor("default",colors.white, colors.black)
  399.   :addColor("title", colors.green, colors.gray)
  400.   :addColor("subtitle", colors.white, colors.black)
  401.   :addColor("pos", colors.green, colors.black)
  402.   :addColor("dim", colors.lightBlue, colors.black)
  403.   :addColor("extra", colors.lightGray, colors.black)
  404.   :addColor("error", colors.red, colors.white)
  405.   :addColor("info", colors.blue, colors.lightGray)
  406.   :addColor("inverse", colors.yellow, colors.blue)
  407.   :addColor("command", colors.lightBlue, colors.black)
  408.   :addColor("help", colors.cyan, colors.black)
  409.   :addColor("background", colors.none, colors.none)
  410.  
  411. newTheme("blue")
  412.   :addColor("default",colors.white, colors.blue)
  413.   :addColor("title", colors.lightBlue, colors.gray)
  414.   :addColor("subtitle", 1, 2048)
  415.   :addColor("pos", 16, 2048)
  416.   :addColor("dim", colors.lime, 0)
  417.   :addColor("extra", 8, 2048)
  418.   :addColor("error",  8, 16384)
  419.   :addColor("info", 2048, 256)
  420.   :addColor("inverse", 2048, 1)
  421.   :addColor("command", 2048, 8)
  422.   :addColor("help", 16384, 1)
  423.   :addColor("background", 1, 2048)
  424.  
  425. newTheme("seagle")
  426.   :addColor("default",colors.white, colors.black)
  427.   :addColor("title", colors.white, colors.black)
  428.   :addColor("subtitle", colors.red, colors.black)
  429.   :addColor("pos", colors.gray, colors.black)
  430.   :addColor("dim", colors.lightBlue, colors.black)
  431.   :addColor("extra", colors.lightGray, colors.black)
  432.   :addColor("error", colors.red, colors.white)
  433.   :addColor("info", colors.blue, colors.lightGray)
  434.   :addColor("inverse", colors.yellow, colors.lightGray)
  435.   :addColor("command", colors.lightBlue, colors.black)
  436.   :addColor("help", colors.red, colors.white)
  437.   :addColor("background", colors.white, colors.black)
  438.  
  439. newTheme("random")
  440.   :addColor("default",colors.white, colors.black)
  441.   :addColor("title", colors.pink, colors.blue)
  442.   :addColor("subtitle", colors.black, colors.white)
  443.   :addColor("pos", colors.green, colors.black)
  444.   :addColor("dim", colors.lightBlue, colors.black)
  445.   :addColor("extra", colors.lightGray, colors.lightBlue)
  446.   :addColor("error", colors.white, colors.yellow)
  447.   :addColor("info", colors.blue, colors.lightGray)
  448.   :addColor("inverse", colors.yellow, colors.lightGray)
  449.   :addColor("command", colors.green, colors.lightGray)
  450.   :addColor("help", colors.white, colors.yellow)
  451.   :addColor("background", colors.white, colors.red)
  452.  
  453. newTheme("rainbow")
  454.   :addColor("dim", 32, 0)
  455.   :addColor("background", 16384, 0)
  456.   :addColor("extra", 2048, 0)
  457.   :addColor("info", 2048, 0)
  458.   :addColor("inverse", 32, 0)
  459.   :addColor("subtitle", 2, 0)
  460.   :addColor("title", 16384, 0)
  461.   :addColor("error", 1024, 0)
  462.   :addColor("default", 1, 512)
  463.   :addColor("command", 16, 0)
  464.   :addColor("pos", 16, 0)
  465.   :addColor("help", 2, 0)
  466.  
  467. newTheme("green")
  468.  :addColor("dim", 16384, 0)
  469.  :addColor("background", 0, 0)
  470.  :addColor("extra", 2048, 0)
  471.  :addColor("info", 32, 256)
  472.  :addColor("inverse", 8192, 1)
  473.  :addColor("subtitle", 1, 0)
  474.  :addColor("title", 8192, 128)
  475.  :addColor("error", 16384, 32768)
  476.  :addColor("default", 1, 8192)
  477.  :addColor("command", 2048, 32)
  478.  :addColor("pos", 16, 0)
  479.  :addColor("help", 512, 32768)
  480.  
  481.  
  482. --If you modify a theme a bunch and want to save it
  483. local function saveTheme(theme, fileName)
  484.   if not theme or not type(fileName) == "string" then return false end
  485.   local file = fs.open(fileName,"w")
  486.   if not file then return false end
  487.   file.writeLine(fileName)
  488.   for a,b in pairs(theme) do
  489.     if type(b) == "table" then --If it contains color objects
  490.       file.writeLine(a.." "..tostring(b.text).." "..tostring(b.background))
  491.     end
  492.   end
  493.   file.close()
  494.   return true
  495. end
  496.  
  497. --BUTTON CLASS
  498. local button = {}
  499.  
  500. button.checkPoint = function(buttons, pos) --Returns a command or nil
  501.   for a, b in pairs(buttons) do
  502.     if pos[2] == b.line then
  503.       if pos[1] >= b.xDim[1] and pos[1] <= b.xDim[2] then
  504.         return b.command
  505.       end
  506.     end
  507.   end
  508. end
  509.  
  510. button.makeLine = function(buttons, sep, xDim)
  511.   local toRet = ""
  512.   for a, b in ipairs(buttons) do
  513.     toRet = toRet..center(b.text, (b.xDim[2]-b.xDim[1]))..sep
  514.   end
  515.   return toRet:sub(1,-2).."" --Take off the last sep
  516. end
  517.  
  518. button.new = function(line, xStart, xEnd, command, display)
  519.   local toRet = {}
  520.   setmetatable(toRet, {__index = button})
  521.   toRet.line = line
  522.   toRet.xDim = {math.min(xStart, xEnd), math.max(xStart, xEnd)}
  523.   toRet.command = command
  524.   toRet.text = display
  525.   return toRet
  526. end
  527.  
  528.  
  529. --==SCREEN CLASS FUNCTIONS==
  530. local screenClass = {} --This is the class for all monitor/screen objects
  531. screenClass.screens = {} --A simply numbered list of screens
  532. screenClass.sides = {} --A mapping of screens by their side attached
  533. screenClass.channels = {} --A mapping of receiving channels that have screens attached. Used for the receiver part
  534. screenClass.sizes = {{7,18,29,39,50}, {5,12,19} , computer = {51, 19}, turtle = {39,13}, pocket = {26,20}}
  535.  
  536. screenClass.setTextColor = function(self, color) --Accepts raw color
  537.   if color and self.term.isColor() then
  538.     self.textColor = color
  539.     self.term.setTextColor(color)
  540.     return true
  541.   end
  542.   return false
  543. end
  544. screenClass.setBackgroundColor = function(self, color) --Accepts raw color
  545.   if color and self.term.isColor() then
  546.     self.backgroundColor = color
  547.     self.term.setBackgroundColor(color)
  548.     return true
  549.   end
  550.   return false
  551. end
  552. screenClass.setColor = function(self, color) --Wrapper, accepts themecolor objects
  553.   if type(color) ~= "table" then error("Set color received a non-table",2) end
  554.   local text, back = color.text, color.background
  555.   if not text or text == 0 then text = self.theme.default.text end
  556.   if not back or back == 0  then back = self.theme.default.background end
  557.   return self:setTextColor(text) and self:setBackgroundColor(back)
  558. end
  559.  
  560. screenClass.themeName = "default" --Setting super for fallback
  561. screenClass.theme = themes.default
  562.  
  563. screenClass.rec = { --Initial values for all displayed numbers
  564.   label = "Quarry Bot",
  565.   id = 1,
  566.   percent = 0,
  567.   xPos = 0,
  568.   zPos = 0,
  569.   layersDone = 0,
  570.   x = 0,
  571.   z = 0,
  572.   layers = 0,
  573.   openSlots = 0,
  574.   mined = 0,
  575.   moved = 0,
  576.   chestFull = false,
  577.   isAtChest = false,
  578.   isGoingToNextLayer = false,
  579.   foundBedrock = false,
  580.   fuel = 0,
  581.   volume = 0,
  582.   distance = 0,
  583.   yPos = 0
  584. }
  585.  
  586. screenClass.new = function(side, receive, themeFile)
  587.   local self = {}
  588.   setmetatable(self, {__index = screenClass}) --Establish Hierarchy
  589.   self.side = side
  590.   if side == "computer" then
  591.     self.term = term
  592.   else
  593.     self.term = peripheral.wrap(side)
  594.     if not (self.term and peripheral.getType(side) == "monitor") then --Don't create an object if it doesn't exist
  595.       if doDebug then
  596.         error("No monitor on side "..tostring(side))
  597.       end
  598.       self = nil --Save memory?
  599.       return false
  600.     end
  601.   end
  602.  
  603.   --Channels and ids
  604.   self.receive = tonumber(receive) --Receive Channel
  605.   self.send = nil --Reply Channel, obtained in handshake
  606.   self.id = #screenClass.screens+1
  607.   --Colors
  608.   self.themeName = nil --Will be set by setTheme
  609.   self.theme = nil
  610.   self.isColor = self.term.isColor() --Just for convenience
  611.   --Other Screen Properties
  612.   self.dim = {self.term.getSize()} --Raw dimensions
  613.   --Initializations
  614.   self.isDone = false --Flag for when the turtle is done transmitting
  615.   self.size = {} --Screen Size, assigned in setSize
  616.   self.textColor = colors.white --Just placeholders until theme is loaded and run
  617.   self.backColor = colors.black
  618.   self.toPrint = {}
  619.   self.isComputer = false
  620.   self.isTurtle = false
  621.   self.isPocket = false
  622.   self.acceptsInput = false
  623.   self.legacy = false --Whether it expects tables or strings
  624.   self.rec = copyTable(screenClass.rec)
  625.  
  626.   screenClass.screens[self.id] = self
  627.   screenClass.sides[self.side] = self
  628.   if self.receive then
  629.     modem.open(self.receive) --Modem should be defined by the time anything is open
  630.     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
  631.   end
  632.   self:setSize() --Finish Initialization
  633.   self:setTheme(themeFile)
  634.   return self
  635. end
  636.  
  637. screenClass.remove = function(tab) --Cleanup function
  638.   if type(tab) == "number" then --Expects table, can take id (for no apparent reason)
  639.     tab = screenClass.screens[tab]
  640.   end
  641.   tab:removeStation()
  642.   if tab.side == "REMOVED" then return end
  643.   if tab.side == "computer" then error("Tried removing computer screen",2) end --This should never happen
  644.   tab:reset() --Clear screen
  645.   tab:say("Removed", tab.theme.info, 1) --Let everyone know whats up
  646.   screenClass.screens[tab.id] = {side = "REMOVED"} --Not nil because screw up len()
  647.   screenClass.sides[tab.side] = nil
  648.   tab:removeChannel()
  649. end
  650.  
  651. --Init Functions
  652. screenClass.removeChannel = function(self)
  653.   self.send = nil
  654.   if self.receive then
  655.     screenClass.channels[self.receive] = nil
  656.     if modem and modem.isOpen(self.receive) then
  657.       modem.close(self.receive)
  658.     end
  659.     self.receive = nil
  660.   end
  661.   self:setSize()
  662. end
  663.  
  664. screenClass.setChannel = function(self, channel)
  665.   if self.isStation then return false end --Don't want to set channel station
  666.   self:removeChannel()
  667.   if type(channel) == "number" then
  668.     self.receive = channel
  669.     screenClass.channels[self.receive] = self
  670.     if modem and not modem.isOpen(channel) then modem.open(channel) end
  671.   end
  672.   self:setSize() --Sets proper draw function
  673. end
  674.  
  675. screenClass.setStation = function(self) --Note: This only changes the "set" methods so that "update" methods remain intact per object :)
  676.   self:removeChannel()
  677.   if not self.isStation then --Just in case this gets called more than once
  678.     self.isStation = true
  679.     table.insert(stationsList,self)
  680.   end
  681.   self:setSize()
  682. end
  683.  
  684. screenClass.removeStation = function(self)
  685.   if self.isStation then
  686.     for i=1, #stationsList do --No IDs so have to do a linear traversal
  687.       if stationsList[i] == self then table.remove(stationsList, i) end
  688.     end
  689.   end
  690.   self.isStation = false
  691.   self:setSize()
  692. end
  693.  
  694. screenClass.setSize = function(self) --Sets screen size
  695.   if self.side ~= "computer" and not self.term then self.term = peripheral.wrap(self.side) end
  696.   if not self.term.getSize() then --If peripheral is having problems/not there. Don't go further than term, otherwise index nil (maybe?)
  697.     debug("There is no term...")
  698.     self.updateDisplay = function() end --Do nothing on screen update, overrides class
  699.     return true
  700.   elseif self.isStation then
  701.     self:setStationDisplay()
  702.   elseif not self.receive then
  703.     self:setBrokenDisplay() --This will prompt user to set channel
  704.   elseif self.send then --This allows for class inheritance
  705.     self:setNormalDisplay() --In case objects have special updateDisplay methods --Remove function in case it exists, defaults to super
  706.   else --If the screen needs to have a handshake display
  707.     self:setHandshakeDisplay()
  708.   end
  709.   self:resetButtons()
  710.   self.dim = { self.term.getSize()}
  711.   local tab = screenClass.sizes
  712.   for a=1, 2 do --Want x and y dim
  713.     for b=1, #tab[a] do --Go through all normal sizes, x and y individually
  714.       if tab[a][b] <= self.dim[a] then --This will set size higher until false
  715.         self.size[a] = b
  716.       end
  717.     end
  718.   end
  719.   local function isThing(toCheck, thing) --E.G. isThing(self.dim,"computer")
  720.     return toCheck[1] == tab[thing][1] and toCheck[2] == tab[thing][2]
  721.   end
  722.   self.isComputer = isThing(self.dim, "computer")
  723.   self.isTurtle = isThing(self.dim, "turtle")
  724.   self.isPocket = isThing(self.dim, "pocket")
  725.   self.acceptsInput = self.isComputer or self.isTurtle or self.isPocket
  726.   return self
  727. end
  728.  
  729. screenClass.setTheme = function(self, themeName, stopReset)
  730.   if not themes[themeName] then --If we don't have it already, try to load it
  731.     local fileName = themeName or ".." --.. returns false and I don't think you can name a file this
  732.     if fs.exists(themeFolder) then fileName = themeFolder..fileName end
  733.     if fs.exists(fileName) then
  734.       debug("Loading theme: ",fileName)
  735.       local file = fs.open(fileName, "r")
  736.       if not file then debug("Could not load theme '",themeName,"' file not found") end
  737.       parseTheme(file.readAll()) --Parses the text to make a theme, returns theme
  738.       file.close()
  739.       self.themeName = themeName:lower() --We can now set our themeName to the fileName
  740.     else
  741.       --Resets theme to super
  742.       if not stopReset then --This exists so its possible to set default theme without breaking world
  743.         self.themeName = nil
  744.         self.theme = nil
  745.       end
  746.       return false
  747.     end
  748.    else
  749.     self.themeName = themeName:lower()
  750.    end
  751.    self.theme = themes[self.themeName] --Now the theme is loaded or the function doesn't get here
  752.    return true
  753. end
  754.  
  755. --Adds text to the screen buffer
  756. screenClass.tryAddRaw = function(self, line, text, color, ...) --This will try to add text if Y dimension is a certain size
  757.   local doAdd = {...} --booleans for small, medium, and large
  758.   if type(text) ~= "string" then error("tryAddRaw got "..type(text)..", expected string",2) end
  759.   if not text then
  760.     debug("tryAddRaw got no string on line ",line)
  761.     return false
  762.   end
  763.   if type(color) ~= "table" then error("tryAddRaw did not get a color",2) end
  764.   --color = color or {text = colors.white}
  765.   for i=1, ySizes do --As of now there are 3 Y sizes
  766.     local test = doAdd[i]
  767.     if test == nil then test = doAdd[#doAdd] end --Set it to the last known setting if doesn't exist
  768.     if test and self.size[2] == i then --If should add this text for this screen size and the monitor is this size
  769.       if #text <= self.dim[1] then
  770.         self.toPrint[line] = {text = text, color = color}
  771.         return true
  772.       else
  773.         debug("Tried adding '",text,"' on line ",line," but was too long: ",#text," vs ",self.dim[1])
  774.       end
  775.     end
  776.   end
  777.   return false
  778. end
  779. screenClass.tryAdd = function(self, text, color,...) --Just a wrapper
  780.   return self:tryAddRaw(#self.toPrint+1, text, color, ...)
  781. end
  782. screenClass.tryAddC = function(self, text, color, ...) --Centered text
  783.   return self:tryAdd(center(text, self.dim[1]), color, ...)
  784. end
  785.  
  786. screenClass.reset = function(self,color)
  787.   color = color or self.theme.background
  788.   self:setColor(color)
  789.   self.term.clear()
  790.   self.term.setCursorPos(1,1)
  791. end
  792. screenClass.say = function(self, text, color, line)
  793.   local currColor = self.backgroundColor
  794.   color = color or debug("Printing ",text," but had no themeColor: ",self.theme.name) or {} --Set default for nice error, alert that errors occur
  795.   self:setColor(color)
  796.   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
  797.   if doDebug and #text > self.dim[1] then error("Tried printing: '"..text.."', but was too big") end
  798.   self.term.setCursorPos(1,line)
  799.   for i=1, self.dim[1]-#text do --This is so the whole line's background gets filled.
  800.     text = text.." "
  801.   end
  802.   self.term.write(text)
  803.   self.term.setCursorPos(1, line+1)
  804. end
  805. screenClass.pushScreenUpdates = function(self)
  806.   for i=1, self.dim[2] do
  807.     local tab = self.toPrint[i]
  808.     if tab then
  809.       self:say(tab.text, tab.color, i)
  810.     end
  811.   end
  812.   self.term.setCursorPos(1,self.dim[2]) --So we can see errors
  813. end
  814. screenClass.resetButtons = function(self)
  815.   self.buttons = {}
  816. end
  817. screenClass.addButton = function(self, button)
  818.   self.buttons[#self.buttons+1] = button
  819. end
  820.  
  821. screenClass.updateNormal = function(self) --This is the normal updateDisplay function
  822.   local str = tostring
  823.   self.toPrint = {} --Reset table
  824.   local message, theme, x = self.rec, self.theme, self.dim[1]
  825.   if not self.isDone then --Normally
  826.    
  827.      
  828.     if self.size[1] == 1 then --Small Width Monitor
  829.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  830.         self:tryAdd("Quarry!", theme.title, false, false, true)
  831.       end
  832.      
  833.       self:tryAdd("-Fuel-", theme.subtitle , false, true, true)
  834.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  835.         self:tryAdd("A lot", theme.extra, false, true, true)
  836.       end
  837.      
  838.       self:tryAdd("--%%%--", theme.subtitle, false, true, true)
  839.       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
  840.       self:tryAdd(center(str(message.percent).."%", x), theme.pos, true, false) --I want it to be centered on 1x1
  841.      
  842.       self:tryAdd("--Pos--", theme.subtitle, false, true, true)
  843.       self:tryAdd("X:"..alignR(str(message.xPos), 5), theme.pos, true)
  844.       self:tryAdd("Z:"..alignR(str(message.zPos), 5), theme.pos , true)
  845.       self:tryAdd("Y:"..alignR(str(message.layersDone), 5), theme.pos , true)
  846.      
  847.       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
  848.         self:tryAdd(str(message.x).."x"..str(message.z), theme.dim , true, false)
  849.       end
  850.       self:tryAdd("--Dim--", theme.subtitle, false, true, true)
  851.       self:tryAdd("X:"..alignR(str(message.x), 5), theme.dim, false, true, true)
  852.       self:tryAdd("Z:"..alignR(str(message.z), 5), theme.dim, false, true, true)
  853.       self:tryAdd("Y:"..alignR(str(message.layers), 5), theme.dim, false, true, true)
  854.      
  855.       self:tryAdd("-Extra-", theme.subtitle, false, false, true)
  856.       self:tryAdd(alignR(textutils.formatTime(os.time()):gsub(" ","").."", 7), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  857.       self:tryAdd("Used:"..alignR(str(16-message.openSlots),2), theme.extra, false, false, true)
  858.       self:tryAdd("Dug"..alignR(str(message.mined), 4), theme.extra, false, false, true)
  859.       self:tryAdd("Mvd"..alignR(str(message.moved), 4), theme.extra, false, false, true)
  860.       if message.status then
  861.         self:tryAdd(alignL(message.status, x), theme.info, false, false, true)
  862.       end
  863.       if message.chestFull then
  864.         self:tryAdd("ChstFll", theme.error, false, false, true)
  865.       end
  866.      
  867.     end
  868.     if self.size[1] == 2 then --Medium Monitor
  869.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  870.         self:tryAdd("Quarry!", theme.title, false, false, true)
  871.       end
  872.      
  873.       self:tryAdd(center("Fuel",x,"-"), theme.subtitle , false, true, true)
  874.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  875.         self.toPrint[#self.toPrint] = nil
  876.         self:tryAdd("A lot", theme.extra, false, true, true)
  877.       end
  878.      
  879.       self:tryAdd(str(message.percent).."% Complete", theme.pos , true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
  880.      
  881.       self:tryAdd(center("Pos",x,"-"), theme.subtitle, false, true, true)
  882.       self:tryAdd(leftRight("X Coordinate:",message.xPos, x), theme.pos, true)
  883.       self:tryAdd(leftRight("Z Coordinate:",message.zPos, x), theme.pos , true)
  884.       self:tryAdd(leftRight("On Layer:",message.layersDone, x), theme.pos , true)
  885.      
  886.       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
  887.         self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true, false)
  888.       end
  889.       self:tryAdd(center("Dim",x,"-"), theme.subtitle, false, true, true)
  890.       self:tryAdd(leftRight("Total X:", message.x, x), theme.dim, false, true, true)
  891.       self:tryAdd(leftRight("Total Z:", message.z, x), theme.dim, false, true, true)
  892.       self:tryAdd(leftRight("Total Layers:", message.layers, x), theme.dim, false, true, true)
  893.       self:tryAdd(leftRight("Volume", message.volume, x), theme.dim, false, false, true)
  894.      
  895.       self:tryAdd(center("Extras",x,"-"), theme.subtitle, false, false, true)
  896.       self:tryAdd(leftRight("Time: ", textutils.formatTime(os.time()):gsub(" ","").."", x), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  897.       self:tryAdd(leftRight("Used Slots:", 16-message.openSlots, x), theme.extra, false, false, true)
  898.       self:tryAdd(leftRight("Blocks Mined:", message.mined, x), theme.extra, false, false, true)
  899.       self:tryAdd(leftRight("Spaces Moved:", message.moved, x), theme.extra, false, false, not self.isPocket)
  900.       if message.status then
  901.         self:tryAdd(message.status, theme.info, false, false, true)
  902.       end
  903.       if message.chestFull then
  904.         self:tryAdd("Chest Full, Fix It", theme.error, false, true, true)
  905.       end
  906.     end
  907.     if self.size[1] >= 3 then --Large or larger screens
  908.       if not self:tryAdd(message.label..alignR(" Turtle #"..str(message.id),x-#message.label), theme.title, true) then
  909.         self:tryAdd("Your turtle's name is long...", theme.title, true)
  910.       end
  911.       self:tryAdd("Fuel: "..alignR(str(message.fuel),x-6), theme.extra, true)
  912.      
  913.       self:tryAdd("Percentage Done: "..alignR(str(message.percent).."%",x-17), theme.pos, true)
  914.      
  915.       local var1 = math.max(#str(message.x), #str(message.z), #str(message.layers))
  916.       local var2 = (x-6-var1+3)/3
  917.       self:tryAdd("Pos: "..alignR(" X:"..alignR(str(message.xPos),var1),var2)..alignR(" Z:"..alignR(str(message.zPos),var1),var2)..alignR(" Y:"..alignR(str(message.layersDone),var1),var2), theme.pos, true)
  918.       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)
  919.       self:tryAdd("Volume: "..str(message.volume), theme.dim, false, true, true)
  920.       self:tryAdd("",{}, false, false, true)
  921.       self:tryAdd(center("____---- EXTRAS ----____",x), theme.subtitle, false, false, true)
  922.       self:tryAdd(center("Time:"..alignR(textutils.formatTime(os.time()),10), x), theme.extra, false, true, true)
  923.       self:tryAdd(center("Current Day: "..str(os.day()), x), theme.extra, false, false, true)
  924.       self:tryAdd("Used Inventory Slots: "..alignR(str(16-message.openSlots),x-22), theme.extra, false, true, true)
  925.       self:tryAdd("Blocks Mined: "..alignR(str(message.mined),x-14), theme.extra, false, true, true)
  926.       self:tryAdd("Blocks Moved: "..alignR(str(message.moved),x-14), theme.extra, false, true, true)
  927.       self:tryAdd("Distance to Turtle: "..alignR(str(message.distance), x-20), theme.extra, false, false, true)
  928.       self:tryAdd("Actual Y Pos (Not Layer): "..alignR(str(message.yPos), x-26), theme.extra, false, false, true)
  929.      
  930.       if message.chestFull then
  931.         self:tryAdd("Dropoff is Full, Please Fix", theme.error, false, true, true)
  932.       end
  933.       if message.foundBedrock then
  934.         self:tryAdd("Found Bedrock! Please Check!!", theme.error, false, true, true)
  935.       end
  936.       if message.status then
  937.         self:tryAdd("Status: "..message.status, theme.info, false, true, true)
  938.       end
  939.       if message.isAtChest then
  940.         self:tryAdd("Turtle is at home chest", theme.info, false, true, true)
  941.       end
  942.       if message.isGoingToNextLayer then
  943.         self:tryAdd("Turtle is going to next layer", theme.info, false, true, true)
  944.       end
  945.      
  946.        
  947.      
  948.     end
  949.     if self.term.isColor() and ((self.size[2] >= 2 and self.size[1] >= 3) or self.isPocket) then
  950.         local line = self.acceptsInput and self.dim[2]-1 or self.dim[2]
  951.         local part = math.floor(x/4)
  952.         if #self.buttons == 0 then
  953.           self:addButton(button.new(line, part*0, part*1-1, "drop","Drop"))
  954.           self:addButton(button.new(line, part*1, part*2-1, "pause","Pause"))
  955.           self:addButton(button.new(line, part*2, part*3-1, "return","Return"))
  956.           self:addButton(button.new(line, part*3, part*4-1, "refuel","Refuel"))
  957.         end
  958.         self:tryAddRaw(line, button.makeLine(self.buttons,"|"):sub(1,self.isPocket and -2 or -1), theme.command, false, true) --Silly code because pocket breaks
  959.       end
  960.   else --If is done
  961.     if self.size[1] == 1 then --Special case for small monitors
  962.       self:tryAdd("Done", theme.title, true)
  963.       if not self:tryAdd("Dug"..alignR(str(message.mined),4, false), theme.pos, true) then
  964.         self:tryAdd("Dug", theme.pos, true)
  965.         self:tryAdd(alignR(str(message.mined),x), theme.pos, true)
  966.       end
  967.       if not self:tryAdd("Fuel"..alignR(str(message.fuel),3, false), theme.pos, true) then
  968.         self:tryAdd("Fuel", theme.pos, true)
  969.         self:tryAdd(alignR(str(message.fuel),x), theme.pos, true)
  970.       end
  971.       self:tryAdd("-------", theme.subtitle, false,true,true)
  972.       self:tryAdd("Turtle", theme.subtitle, false, true, true)
  973.       self:tryAdd(center("is", x), theme.subtitle, false, true, true)
  974.       self:tryAdd(center("Done!", x), theme.subtitle, false, true, true)
  975.     else
  976.       self:tryAdd("Done!", theme.title, true)
  977.       self:tryAdd("Curr Fuel: "..str(message.fuel), theme.pos, true)
  978.       if message.preciseTotals then
  979.         local tab = {}
  980.         for a,b in pairs(message.preciseTotals) do --Sorting the table
  981.           a = a:match(":(.+)")
  982.           if #tab == 0 then --Have to initialize or rest does nothing :)
  983.             tab[1] = {a,b}
  984.           else
  985.             for i=1, #tab do --This is a really simple sort. Probably not very efficient, but I don't care.
  986.               if b > tab[i][2] then --Gets the second value from the table, which is the itemCount
  987.                 table.insert(tab, i, {a,b})
  988.                 break
  989.               elseif i == #tab then --Insert at the end if not bigger than anything
  990.                 table.insert(tab,{a,b})
  991.               end
  992.             end
  993.           end
  994.         end
  995.         for i=1, #tab do --Print all the blocks in order
  996.           local firstPart = "#"..tab[i][1]..": "
  997.           self:tryAdd(firstPart..alignR(tab[i][2], x-#firstPart), (i%2 == 0) and theme.inverse or theme.info, true, true, true) --Switches the colors every time
  998.         end
  999.       else
  1000.         self:tryAdd("Blocks Dug: "..str(message.mined), theme.inverse, true)
  1001.         self:tryAdd("Cobble Dug: "..str(message.cobble), theme.pos, false, true, true)
  1002.         self:tryAdd("Fuel Dug: "..str(message.fuelblocks), theme.pos, false, true, true)
  1003.         self:tryAdd("Others Dug: "..str(message.other), theme.pos, false, true, true)
  1004.       end
  1005.     end
  1006.   end
  1007. end
  1008. screenClass.updateHandshake = function(self)
  1009.   self.toPrint = {}
  1010.   local half = math.ceil(self.dim[2]/2)
  1011.   if self.size[1] == 1 then --Not relying on the parameter system because less calls
  1012.     self:tryAddRaw(half-2, "Waiting", self.theme.error, true)
  1013.     self:tryAddRaw(half-1, "For Msg", self.theme.error, true)
  1014.     self:tryAddRaw(half, "On Chnl", self.theme.error, true)
  1015.     self:tryAddRaw(half+1, tostring(self.receive), self.theme.error, true)
  1016.   else
  1017.     local str = "for"
  1018.     if self.size[1] == 2 then str = "4" end--Just a small grammar change
  1019.     self:tryAddRaw(half-2, "", self.theme.error, true) --Filler
  1020.     self:tryAddRaw(half-1, center("Waiting "..str.." Message", self.dim[1]), self.theme.error, true)
  1021.     self:tryAddRaw(half, center("On Channel "..tostring(self.receive), self.dim[1]), self.theme.error, true)
  1022.     self:tryAddRaw(half+1, "",self.theme.error, true)
  1023.   end
  1024. end
  1025. screenClass.updateBroken = function(self) --If screen needs channel
  1026.   self.toPrint = {}
  1027.   if self.size[1] == 1 then
  1028.     self:tryAddC("No Rec", self.theme.pos, false, true, true)
  1029.     self:tryAddC("Channel", self.theme.pos, false, true, true)
  1030.     self:tryAddC("-------", self.theme.title, false, true, true)
  1031.     self:tryAddC("On Comp", self.theme.info, true)
  1032.     self:tryAddC("Type:", self.theme.info, true)
  1033.     self:tryAddC("RECEIVE", self.theme.command, true)
  1034.     if not self:tryAddC(self.side:upper(), self.theme.command, true) then --If we can't print the full side
  1035.       self:tryAddC("[side]",self.theme.command, true)
  1036.     end
  1037.     self:tryAddC("[Chnl]", self.theme.command, true)
  1038.   else
  1039.     self:tryAddC("No receiving", self.theme.pos, false, true, true)
  1040.     self:tryAddC("channel for", self.theme.pos, false, true, true)
  1041.     self:tryAddC("this screen", self.theme.pos, false, true, true)
  1042.     self:tryAddC("-----------------", self.theme.title, false, true, true)
  1043.     self:tryAddC("On main computer,", self.theme.info, true)
  1044.     self:tryAddC("Type:", self.theme.info, true)
  1045.     self:tryAdd("", self.theme.command, false, true, true)
  1046.     self:tryAddC('"""', self.theme.command, false, true, true)
  1047.     self:tryAddC("RECEIVE", self.theme.command, true)
  1048.     if not self:tryAddC(self.side:upper(), self.theme.command, true) then --If we can't print the full side
  1049.       self:tryAddC("[side]",self.theme.command, true)
  1050.     end
  1051.     self:tryAddC("[desired channel]", self.theme.command, true)
  1052.     self:tryAddC('"""', self.theme.command, false, true, true)
  1053.   end
  1054. end
  1055. screenClass.updateStation = function(self)
  1056.   self.toPrint = {}
  1057.   sepChar = "| "
  1058.   local part = math.floor((self.dim[1]-3*#sepChar - 3)/3)
  1059.   self:tryAdd(alignL("ID",3)..sepChar..alignL("Side",part)..sepChar..alignL("Channel",part)..sepChar..alignL("Theme",part), self.theme.title, true, true, true)--Headings
  1060.   local line = ""
  1061.   for i=1, self.dim[1] do line = line.."-" end
  1062.   self:tryAdd(line, self.theme.title, false, true, true)
  1063.   for a,b in ipairs(screenClass.screens) do
  1064.     if b.side ~= "REMOVED" then
  1065.       self:tryAdd(alignL(b.id,3)..sepChar..alignL(b.side,part)..sepChar..alignL(b.receive, part)..sepChar..alignL(b.theme.name,part), self.theme.info, true, true, true)--Prints info about all screens
  1066.     end
  1067.   end
  1068. end
  1069.  
  1070. screenClass.updateDisplay = screenClass.updateNormal --Update screen method is normally this one
  1071.  
  1072. --Misc
  1073. screenClass.setNormalDisplay = function(self)
  1074.   self.updateDisplay = self.updateNormal --This defaults to super if doesn't exist
  1075. end
  1076. screenClass.setHandshakeDisplay = function(self)
  1077.   self.updateDisplay = self.updateHandshake --Sets update to handshake version, defaults to super if doesn't exist
  1078. end
  1079. screenClass.setBrokenDisplay = function(self)
  1080.   self.updateDisplay = self.updateBroken
  1081. end
  1082. screenClass.setStationDisplay = function(self)
  1083.   self.updateDisplay = self.updateStation
  1084. end
  1085.  
  1086. --Help Function. Goes so low so can see screenClass.theme
  1087. local function displayHelp()
  1088.   local dummy = {term = term} --This will be a dummy "screnClass object" for setting color
  1089.   setmetatable(dummy, {__index = screenClass})
  1090.   local theme = dummy.theme
  1091.   local tab = {}
  1092.   local indexOuter = "main"
  1093.   local indexInner = 1
  1094.   for key, value in pairs(helpResources) do
  1095.     tab[key] = {}
  1096.     for a in value:gmatch("$$([^$]+)") do
  1097.       table.insert(tab[key], a) --Just inserting pages
  1098.     end
  1099.   end
  1100.   while true do
  1101.     dummy:setColor(theme.help)
  1102.     clearScreen(1,2)
  1103.     print(tab[indexOuter][indexInner]:match("\n(.+)")) --Print all but first line
  1104.     dummy:setColor(theme.title)
  1105.     dummy.term.setCursorPos(1,1)
  1106.     print(alignL(tab[indexOuter][indexInner]:match("[^\n]+") or "",({dummy.term.getSize()})[1])) --Print first line
  1107.     dummy:setColor(theme.info)
  1108.     local text = tostring(indexInner).."/"..tostring(#tab[indexOuter])
  1109.     term.setCursorPos(({term.getSize()})[1]-#text,1)
  1110.     term.write(text) --Print the current page number
  1111.     local event, key = os.pullEvent("key")
  1112.     key = keyMap[key]
  1113.     if tonumber(key) and tab[tonumber(key)] then
  1114.       indexOuter = tonumber(key)
  1115.       indexInner = 1
  1116.     elseif key == "Q" then
  1117.       os.pullEvent("char") --Capture extra event (note: this always works because only q triggers this)
  1118.       return true
  1119.     elseif key == "0" then --Go back to beginning
  1120.       indexOuter, indexInner = "main",1
  1121.     elseif key == "up" and indexInner > 1 then
  1122.       indexInner = indexInner-1
  1123.     elseif key == "down" and indexInner < #tab[indexOuter] then
  1124.       indexInner = indexInner + 1
  1125.     end
  1126.   end
  1127.    
  1128. end
  1129.  
  1130.  
  1131. local function wrapPrompt(prefix, str, dim) --Used to wrap the commandString
  1132.   return prefix..str:sub(roundNegative(#str+#prefix-computer.dim[1]+2), -1).."_" --it is str + 2 because we add in the "_"
  1133. end
  1134.  
  1135. local function updateAllScreens()
  1136.   for a, b in pairs(screenClass.sides) do
  1137.     b:updateDisplay()
  1138.     b:reset()
  1139.     b:pushScreenUpdates()
  1140.   end
  1141. end
  1142. --Rednet
  1143. local function newMessageID()
  1144.   return math.random(1,2000000000) --1 through 2 billion. Good enough solution
  1145. end
  1146. local function transmit(send, receive, message, legacy, fingerprint)
  1147.   fingerprint = fingerprint or replyFingerprint
  1148.   if legacy then
  1149.     modem.transmit(send, receive, message)
  1150.   else
  1151.     modem.transmit(send, receive, {message = message, id = newMessageID(), fingerprint = fingerprint})
  1152.   end
  1153. end
  1154.  
  1155. --QuadRotor
  1156. local function launchQuad(message)
  1157.   if quadEnabled and message.emergencyLocation then --This means the turtle is out of fuel. Also that it sent its two initial positions
  1158.     local movement = {}
  1159.     local function add(what) table.insert(movement,what) end
  1160.     add(quadDirection) --Get to the fuel chest
  1161.     add("suck")
  1162.     add(quadDirection) --So it can properly go down/up first
  1163.     local function go(dest, orig, firstMove) --Goes to a place. firstMove because I'm lazy. Its for getting away from computer. If false, its the second move so go one above turtle. If nothing then nothing
  1164.       local distX, distY, distZ = dest[1]-orig[1], dest[2]-orig[2], dest[3]-orig[3]
  1165.       if firstMove then
  1166.         distX = distX - 3 * (quadDirection == "east" and 1 or (quadDirection == "west" and -1 or 0))
  1167.         distZ = distZ - 3 * (quadDirection == "south" and 1 or (quadDirection == "north" and -1 or 0))
  1168.         distY = distY - 1 --Because the quad is a block above the first thing
  1169.       elseif firstMove == false then
  1170.         local num = 2
  1171.         if message.layersDone  <= 1 then
  1172.           num = 1
  1173.         end
  1174.         distY = distY + num * (distY < 0 and 1 or -1) --This is to be above the turtle and accounts for invert
  1175.       end
  1176.       add((distY > 0 and "up" or "down").." "..tostring(math.abs(distY)))
  1177.       add((distX > 0 and "east" or "west").." "..tostring(math.abs(distX)))
  1178.       add((distZ > 0 and "south" or "north").." "..tostring(math.abs(distZ)))
  1179.       if firstMove == false and message.layersDone > 1 then
  1180.         add(distY < 0 and "down" or "up") --This is so it goes into the turtle's proper layer (invert may or may not work, actually)
  1181.       end
  1182.     end
  1183.     debug("Location Types")
  1184.     debug(computerLocation)
  1185.     debug(message.firstPos)
  1186.     debug(message.secondPos)
  1187.     debug(message.emergencyLocation)
  1188.     go(message.firstPos, computerLocation, true) --Get to original position of turtle
  1189.     go(message.secondPos,message.firstPos) --Get into quarry
  1190.     go(message.emergencyLocation, message.secondPos, false)
  1191.    
  1192.     add("drop")
  1193.     add("return")
  1194.     for a,b in pairs(movement) do
  1195.       debug(a,"   ",b)
  1196.     end
  1197.     quadBase.flyQuad(movement) --Note, if there are no quadrotors, nothing will happen and the turtle will sit forever
  1198.    
  1199.   end
  1200. end
  1201.  
  1202. --==SET UP==
  1203. clearScreen()
  1204. print("Welcome to Quarry Receiver!")
  1205. sleep(1)
  1206.  
  1207. --==ARGUMENTS==
  1208.  
  1209. --[[
  1210. Parameters:
  1211.   -help/-?/help/?
  1212.   -v/verbose --Turn on debugging
  1213.   -receiveChannel/channel [channel] --For only the main screen
  1214.   -theme --Sets a default theme
  1215.   -screen [side] [channel] [theme]
  1216.   -station
  1217.   -auto --Prompts for all sides, or you can supply a list of receive channels for random assignment!
  1218.   -colorEditor
  1219.   -quad [cardinal direction] --This looks for a quadrotor from the quadrotors mod. The direction is of the fuel chest.
  1220.   -autoRestart --Will reset any attached screen when done, instead of bricking them
  1221. ]]
  1222.  
  1223. --tArgs init
  1224. local parameters = {} --Each command is stored with arguments
  1225.  
  1226. local function addParam(value)
  1227.   val = value:lower()
  1228.   if val:match("^%-") then
  1229.     parameters[#parameters+1] = {val:sub(2)} --Starts a chain with the command. Can be unpacked later
  1230.     parameters[val:sub(2)] = {} --Needed for force/before/after parameters
  1231.   elseif parameterIndex ~= 0 then
  1232.     table.insert(parameters[#parameters], value) --value because arguments should be case sensitive for filenames
  1233.     table.insert(parameters[parameters[#parameters][1]], value) --Needed for force/after parameters
  1234.   end
  1235. end
  1236.  
  1237. for a,b in ipairs(tArgs) do
  1238.   addParam(b)
  1239. end
  1240.  
  1241. if parameters.theme then --This goes here so help can display in different theme :)
  1242.   screenClass:setTheme(parameters.theme[1])
  1243. end
  1244.  
  1245. for a,b in ipairs(tArgs) do
  1246.   val = b:lower()
  1247.   if val == "help" or val == "-help" or val == "?" or val == "-?" or val == "usage" or val == "-usage" then
  1248.     displayHelp() --To make
  1249.     error("The End of Help",0)
  1250.   end
  1251. end
  1252.  
  1253. --Debug parameters
  1254. if parameters.v or parameters.verbose then --Why not
  1255.   doDebug = true
  1256. end
  1257.  
  1258. for i=1,#parameters do
  1259.   debug("Parameter: ",parameters[i][1])
  1260. end
  1261.  
  1262. --Options before screen loads
  1263.  
  1264. if parameters.modem then
  1265.   modemSide = parameters.modem[1]
  1266. end
  1267.  
  1268. if parameters.quad then
  1269.   if not parameters.quad[1] then parameters.quad[1] = "direction doesn't exist" end
  1270.   local dir = parameters.quad[1]:lower():sub(1,1)
  1271.   if quadDirections[dir] then
  1272.     quadEnabled = true
  1273.     quadDirection = quadDirections[dir]
  1274.   else
  1275.     clearScreen()
  1276.     print("Please specify the cardinal direction your quad station is in")
  1277.     print("Make sure you have a quad station on one side with a chest behind it, forming a line")
  1278.     print("Like this: [computer] [station] [fuel chest]")
  1279.     print("The program will now terminate")
  1280.     error("",0)
  1281.   end
  1282. end
  1283.  
  1284. if parameters.autorestart then
  1285.   local val = parameters.autorstart[1]
  1286.   if not val then
  1287.     autoRestart = true --Assume no value = force true
  1288.   else
  1289.    val = val:sub(1,1):lower()
  1290.    autoRestart = not (val == "n" or val == "f")
  1291.   end
  1292. end
  1293.    
  1294. --Init Modem
  1295. while not initModem() do
  1296.   clearScreen()
  1297.   print("No modem is connected, please attach one")
  1298.   if not peripheral.find then
  1299.     print("What side was that on?")
  1300.     modemSide = read()
  1301.   else
  1302.     os.pullEvent("peripheral")
  1303.   end
  1304. end
  1305. debug("Modem successfully connected!")
  1306.  
  1307. local function autoDetect(channels)
  1308.   if type(channels) ~= "table" then channels = {} end
  1309.   local tab = peripheral.getNames()
  1310.   local index = 1
  1311.   for i=1, #tab do
  1312.     if peripheral.getType(tab[i]) == "monitor" and not screenClass.sides[tab[i]] then
  1313.       screenClass.new(tab[i], channels[index]) --You can specify a list of channels in "auto" parameter
  1314.       index = index+1
  1315.     end
  1316.   end
  1317. end
  1318.  
  1319. --Init QuadRotor Station
  1320. if quadEnabled then
  1321.   local flag
  1322.   while not flag do
  1323.     for a,b in ipairs({"front","back","left","right","top"}) do
  1324.       if peripheral.isPresent(b) and peripheral.getType(b) == "quadbase" then
  1325.         quadBase = peripheral.wrap(b)
  1326.       end
  1327.     end
  1328.     clearScreen()
  1329.     if not quadBase then
  1330.       print("No QuadRotor Base Attached, please attach one")
  1331.     elseif quadBase.getQuadCount() == 0 then
  1332.       print("Please install at least one QuadRotor in the base")
  1333.       sleep(1) --Prevents screen flickering and overcalling gps
  1334.     else
  1335.       flag = true
  1336.       debug("QuadBase successfully connected!")
  1337.     end
  1338.     if not computerLocation and not gps.locate(5) then
  1339.       flag = false
  1340.       error("No GPS lock. Please make a GPS network to use quadrotors")
  1341.     else
  1342.       computerLocation = {gps.locate(5)}
  1343.       debug("GPS Location Acquired")
  1344.     end
  1345.   end
  1346. end
  1347.  
  1348. --Init Computer Screen Object (was defined at top)
  1349. computer = screenClass.new("computer", (parameters.receivechannel and parameters.receivechannel[1]) or (parameters.channel and parameters.channel[1]))--This sets channel, checking if parameter exists
  1350. computer.updateNormal = function(self)
  1351.   screenClass.updateNormal(self)
  1352.   computer:displayCommand()
  1353. end
  1354. computer.updateHandshake = function(self) --Not in setHandshake because that func checks object updateHandshake
  1355.   screenClass.updateHandshake(self)
  1356.   computer:displayCommand()
  1357. end
  1358. computer.updateBroken = function(self)
  1359.   screenClass.updateBroken(self)
  1360.   computer:displayCommand()
  1361. end
  1362. computer.updateStation = function(self)--This gets set in setSize
  1363.   screenClass.updateStation(self)
  1364.   self:displayCommand()
  1365. end
  1366.  
  1367.  
  1368. for i=1, #parameters do --Do actions for parameters that can be used multiple times
  1369.   local command, args = parameters[i][1], parameters[i] --For ease
  1370.   if command == "screen" then
  1371.     if not screenClass.sides[args[2]] then --Because this screwed up the computer
  1372.       local a = screenClass.new(args[2], args[3], args[4])
  1373.       debug(type(a))
  1374.     else
  1375.       debug("Overwriting existing screen settings for '",args[2],"'")
  1376.       local a = screenClass.sides[args[2]]
  1377.       a:setChannel(tonumber(args[3]))
  1378.       a:setTheme(args[4])
  1379.     end
  1380.   end
  1381.   if command == "station" then --This will set the screen update to display stats on all other monitors
  1382.     if not args[2] or args[2]:lower() == "computer" then --Not below because it exists
  1383.       computer:setStation() --This handles setting updateNormal, setHandshakeDisplay, etc
  1384.     else
  1385.       local a = screenClass.new(args[2], nil, args[3]) --This means syntax is -station [side] [theme]
  1386.       if a then --If the screen actually exists
  1387.         a:setStation()
  1388.       end
  1389.     end
  1390.   end
  1391. end
  1392.  
  1393. if parameters.auto then --This must go after computer declaration so computer ID is 1
  1394.   autoDetect(parameters.auto)
  1395.   addParam("-station") --Set computer as station
  1396.   addParam("computer") --Yes, I'm literally just feeding in more tArgs like from IO
  1397. end
  1398.  
  1399. computer.displayCommand = function(self)
  1400.   local sideString = ((defaultSide and " (") or "")..(defaultSide or "")..((defaultSide and ")") or "")
  1401.   if self.size == 1 then
  1402.     self:tryAddRaw(self.dim[2], wrapPrompt("Cmd"..sideString:sub(2,-2)..": ", commandString, self.dim[1]), self.theme.command, true)
  1403.   else
  1404.     self:tryAddRaw(self.dim[2], wrapPrompt("Command"..sideString..": ",commandString, self.dim[1]), self.theme.command, true) --This displays the last part of a string.
  1405.   end
  1406. end
  1407. --Initializing the computer screen
  1408. if parameters.coloreditor then
  1409.  
  1410.   computer:removeChannel() --So it doesn't receive messages
  1411.   computer.isStation = true --So we can't assign a channel
  1412.  
  1413.   computer.updateNormal = function(self) --This is only for editing colors
  1414.     self.toPrint = {}
  1415.     for i=1, #requiredColors do
  1416.       self:tryAdd(requiredColors[i], self.theme[requiredColors[i]],true)
  1417.     end
  1418.     self:displayCommand()
  1419.   end
  1420.   computer.updateHandshake = computer.updateNormal
  1421.   computer.updateBroken = computer.updateNormal
  1422.   computer.updateStation = computer.updateNormal
  1423. end
  1424. computer:setSize() --Update changes made to display functions
  1425.  
  1426. for a,b in pairs(screenClass.sides) do debug(a) end
  1427.  
  1428. --==FINAL CHECKS==
  1429.  
  1430. --If only one screen and computer has no channel, make it a station
  1431. if #screenClass.screens > 1 and not computer.receive then
  1432.   debug("Only one screen, no comp channel. Setting station")
  1433.   computer:setStation()
  1434. end
  1435.  
  1436. --Updating all screen for first time and making sure channels are open
  1437. for a, b in pairs(screenClass.sides) do
  1438.   b:setSize()
  1439.   b:updateDisplay()--Finish initialization process
  1440.   b:reset()
  1441.   b:pushScreenUpdates()
  1442. end
  1443.  
  1444. --Handshake will be handled in main loop
  1445.  
  1446. --[[Workflow
  1447.   Wait for events
  1448.   modem_message
  1449.     if valid channel and valid message, update appropriate screen
  1450.   key
  1451.     if any letter, add to command string if room.
  1452.     if enter key
  1453.       if valid self command, execute command. Commands:
  1454.         command [side] [command] --If only one screen, then don't need channel. Send a command to a turtle
  1455.         screen [side] [channel] [theme] --Links a new screen to use.
  1456.         remove [side] --Removes a screen
  1457.         theme [themeName] --Sets the default theme
  1458.         theme [side] [themeName] --Changes this screen's theme
  1459.         savetheme [new name] [themeName]
  1460.         color [side/theme] [colorName] [textColor] [backgroundColor]
  1461.         side [side] --Sets a default side, added to prompts
  1462.         set [string] --Sets a default command, added to display immediately
  1463.         receive [side] [newChannel] --Changes the channel of the selected screen
  1464.         send [side] [newChannel]
  1465.         auto --Automatically adds screens not connected
  1466.         station --Sets the selected screen as a station (or resets if already a station)
  1467.         exit/quit/end
  1468.   peripheral_detach
  1469.     check what was lost, if modem, set to nil. If screen side, do screen:setSize()
  1470.   peripheral
  1471.     check if screen side already added
  1472.       reset screen size
  1473.   monitor_resize
  1474.     resize proper screen
  1475.   monitor_touch
  1476.     if screen already added
  1477.       select screen on main computer
  1478.     else
  1479.       add screen
  1480.  
  1481. ]]
  1482.  
  1483. --Modes: 1 - Sided, 2 - Not Sided, 3 - Both sided and not
  1484. 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,
  1485.                        auto = 2, verbose = 2, quiet = 2, station = 1}
  1486. while continue do
  1487.   local event, par1, par2, par3, par4, par5 = os.pullEvent()
  1488.   ----MESSAGE HANDLING----
  1489.   if event == "modem_message" and screenClass.channels[par2] then --If we got a message for a screen that exists
  1490.     local screen = screenClass.channels[par2] --For convenience
  1491.     if not screen.send then --This is the handshake
  1492.       debug("\nChecking handshake. Received: ",par4)
  1493.       local flag = false
  1494.       if par4 == expectedMessage then --Legacy quarries don't accept receiver dropping in mid-run
  1495.         screen.legacy = true --Accepts serialized tables
  1496.         flag = true
  1497.       elseif type(par4) == "table" and par4.fingerprint == expectedFingerprint then --Don't care about expected message, allows us to start receiver mid-run, fingerprint should be pretty specific
  1498.         screen.legacy = false
  1499.         flag = true
  1500.       end
  1501.      
  1502.       if flag and (autoRestart or (not autoRestart and not screen.isDone)) then --We don't accept handshakes when we don't want autorestarts
  1503.         screen.isDone = false
  1504.         screen.rec = copyTable(screenClass.rec) --Need to reset this. Existing message from restart doesn't have everything
  1505.         debug("Screen ",screen.side," received a handshake")
  1506.         screen.send = par3
  1507.         screen:setSize() --Resets update method to proper since channel is set
  1508.         debug("Sending back on ",screen.send)
  1509.         transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  1510.       end
  1511.    
  1512.     else --Everything else is for regular messages
  1513.      
  1514.       local rec
  1515.       if screen.legacy then --We expect strings here
  1516.         if type(par4) == "string" then --Otherwise its not ours
  1517.           if par4 == "stop" then --This is the stop message. All other messages will be ending ones
  1518.             screen.isDone = true
  1519.           elseif par4 == expectedMessage then --We support dropping in mid-run
  1520.             debug("Screen ",screen.side," received mid-run handshake")
  1521.             transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  1522.           elseif textutils.unserialize(par4) then
  1523.             rec = textutils.unserialize(par4)
  1524.             rec.distance = par5
  1525.           end
  1526.         end
  1527.       elseif type(par4) == "table" and par4.fingerprint == expectedFingerprint then --Otherwise, we check if it is valid message
  1528.        
  1529.         if type(par4.message) == "table" then
  1530.           rec = par4.message
  1531.           if not par4.distance then --This is cool because it can add distances from the repeaters
  1532.             rec.distance = par5
  1533.           else
  1534.             rec.distance = par4.distance + par5
  1535.           end
  1536.           if rec.isDone then
  1537.             screen.isDone = true
  1538.             screen.send = nil --So that we can receive handshakes again.
  1539.           end
  1540.         elseif par4.message == expectedMessage then
  1541.           debug("Screen ",screen.side," received mid-run handshake")
  1542.           transmit(screen.send,screen.receive, replyMessage, screen.legacy)
  1543.         else
  1544.           debug("Message received did not contain table")
  1545.         end
  1546.       end
  1547.        
  1548.       if rec then
  1549.         rec.distance = math.floor(rec.distance)
  1550.         rec.label = rec.label or "Quarry!"
  1551.         screen.rec = rec --Set the table
  1552.         --Updating screen occurs outside of the if
  1553.         local toSend
  1554.         if screen.queuedMessage then
  1555.           toSend = screen.queuedMessage
  1556.           screen.queuedMessage = nil
  1557.         else
  1558.           toSend = replyMessage
  1559.         end
  1560.         if not screen.isDone then --Because then sendChannel doesn't exist
  1561.           transmit(screen.send,screen.receive, toSend, screen.legacy) --Send reply message for turtle
  1562.         end
  1563.       end
  1564.      
  1565.     end
  1566.    
  1567.     launchQuad(screen.rec) --Launch the Quad! (This only activates when turtle needs it)
  1568.    
  1569.     screen:updateDisplay() --isDone is queried inside this
  1570.     screen:reset(screen.theme.background)
  1571.     screen:pushScreenUpdates() --Actually write things to screen
  1572.     --if screen.isDone and not autoRestart then screen:removeChannel() end --Don't receive any more messages. Allows turtle to think connected. Done after message sending so no error :)
  1573.  
  1574.   ----KEY HANDLING----
  1575.   elseif event == "key" and keyMap[par1] then
  1576.     local key = keyMap[par1]
  1577.     if key ~= "enter" then --If we aren't submitting a command
  1578.       if key == "backspace" then
  1579.         if #commandString > 0 then
  1580.           commandString = commandString:sub(1,-2)
  1581.         end
  1582.       elseif key == "up" then
  1583.         commandString = lastCommand or commandString --Set to last command, or do nothing if it doesn't exist
  1584.       elseif key == "down" then
  1585.         commandString = "" --If key down, clear
  1586.       elseif #key == 1 then
  1587.         commandString = commandString..key
  1588.       end
  1589.     --ALL THE COMMANDS
  1590.     else --If we are submitting a command
  1591.       lastCommand = commandString --For using up arrow
  1592.       local args = {}
  1593.       for a in commandString:gmatch("%S+") do --This captures all individual words in the command string
  1594.         args[#args+1] = a:lower()
  1595.       end
  1596.       local command = args[1]
  1597.       if validCommands[command] then --If it is a valid command...
  1598.         local commandType = validCommands[command]
  1599.         if commandType == 1 or commandType == 3 then --If the command requires a "side" like transmitting commands, versus setting a default
  1600.           if defaultSide then table.insert(args, 2, defaultSide) end
  1601.           local screen
  1602.           local test = screenClass.screens[tonumber(args[2])]
  1603.           if test and test.side ~= "REMOVED" then --This way we can specify IDs as well
  1604.             screen = test
  1605.           else
  1606.             screen = screenClass.sides[args[2]]
  1607.           end
  1608.           if screen then --If the side exists
  1609.             if command == "command" and screen.send then --If sending command to the turtle
  1610.               screen.queuedMessage = table.concat(args," ", 3) --Tells message handler to send appropriate message
  1611.               --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
  1612.             end
  1613.  
  1614.             if command == "color" then
  1615.               screen.theme:addColor(args[3],colors[args[4]],colors[args[5]] )
  1616.               updateAllScreens() --Because we are changing a theme color which others may have
  1617.             end
  1618.             if command == "theme" then
  1619.               screen:setTheme(args[3])
  1620.             end
  1621.             if command == "send" then --This changes a send channel, and can also revert to handshake
  1622.               local chan = checkChannel(tonumber(args[3]) or -1)
  1623.               if chan then screen.send = chan else screen.send = nil end
  1624.               screen:setSize() --If on handshake, resets screen
  1625.             end
  1626.             if command == "receive" and not screen.isStation then
  1627.               local chan = checkChannel(tonumber(args[3]) or -1)
  1628.               if chan and not screenClass.channels[chan] then
  1629.                 screen:setChannel(chan)
  1630.                 screen:setSize() --Update broken status
  1631.               end
  1632.             end
  1633.             if command == "station" then
  1634.               if screen.isStation then screen:removeStation() else screen:setStation() end
  1635.             end
  1636.             if command == "remove" and screen.side ~= "computer" then --We don't want to remove the main display!
  1637.               print()
  1638.               screen:remove()
  1639.             else --Because if removed it does stupid things
  1640.               screen:reset()
  1641.               debug("here")
  1642.               screen:updateDisplay()
  1643.               debug("Here")
  1644.               screen:pushScreenUpdates()
  1645.               debug("Hereer")
  1646.             end
  1647.           end
  1648.         end
  1649.         if commandType == 2 or commandType == 3 then--Does not require a screen side
  1650.           if command == "screen" and peripheral.getType(args[2]) == "monitor" then  --Makes sure there is a monitor on the screen side
  1651.             if not args[3] or not screenClass.channels[tonumber(args[3])] then --Make sure the channel doesn't already exist
  1652.               local mon = screenClass.new(args[2], args[3], args[4])
  1653.                 --args[3] is the channel  and will set broken display if it doesn't exist
  1654.                 --args[4] is the theme, and will default if doesn't exists.
  1655.               mon:updateDisplay()
  1656.               mon:reset()
  1657.               mon:pushScreenUpdates()
  1658.             end
  1659.           end
  1660.           if command == "theme" then
  1661.             screenClass:setTheme(args[2], true) --Otherwise this would set base theme to nil, erroring
  1662.             updateAllScreens()
  1663.           end
  1664.           if command == "color" and themes[args[2]] then
  1665.             themes[args[2]]:addColor(args[3],colors[args[4]],colors[args[5]])
  1666.             updateAllScreens() --Because any screen could have this theme
  1667.           end
  1668.           if command == "side" then
  1669.             if screenClass.sides[args[2]] then
  1670.               defaultSide = args[2]
  1671.             else
  1672.               defaultSide = nil
  1673.             end
  1674.           end
  1675.           if command == "set" then
  1676.             if args[2] then
  1677.               defaultCommand = table.concat(args," ",2)
  1678.               defaultCommand = defaultCommand:upper()
  1679.             else
  1680.               defaultCommand = nil
  1681.             end
  1682.           end
  1683.           if command == "savetheme" then
  1684.             if saveTheme(themes[args[2]], args[3]) then
  1685.               computer:tryAddRaw(computer.dim[2]-1, "Save Theme Succeeded!", computer.theme.inverse, true)
  1686.             else
  1687.               computer:tryAddRaw(computer.dim[2]-1, "Save Theme Failed!", computer.theme.inverse, true)
  1688.             end
  1689.             computer:reset()
  1690.             computer:pushScreenUpdates()
  1691.             sleep(1)
  1692.           end
  1693.           if command == "auto" then
  1694.             local newTab = copyTable(args) --This is so we can pass all additional words as channel numbers
  1695.             table.remove(newTab, 1)
  1696.             autoDetect(newTab)
  1697.             updateAllScreens()
  1698.           end
  1699.           if command == "verbose" then doDebug = true end
  1700.           if command == "quiet" then doDebug = false end
  1701.           if command == "quit" or command == "exit" or command == "end" then
  1702.             continue = false
  1703.           end
  1704.         end
  1705.       else
  1706.         debug("\nInvalid Command")
  1707.       end
  1708.       if defaultCommand then commandString = defaultCommand.." " else commandString = "" end --Reset command string because it was sent
  1709.     end
  1710.    
  1711.    
  1712.     --Update computer display (computer is only one that displays command string
  1713.     computer:updateDisplay() --Note: Computer's method automatically adds commandString to last line
  1714.     if not continue then computer:tryAddRaw(computer.dim[2]-1,"Program Exiting", computer.theme.inverse, false, true, true) end
  1715.     computer:reset()
  1716.     computer:pushScreenUpdates()
  1717.    
  1718.   elseif event == "monitor_resize" then
  1719.     local screen = screenClass.sides[par1]
  1720.     if screen then
  1721.       screen:setSize()
  1722.       screen:updateDisplay()
  1723.       screen:reset()
  1724.       screen:pushScreenUpdates()
  1725.     end
  1726.   elseif event == "monitor_touch" then
  1727.     local screen = screenClass.sides[par1]
  1728.     debug("Side: ",par1," touched")
  1729.     if screen then --This part is copied from the "side" command
  1730.       local test = button.checkPoint(screen.buttons, {par2, par3})
  1731.       if test then
  1732.         screen.queuedMessage = test
  1733.       else
  1734.         if not screen.receive then
  1735.           commandString = "RECEIVE "..par1:upper().." "
  1736.         end
  1737.       end
  1738.     else
  1739.       debug("Adding Screen")
  1740.       local mon = screenClass.new(par1)
  1741.       commandString = "RECEIVE "..mon.side:upper().." "
  1742.       mon:reset()
  1743.       mon:updateDisplay()
  1744.       mon:pushScreenUpdates()
  1745.      
  1746.     end
  1747.     computer:reset()
  1748.     computer:updateDisplay()
  1749.     computer:pushScreenUpdates() --Need to update computer for command string
  1750.   elseif event == "mouse_click" then
  1751.     screen = computer
  1752.     local test = button.checkPoint(screen.buttons, {par2, par3})
  1753.     if test then
  1754.       screen.queuedMessage = test
  1755.     end
  1756.  
  1757.   elseif event == "peripheral_detach" then
  1758.     local screen = screenClass.sides[par1]
  1759.     if screen then
  1760.       screen:setSize()
  1761.     end
  1762.     --if screen then
  1763.     --  screen:remove()
  1764.     --end
  1765.    
  1766.   elseif event == "peripheral" then
  1767.     local screen = screenClass.sides[par1]
  1768.     if screen then
  1769.       screen:setSize()
  1770.     elseif peripheral.getType(par1) == "monitor" then
  1771.       commandString = "SCREEN "..par1:upper().." "
  1772.     end
  1773.  
  1774.   end
  1775.  
  1776.   local flag = false --Saying all screens are done, must disprove
  1777.   local count = 0 --We want it to wait if no screens have channels
  1778.   for a,b in pairs(screenClass.channels) do
  1779.     count = count + 1
  1780.     if autoRestart or not b.isDone then
  1781.       flag = true
  1782.     end
  1783.   end
  1784.   if continue and count > 0 then --If its not already false from something else
  1785.     continue = flag
  1786.   end
  1787.  
  1788.   if #stationsList > 0 and event ~= "key" and event ~= "char" then --So screen is properly updated
  1789.     for a, b in ipairs(stationsList) do
  1790.       b:reset()
  1791.       b:updateDisplay()
  1792.       b:pushScreenUpdates()
  1793.     end
  1794.   end
  1795.  
  1796.  
  1797. end
  1798.  
  1799. sleep(1.5)
  1800. for a in pairs(screenClass.channels) do
  1801.   modem.close(a)
  1802. end
  1803. for a, b in pairs(screenClass.sides) do
  1804.   if not b.isDone then --Otherwise we want it display the ending stats
  1805.     b:setTextColor(colors.white)
  1806.     b:setBackgroundColor(colors.black)
  1807.     b.term.clear()
  1808.     b.term.setCursorPos(1,1)
  1809.   end
  1810. end
  1811.  
  1812. local text --Fun :D
  1813. if computer.isComputer then text = "SUPER COMPUTER OS 9000"
  1814. elseif computer.isTurtle then text = "SUPER DIAMOND-MINING OS XXX"
  1815. elseif computer.isPocket then text = "PoCkEt OOS AMAYZE 65"
  1816. end
  1817. if text and not computer.isDone then
  1818.   computer:say(text, computer.theme.title,1)
  1819. else
  1820.   computer.term.setCursorPos(1,computer.dim[2])
  1821.   computer.term.clearLine()
  1822. end
  1823. --Down here shut down all the channels, remove the saved file, other cleanup stuff
  1824.  
Add Comment
Please, Sign In to add comment