Advertisement
civilwargeeky

Quarry Receiver 3.6.0

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