Advertisement
civilwargeeky

Quarry Receiver 3.6.2

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