Advertisement
civilwargeeky

Quarry Receiver 3.5.5

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