civilwargeeky

Quarry Receiver 3.6.5

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