Advertisement
civilwargeeky

Quarry Receiver 3.6.4

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