Advertisement
Wassaa

rednet rec

Oct 27th, 2014
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 42.66 KB | None | 0 0
  1. --Quarry Receiver Version 3.5.4
  2. --Made by Civilwargeeky
  3. --[[
  4. Ideas:
  5.  
  6.    
  7. ]]
  8. --[[
  9. Recent Changes:
  10.   Completely Remade!
  11. ]]
  12.  
  13. local commandHelpParagraph = [[
  14. Stop: Stops the turtle where it is
  15. Return: The turtle will return to its starting point, drop off its load, and stop
  16. Drop: Turtle will immediately go and drop its inventory
  17. Pause: Pauses the turtle
  18. Resume: Resumes paused turtles
  19. Help: This :D
  20. ]]
  21.  
  22.  
  23. --Config
  24. local doDebug = true --For testing purposes
  25. local ySizes = 3 --There are 3 different Y Screen Sizes right now
  26.  
  27. --Initializing Program-Wide Variables
  28. local expectedMessage = "Civil's Quarry" --Expected initial message
  29. local expectedFingerprint = "quarry"
  30. local replyMessage = "Turtle Quarry Receiver" --Message to respond to  handshake with
  31. local replyFingerprint = "quarryReceiver"
  32. local stopMessage = "stop"
  33. local expectedFingerprint = "quarry"
  34. local themeFolder = "quarryResources/receiverThemes/"
  35. local modemSide --User can specify a modem side, but it is not necessary
  36. local modem --This will be the table for the modem
  37. local computer --The main screen is special. It gets defined first :3
  38. local continue = true --This keeps the main while loop going
  39. --These two are used by controller in main loop
  40. local commandString = "" --This will be a command string sent to turtle. This var is stored for display
  41. local queuedMessage --If a command needs to be sent, this gets set
  42. local defaultSide
  43. local defaultCommand
  44.  
  45. local keyMap = {[57] = " ", [11] = "0", [12] = "-", [52] = "."} --This is for command string
  46. for i=2,10 do keyMap[i] = tostring(i-1) end --Add numbers
  47. for a,b in pairs(keys) do --Add all letters from keys api
  48.   if #a == 1 then
  49.     keyMap[b] = a:upper()
  50.   end
  51. end
  52. keyMap[keys.enter] = "enter"
  53. keyMap[keys.backspace] = "backspace"
  54.  
  55. --Generic Functions--
  56. local function debug(...)
  57.   --if doDebug then return print(...) end --Basic
  58.   if doDebug then
  59.     print("\nDEBUG: ",...)
  60.     os.pullEvent("char")
  61.   end
  62. end
  63. local function clearScreen(x,y, periph)
  64.   periph, x, y = periph or term, x or 1, y or 1
  65.   periph.clear()
  66.   periph.setCursorPos(x,y)
  67. end
  68.  
  69. local function swapKeyValue(tab)
  70.   for a,b in pairs(tab) do
  71.     tab[b] = a
  72.   end
  73.   return tab
  74. end
  75. local function copyTable(tab)
  76.   local toRet = {}
  77.   for a,b in pairs(tab) do
  78.     toRet[a] = b
  79.   end
  80.   return toRet
  81. end
  82. local function checkChannel(num)
  83.   num = tonumber(num)
  84.   if not num then return false end
  85.   if 1 <= num and num <= 65535 then
  86.     return num
  87.   end
  88.   return false
  89. end
  90. local function align(text, xDim)
  91.   text = tostring(text) or ""
  92.   if #text >= xDim then return text end
  93.   for i=1, xDim-#text do
  94.     text = " "..text
  95.   end
  96.   return text
  97. end
  98. local function center(text, xDim)
  99.   xDim = xDim or dim[1] --Temp fix
  100.   local a = (xDim-#text)/2
  101.   for i=1, a do
  102.     text = " "..text.." "
  103.   end
  104.   return text  
  105. end
  106. local function roundNegative(num) --Rounds numbers up to 0
  107.   if num >= 0 then return num else return 0 end
  108. end
  109.  
  110.  
  111. local function testPeripheral(periph, periphFunc)
  112.   if not periph or type(periph) ~= "table" then return false end
  113.   if periph[periphFunc]() == nil then --Expects string because the function could access nil
  114.     periph.isDisconnected = true --My current solution
  115.     return false
  116.   end
  117.   return true
  118. end
  119.  
  120. local function initModem() --Sets up modem, returns true if modem exists
  121.   if not testPeripheral(modem, "isWireless") then
  122.     if peripheral.getType(modemSide or "") == "modem" then
  123.       modem = peripheral.wrap(modemSide)
  124.       return true
  125.     end
  126.     modem = peripheral.find("modem")
  127.     return modem and true or false
  128.   end
  129.   return true
  130. end
  131.  
  132. --COLOR/THEME RELATED
  133. for a, b in pairs(colors) do --This is so commands color commands can be entered in one case
  134.   colors[a:lower()] = b
  135. end
  136.  
  137. local themes = {} --Loaded themes, gives each one a names
  138. local function newTheme(name)
  139.   name = name:lower() or "none"
  140.   local self = {name = name}
  141.   self.addColor = function(self, name, text, back) --Background is optional. Will not change if nil
  142.     name = name or "none"
  143.     self[name] = {text = text, background = back}
  144.     return self --Allows for chaining :)
  145.   end
  146.   themes[name] = self
  147.   return self
  148. end
  149.  
  150. local requiredColors = {"title", "subtitle", "pos", "dim", "extra", "error", "info", "inverse", "command", "help", "background"}
  151.  
  152. --This is how adding colors will work
  153. newTheme("default")
  154.   :addColor("title", colors.green, colors.gray)
  155.   :addColor("subtitle", colors.white, colors.black)
  156.   :addColor("pos", colors.green, colors.black)
  157.   :addColor("dim", colors.lightBlue, colors.black)
  158.   :addColor("extra", colors.lightGray, colors.black)
  159.   :addColor("error", colors.red, colors.white)
  160.   :addColor("info", colors.blue, colors.lightGray)
  161.   :addColor("inverse", colors.yellow, colors.lightGray)
  162.   :addColor("command", colors.lightBlue, colors.black)
  163.   :addColor("help", colors.red, colors.white)
  164.   :addColor("background", colors.white, colors.black)
  165.  
  166. newTheme("random")
  167.   :addColor("title", colors.pink, colors.blue)
  168.   :addColor("subtitle", colors.black, colors.white)
  169.   :addColor("pos", colors.green, colors.black)
  170.   :addColor("dim", colors.lightBlue, colors.black)
  171.   :addColor("extra", colors.lightGray, colors.lightBlue)
  172.   :addColor("error", colors.white, colors.yellow)
  173.   :addColor("info", colors.blue, colors.lightGray)
  174.   :addColor("inverse", colors.yellow, colors.lightGray)
  175.   :addColor("command", colors.green, colors.lightGray)
  176.   :addColor("help", colors.white, colors.yellow)
  177.   :addColor("background", colors.white, colors.red)
  178.  
  179. --If you modify a theme a bunch and want to save it
  180. local function saveTheme(fileName, theme)
  181.   if not theme or not type(fileName) == "string" then return false end
  182.   local file = fs.open(fileName,"w")
  183.   if not file then return false end
  184.   file.writeLine(fileName)
  185.   for a,b in pairs(theme) do
  186.     if type(b) == "table" then --If it contains color objects
  187.       file.writeLine(a.." "..tostring(b.text).." "..tostring(b.background))
  188.     end
  189.   end
  190.   file.close()
  191. end
  192.  
  193.  
  194. --==SCREEN CLASS FUNCTIONS==
  195. local screenClass = {} --This is the class for all monitor/screen objects
  196. screenClass.screens = {} --A simply numbered list of screens
  197. screenClass.sides = {} --A mapping of screens by their side attached
  198. screenClass.channels = {} --A mapping of receiving channels that have screens attached. Used for the receiver part
  199. screenClass.sizes = {{7,18,29,39,50}, {5,12,19} , computer = {51, 19}, turtle = {39,13}, pocket = {26,20}}
  200.  
  201. screenClass.setTextColor = function(self, color) --Accepts raw color
  202.   if color and self.term.isColor() then
  203.     self.textColor = color
  204.     self.term.setTextColor(color)
  205.     return true
  206.   end
  207.   return false
  208. end
  209. screenClass.setBackgroundColor = function(self, color) --Accepts raw color
  210.   if color and self.term.isColor() then
  211.     self.backgroundColor = color
  212.     self.term.setBackgroundColor(color)
  213.     return true
  214.   end
  215.   return false
  216. end
  217. screenClass.setColor = function(self, color) --Wrapper, accepts themecolor objects
  218.   if type(color) ~= "table" then error("Set color received a non-table",2) end
  219.   return self:setTextColor(color.text) and self:setBackgroundColor(color.background)
  220. end
  221.  
  222. screenClass.themeName = "default" --Setting super for fallback
  223. screenClass.theme = themes.default
  224.  
  225.  
  226. screenClass.new = function(side, receive, themeFile)
  227.   local self = {}
  228.   setmetatable(self, {__index = screenClass}) --Establish Hierarchy
  229.   self.side = side
  230.   if side == "computer" then
  231.     self.term = term
  232.   else
  233.     self.term = peripheral.wrap(side)
  234.     if not (self.term and peripheral.getType(side) == "monitor") then --Don't create an object if it doesn't exist
  235.       if doDebug then
  236.         error("No monitor on side "..tostring(side))
  237.       end
  238.       self = nil --Save memory?
  239.       return false
  240.     end
  241.   end
  242.  
  243.   --Channels and ids
  244.   self.receive = tonumber(receive) --Receive Channel
  245.   self.send = nil --Reply Channel, obtained in handshake
  246.   self.id = #screenClass.screens+1
  247.   --Colors
  248.   self.themeName = nil --Will be set by setTheme
  249.   self.theme = nil
  250.   self.isColor = self.term.isColor() --Just for convenience
  251.   --Other Screen Properties
  252.   self.dim = {self.term.getSize()} --Raw dimensions
  253.   --Initializations
  254.   self.isDone = false --Flag for when the turtle is done transmitting
  255.   self.size = {} --Screen Size, assigned in setSize
  256.   self.textColor = colors.white --Just placeholders until theme is loaded and run
  257.   self.backColor = colors.black
  258.   self.toPrint = {}
  259.   self.isComputer = false
  260.   self.isTurtle = false
  261.   self.isPocket = false
  262.   self.acceptsInput = false
  263.   self.legacy = false --Whether it expects tables or strings
  264.   self.rec = { --Initial values for all displayed numbers
  265.     label = "Quarry Bot",
  266.     id = 1,
  267.     percent = 0,
  268.     relxPos = 0,
  269.     zPos = 0,
  270.     layersDone = 0,
  271.     x = 0,
  272.     z = 0,
  273.     layers = 0,
  274.     openSlots = 0,
  275.     mined = 0,
  276.     moved = 0,
  277.     chestFull = false,
  278.     isAtChest = false,
  279.     isGoingToNextLayer = false,
  280.     foundBedrock = false,
  281.     fuel = 0,
  282.     volume = 0,
  283.     distance = 0,
  284.     yPos = 0
  285.     --Maybe add in some things like if going to then add a field
  286.   }
  287.  
  288.   screenClass.screens[self.id] = self
  289.   screenClass.sides[self.side] = self
  290.   if self.receive then
  291.     modem.open(self.receive) --Modem should be defined by the time anything is open
  292.     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
  293.   end
  294.   self:setSize() --Finish Initialization
  295.   self:setTheme(themeFile)
  296.   return self
  297. end
  298.  
  299. screenClass.remove = function(tab) --Cleanup function
  300.   if type(tab) == "number" then --Expects table, can take id (for no apparent reason)
  301.     tab = screenClass.screens[tab]
  302.   end
  303.   if tab == "REMOVED" then return end
  304.   if tab.side == "computer" then error("Tried removing computer screen",2) end --This should never happen
  305.   tab:reset() --Clear screen
  306.   tab:say("Removed", tab.theme.info, 1) --Let everyone know whats up
  307.   screenClass.screens[tab.id] = "REMOVED" --Not nil because screw up len()
  308.   screenClass.sides[tab.side] = nil
  309.   screenClass.channels[tab.receive] = nil
  310.   if modem and modem.isOpen(tab.receive) then
  311.     modem.close(tab.receive)
  312.   end
  313. end
  314.  
  315. --Init Functions
  316. screenClass.setSize = function(self) --Sets screen size
  317.   if self.side ~= "computer" and not self.term then self.term = peripheral.wrap(self.side) end
  318.   if not self.term then --If peripheral is having problems/not there. Don't go further than term, otherwise index nil (maybe?)
  319.     debug("There is no term...")
  320.     self.updateDisplay = function() end --Do nothing on screen update, overrides class
  321.   --elseif not self.receive then --Ironically broken right now
  322.   --  self:setBrokenDisplay() --This will prompt user to set channel
  323.   elseif self.send then --This allows for class inheritance
  324.     self:init() --In case objects have special updateDisplay methods --Remove function in case it exists, defaults to super
  325.   else --If the screen needs to have a handshake display
  326.     self:setHandshakeDisplay()
  327.   end
  328.   self.dim = { self.term.getSize()}
  329.   local tab = screenClass.sizes
  330.   for a=1, 2 do --Want x and y dim
  331.     for b=1, #tab[a] do --Go through all normal sizes, x and y individually
  332.       if tab[a][b] <= self.dim[a] then --This will set size higher until false
  333.         self.size[a] = b
  334.       end
  335.     end
  336.   end
  337.   local function isThing(toCheck, thing) --E.G. isThing(self.dim,"computer")
  338.     return toCheck[1] == tab[thing][1] and toCheck[2] == tab[thing][2]
  339.   end
  340.   self.isComputer = isThing(self.dim, "computer")
  341.   self.isTurtle = isThing(self.dim, "turtle")
  342.   self.isPocket = isThing(self.dim, "pocket")
  343.   self.acceptsInput = self.isComputer or self.isTurtle or self.isPocket
  344.   return self
  345. end
  346.  
  347. screenClass.setTheme = function(self, themeName, stopReset)
  348.   if not themes[themeName] then --If we don't have it already, try to load it
  349.     local fileName = themeName or ".." --.. returns false and I don't think you can name a file this
  350.     if fs.exists(themeFolder) then fileName = themeFolder..fileName end
  351.     if fs.exists(fileName) then
  352.       debug("Loading theme: ",fileName)
  353.       local file = fs.open(fileName, "r")
  354.       if not file then debug("Could not load theme '",themeName,"' file not found") end
  355.       local addedTheme = newTheme(file.readLine() or "newTheme") --Initializes the new theme
  356.       for line in function() return file.readLine() end do --Go through all the color lines (hopefully not the first one. Requires testing)
  357.         local args = {}
  358.         for word in line:gmatch("%S+") do
  359.           table.insert(args,word)
  360.         end
  361.         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
  362.       end
  363.       file.close()
  364.       local flag = true --Make sure a theme has all required elements
  365.       for a,b in ipairs(requiredColors) do
  366.         if not addedTheme[b] then
  367.           flag = false
  368.           debug("Theme is missing color '",b,"'")
  369.         end
  370.       end
  371.       if not flag then
  372.         themes[addedTheme.name] = nil
  373.         debug("Failed to load theme")
  374.         return false
  375.       end
  376.       self.themeName = themeName:lower() --We can now set our themeName to the fileName
  377.     else
  378.       --Resets theme to super
  379.       if not stopReset then --This exists so its possible to set default theme without breaking world
  380.         self.themeName = nil
  381.         self.theme = nil
  382.       end
  383.       return false
  384.     end
  385.    else
  386.     self.themeName = themeName:lower()
  387.    end
  388.    self.theme = themes[self.themeName] --Now the theme is loaded or the function doesn't get here
  389.    return true
  390. end
  391.  
  392. --Adds text to the screen buffer
  393. screenClass.tryAddRaw = function(self, line, text, color, ...) --This will try to add text if Y dimension is a certain size
  394.   local doAdd = {...} --booleans for small, medium, and large
  395.   if type(text) ~= "string" then error("tryAddRaw got non string",2) end
  396.   text = text or "NIL"
  397.   if type(color) ~= "table" then error("tryAddRaw did not get a color",2) end
  398.   --color = color or {text = colors.white}
  399.   for i=1, ySizes do --As of now there are 3 Y sizes
  400.     if doAdd[i] and self.size[2] == i then --If should add this text for this screen size and the monitor is this size
  401.       if #text <= self.dim[1] then
  402.         self.toPrint[line] = {text = text, color = color}
  403.         return true
  404.       else
  405.         debug("Tried adding ",text," on line ",line," but was too long")
  406.       end
  407.     end
  408.   end
  409.   return false
  410. end
  411. screenClass.tryAdd = function(self, text, color,...) --Just a wrapper
  412.   return self:tryAddRaw(#self.toPrint+1, text, color, ...)
  413. end
  414.  
  415. screenClass.reset = function(self,color)
  416.   color = color or self.theme.background
  417.   self:setColor(color)
  418.   self.term.clear()
  419.   self.term.setCursorPos(1,1)
  420. end
  421. screenClass.say = function(self, text, color, line)
  422.   local currColor = self.backgroundColor
  423.   color = color or debug("Printing ",text," but had no themeColor: ",self.theme.name) or {} --Set default for nice error, alert that errors occur
  424.   self:setColor(color)
  425.   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
  426.   if doDebug and #text > self.dim[1] then error("Tried printing: '"..text.."', but was too big") end
  427.   self.term.setCursorPos(1,line)
  428.   for i=1, self.dim[1]-#text do --This is so the whole line's background gets filled.
  429.     text = text.." "
  430.   end
  431.   self.term.write(text)
  432.   self.term.setCursorPos(1, line+1)
  433. end
  434. screenClass.pushScreenUpdates = function(self)
  435.   for i=1, self.dim[2] do
  436.     local tab = self.toPrint[i]
  437.     if tab then
  438.       self:say(tab.text, tab.color, i)
  439.     end
  440.   end
  441. end
  442.  
  443. screenClass.updateNormal = function(self, isDone) --This is the normal updateDisplay function
  444.   local str = tostring
  445.   self.toPrint = {} --Reset table
  446.   local message, theme = self.rec, self.theme
  447.  
  448.   if not message.isDone then --Normally
  449.     if self.size[1] == 1 then --Small Width Monitor
  450.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  451.         self:tryAdd("Quarry!", theme.title, false, false, true)
  452.       end
  453.      
  454.       self:tryAdd("-Fuel-", theme.subtitle , false, true, true)
  455.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  456.         self:tryAdd("A lot", nil, false, true, true)
  457.       end
  458.      
  459.       self:tryAdd("--%%%--", theme.subtitle, false, true, true)
  460.       self:tryAdd(align(str(message.percent).."%", 7), theme.pos , false, true, true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
  461.       self:tryAdd(center(str(message.percent).."%", self.dim[1]), theme.pos, true) --I want it to be centered on 1x1
  462.      
  463.       self:tryAdd("--Pos--", theme.subtitle, false, true, true)
  464.       self:tryAdd("X:"..align(str(message.relxPos), 5), theme.pos, true, true, true)
  465.       self:tryAdd("Z:"..align(str(message.zPos), 5), theme.pos , true, true, true)
  466.       self:tryAdd("Y:"..align(str(message.layersDone), 5), theme.pos , true, true, true)
  467.      
  468.       if not self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true) then --If you can't display the y, then don't
  469.         self:tryAdd(str(message.x).."x"..str(message.z), theme.dim , true)
  470.       end
  471.       self:tryAdd("--Dim--", theme.subtitle, false, true, true)
  472.       self:tryAdd("X:"..align(str(message.x), 5), theme.dim, false, true, true)
  473.       self:tryAdd("Z:"..align(str(message.z), 5), theme.dim, false, true, true)
  474.       self:tryAdd("Y:"..align(str(message.layers), 5), theme.dim, false, true, true)
  475.      
  476.       self:tryAdd("-Extra-", theme.subtitle, false, false, true)
  477.       self:tryAdd(align(textutils.formatTime(os.time()):gsub(" ","").."", 7), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  478.       self:tryAdd("Open:"..align(str(message.openSlots),2), theme.extra, false, false, true)
  479.       self:tryAdd("Dug"..align(str(message.mined), 4), theme.extra, false, false, true)
  480.       self:tryAdd("Mvd"..align(str(message.moved), 4), theme.extra, false, false, true)
  481.       if message.chestFull then
  482.         self:tryAdd("ChstFll", theme.error, false, false, true)
  483.       end
  484.      
  485.     end
  486.     if self.size[1] == 2 then --Medium Monitor
  487.       if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
  488.         self:tryAdd("Quarry!", theme.title, false, false, true)
  489.       end
  490.      
  491.       self:tryAdd("-------Fuel-------", theme.subtitle , false, true, true)
  492.       if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
  493.         toPrint[#toPrint] = nil
  494.         self:tryAdd("A lot", theme.extra, false, true, true)
  495.       end
  496.      
  497.       self:tryAdd(str(message.percent).."% Complete", theme.pos , true, true, true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
  498.      
  499.       self:tryAdd("-------Pos--------", theme.subtitle, false, true, true)
  500.       self:tryAdd("X Coordinate:"..align(str(message.relxPos), 5), theme.pos, true, true, true)
  501.       self:tryAdd("Z Coordinate:"..align(str(message.zPos), 5), theme.pos , true, true, true)
  502.       self:tryAdd("On Layer:"..align(str(message.layersDone), 9), theme.pos , true, true, true)
  503.      
  504.       if not self:tryAdd("Size: "..str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true) then --This is already here... I may as well give an alternative for those people with 1000^3quarries
  505.         self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true)
  506.       end
  507.       self:tryAdd("-------Dim--------", theme.subtitle, false, true, true)
  508.       self:tryAdd("Total X:"..align(str(message.x), 10), theme.dim, false, true, true)
  509.       self:tryAdd("Total Z:"..align(str(message.z), 10), theme.dim, false, true, true)
  510.       self:tryAdd("Total Layers:"..align(str(message.layers), 5), theme.dim, false, true, true)
  511.       self:tryAdd("Volume"..align(str(message.volume),12), theme.dim, false, false, true)
  512.      
  513.       self:tryAdd("------Extras------", theme.subtitle, false, false, true)
  514.       self:tryAdd("Time: "..align(textutils.formatTime(os.time()):gsub(" ","").."", 12), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
  515.       self:tryAdd("Used Slots:"..align(str(16-message.openSlots),7), theme.extra, false, false, true)
  516.       self:tryAdd("Blocks Mined:"..align(str(message.mined), 5), theme.extra, false, false, true)
  517.       self:tryAdd("Spaces Moved:"..align(str(message.moved), 5), theme.extra, false, false, true)
  518.       if message.chestFull then
  519.         self:tryAdd("Chest Full, Fix It", theme.error, false, true, true)
  520.       end
  521.     end
  522.     if self.size[1] >= 3 then --Large or larger screens
  523.       if not self:tryAdd(message.label..align(" Turtle #"..str(message.id),self.dim[1]-#message.label), theme.title, true, true, true) then
  524.         self:tryAdd("Your turtle's name is long...", theme.title, true, true, true)
  525.       end
  526.       self:tryAdd("Fuel: "..align(str(message.fuel),self.dim[1]-6), theme.extra, true, true, true)
  527.      
  528.       self:tryAdd("Percentage Done: "..align(str(message.percent).."%",self.dim[1]-17), theme.pos, true, true, true)
  529.      
  530.       local var1 = math.max(#str(message.x), #str(message.z), #str(message.layers))
  531.       local var2 = (self.dim[1]-6-var1+3)/3
  532.       self:tryAdd("Pos: "..align(" X:"..align(str(message.relxPos),var1),var2)..align(" Z:"..align(str(message.zPos),var1),var2)..align(" Y:"..align(str(message.layersDone),var1),var2), theme.pos, true, true, true)
  533.       self:tryAdd("Size:"..align(" X:"..align(str(message.x),var1),var2)..align(" Z:"..align(str(message.z),var1),var2)..align(" Y:"..align(str(message.layers),var1),var2), theme.dim, true, true, true)
  534.       self:tryAdd("Volume: "..str(message.volume), theme.dim, false, true, true)
  535.       self:tryAdd("",{}, false, false, true)
  536.       self:tryAdd(center("____---- EXTRAS ----____",self.dim[1]), theme.subtitle, false, false, true)
  537.       self:tryAdd(center("Time:"..align(textutils.formatTime(os.time()),8), self.dim[1]), theme.extra, false, true, true)
  538.       self:tryAdd(center("Current Day: "..str(os.day()), self.dim[1]), theme.extra, false, false, true)
  539.       self:tryAdd("Used Inventory Slots: "..align(str(16-message.openSlots),self.dim[1]-22), theme.extra, false, true, true)
  540.       self:tryAdd("Blocks Mined: "..align(str(message.mined),self.dim[1]-14), theme.extra, false, true, true)
  541.       self:tryAdd("Blocks Moved: "..align(str(message.moved),self.dim[1]-14), theme.extra, false, true, true)
  542.       self:tryAdd("Distance to Turtle: "..align(str(message.distance), self.dim[1]-20), theme.extra, false, false, true)
  543.       self:tryAdd("Actual Y Pos (Not Layer): "..align(str(message.yPos), self.dim[1]-26), theme.extra, false, false, true)
  544.      
  545.       if message.chestFull then
  546.         self:tryAdd("Dropoff is Full, Please Fix", theme.error, false, true, true)
  547.       end
  548.       if message.foundBedrock then
  549.         self:tryAdd("Found Bedrock! Please Check!!", theme.error, false, true, true)
  550.       end
  551.       if message.isAtChest then
  552.         self:tryAdd("Turtle is at home chest", theme.info, false, true, true)
  553.       end
  554.       if message.isGoingToNextLayer then
  555.         self:tryAdd("Turtle is going to next layer", theme.info, false, true, true)
  556.       end
  557.     end
  558.   else --If is done
  559.     if screenSize[1] == sizesEnum.small then --Special case for small monitors
  560.       self:tryAdd("Done", theme.title, true, true, true)
  561.       self:tryAdd("Dug"..align(str(message.mined),4), theme.pos, true, true, true)
  562.       self:tryAdd("Fuel"..align(str(message.fuel),3), theme.pos, true, true, true)
  563.       self:tryAdd("-------", theme.subtitle, false,true,true)
  564.       self:tryAdd("Turtle", theme.subtitle, false, true, true)
  565.       self:tryAdd(center("is", self.dim[1]), theme.subtitle, false, true, true)
  566.       self:tryAdd(center("Done!", self.dim[1]), theme.subtitle, false, true, true)
  567.     else
  568.       self:tryAdd("Done!", theme.title, true, true, true)
  569.       self:tryAdd("Blocks Dug: "..str(message.mined), theme.inverse, true, true, true)
  570.       self:tryAdd("Cobble Dug: "..str(message.cobble), theme.pos, false, true, true)
  571.       self:tryAdd("Fuel Dug: "..str(message.fuelblocks), theme.pos, false, true, true)
  572.       self:tryAdd("Others Dug: "..str(message.other), theme.pos, false, true, true)
  573.       self:tryAdd("Curr Fuel: "..str(message.fuel), theme.inverse, true, true, true)
  574.     end
  575.   end
  576. end
  577. screenClass.updateHandshake = function(self)
  578.   self.toPrint = {}
  579.   local half = math.ceil(self.dim[2]/2)
  580.   if self.size[1] == 1 then --Not relying on the parameter system because less calls
  581.     self:tryAddRaw(half-2, "Waiting", self.theme.error, true)
  582.     self:tryAddRaw(half-1, "For Msg", self.theme.error, true)
  583.     self:tryAddRaw(half, "On Chnl", self.theme.error, true)
  584.     self:tryAddRaw(half+1, tostring(self.receive), self.theme.error, true)
  585.   else
  586.     local str = "for"
  587.     if self.size[1] == 2 then str = "4" end--Just a small grammar change
  588.     self:tryAddRaw(half-2, "", self.theme.error, true, true, true) --Filler
  589.     self:tryAddRaw(half-1, center("Waiting "..str.." Message", self.dim[1]), self.theme.error, true, true, true)
  590.     self:tryAddRaw(half, center("On Channel "..tostring(self.receive), self.dim[1]), self.theme.error, true, true, true)
  591.     self:tryAddRaw(half+1, "",self.theme.error, true, true, true)
  592.   end
  593. end
  594. screenClass.updateBroken = function(self) --If screen needs channel
  595.   self.toPrint = {}
  596.   self:tryAdd("On Comp", self.theme.error, true, true, true)
  597.   sefl:tryAdd("Type:", self.theme.error, true, true, true)
  598.   self:tryAdd("RECEIVE",self.theme.info, true, true, true)
  599.   local lolChaining = self:tryAdd(self.side, self.theme.info, true, true, true) or self:tryAdd("[side]",self.theme.info, true, true, true) --Simpler than if else
  600.   self:tryAdd("[Chnl]", self.theme.info, true, true, true)
  601. end
  602.  
  603. screenClass.updateDisplay = screenClass.updateNormal --Update screen method is normally this one
  604.  
  605. --Misc
  606. screenClass.init = function(self) --Currently used by computer screen to replace its original method
  607.   self.updateDisplay = self.updateNormal --This defaults to super if doesn't exist
  608. end
  609. screenClass.setHandshakeDisplay = function(self)
  610.   self.updateDisplay = self.updateHandshake --Sets update to handshake version, defaults to super if doesn't exist
  611. end
  612. screenClass.setBrokenDisplay = function(self)
  613.   self.updateDisplay = self.updateBroken
  614. end
  615.  
  616.  
  617. local function wrapPrompt(prefix, str, dim) --Used to wrap the commandString
  618.   return prefix..str:sub(roundNegative(#str+#prefix-computer.dim[1]+2), -1).."_" --it is str + 2 because we add in the "_"
  619. end
  620.  
  621. local function updateAllScreens()
  622.   for a, b in pairs(screenClass.sides) do
  623.     b:updateDisplay()
  624.     b:reset()
  625.     b:pushScreenUpdates()
  626.   end
  627. end
  628. --Rednet
  629. local function newMessageID()
  630.   return math.random(1,2000000000) --1 through 2 billion. Good enough solution
  631. end
  632. local function transmit(send, receive, message, legacy, fingerprint)
  633.   fingerprint = fingerprint or replyFingerprint
  634.   if legacy then
  635.     modem.transmit(send, receive, message)
  636.   else
  637.     modem.transmit(send, receive, {message = message, id = newMessageID(), fingerprint = fingerprint})
  638.   end
  639. end
  640.  
  641. --==SET UP==
  642. clearScreen()
  643. print("Welcome to Quarry Receiver!")
  644. sleep(1)
  645.  
  646. while not initModem() do
  647.   clearScreen()
  648.   print("No modem is connected, please attach one")
  649.   os.pullEvent("peripheral")
  650. end
  651. debug("Modem successfully connected!")
  652.  
  653. --==ARGUMENTS==
  654.  
  655. --[[
  656. Parameters:
  657.   -help/-?/help/?
  658.   -receiveChannel [channel] --For only the main screen
  659.   -theme --Sets a default theme
  660.   -screen [side] [channel] [theme]
  661.   -station
  662.   -auto --Prompts for all sides, or you can supply a list of receive channels for random assignment!
  663. ]]
  664.  
  665. --tArgs and peripheral list init
  666. local tArgs = {...}
  667. local parameters = {} --Each command is stored with arguments
  668. local parameterIndex = 0 --So we can add new commands to the right table
  669. for a,b in ipairs(tArgs) do
  670.   val = b:lower()
  671.   if val == "help" or val == "-help" or val == "?" or val == "-?" then
  672.     displayHelp() --To make
  673.     error("The End of Help",0)
  674.   end
  675.   if val:match("^%-") then
  676.     parameterIndex = parameterIndex + 1
  677.     parameters[parameterIndex] = {val:sub(2)} --Starts a chain with the command. Can be unpacked later
  678.     parameters[val:sub(2)] = {} --Needed for force/before/after parameters
  679.   elseif parameterIndex ~= 0 then
  680.     table.insert(parameters[parameterIndex], b) --b because arguments should be case sensitive for filenames
  681.     table.insert(parameters[parameters[parameterIndex][1]], b) --Needed for force/after parameters
  682.   end
  683. end
  684.  
  685. for i=1,#parameters do
  686.   debug("Parameter: ",parameters[i][1])
  687. end
  688.  
  689. --Options before screen loads
  690. if parameters.theme then
  691.   screenClass:setTheme(parameters.theme[1])
  692. end
  693.  
  694. --Init Computer Screen Object (was defined at top)
  695. computer = screenClass.new("computer", parameters.receivechannel and parameters.receivechannel[1])--This sets channel, checking if parameter exists
  696.  
  697. computer.displayCommand = function(self)
  698.   local sideString = ((defaultSide and " (") or "")..(defaultSide or "")..((defaultSide and ")") or "")
  699.   self:tryAddRaw(self.dim[2], wrapPrompt("Cmd"..sideString:sub(2,-2)..": ", commandString, self.dim[1]), self.theme.command, true, true, false)
  700.   self:tryAddRaw(self.dim[2], wrapPrompt("Command"..sideString..": ",commandString, self.dim[1]), self.theme.command, false, false, true) --This displays the last part of a string.
  701. end
  702. --Technically, you could have any screen be the station, but oh well.
  703. if parameters.station then --This will set the screen update to display stats on all other monitors. For now it does little
  704.   screenClass.receiveChannels[computer.receive] = nil --Because it doesn't have a channel
  705.   computer.receive = -1 --So it doesn't receive messages
  706.   computer.send = nil
  707.    
  708.   computer.updateNormal = function(self)--This gets set in setSize
  709.     for a, b in pairs(screenClass.sides) do
  710.       self:tryAdd("Side: ", a," ",b.id," ",b.receive, theme.pos, false, true, true) --Prints info about all screens
  711.     end
  712.     computer:displayCommand()
  713.   end
  714.   computer.setHandshakeDisplay = computer.init --Handshake is same as regular
  715. else --If computer is a regular screen
  716.   computer.updateNormal = function(self, isDone)
  717.     screenClass.updateDisplay(self, isDone)
  718.     computer:displayCommand()
  719.   end
  720.   computer.updateHandshake = function(self) --Not in setHandshake because that func checks object updateHandshake
  721.     screenClass.updateHandshake(self)
  722.     computer:displayCommand()
  723.   end
  724.   computer.updateBroken = function(self)
  725.     screenClass.updateBroken(self)
  726.     computer:displayCommand()
  727.   end
  728. end
  729. computer:setSize() --Update changes made to display functions
  730.  
  731.  
  732. for i=1, #parameters do --Do actions for parameters that can be used multiple times
  733.   local command, args = parameters[i][1], parameters[i] --For ease
  734.   if command == "screen" then
  735.     local a = screenClass.new(args[2], args[3], args[4])
  736.     debug(type(a))
  737.   end
  738.  
  739. end
  740.  
  741. for a,b in pairs(screenClass.sides) do debug(a) end
  742.  
  743. if parameters.auto then
  744.   local tab = peripheral.getNames()
  745.   for i=1, #tab do
  746.     if peripheral.getType(tab[i]) == "modem" then
  747.       screenClass.new(tab[i], parameters.auto[i]) --You can specify a list of channels in "auto" parameter
  748.     end
  749.   end
  750. end
  751.  
  752.  
  753. --==FINAL CHECKS==
  754.  
  755. --Making sure all screens have a channel
  756. for a, b in pairs(screenClass.sides) do
  757.   while not b.receive do
  758.     clearScreen()
  759.     print("Screen ",a," has no channel")
  760.     print("Enter the channel from the turtle!")
  761.     local input = tonumber(read())
  762.     if checkChannel(input) then
  763.       if not screenClass.channels[input] then
  764.         b.receive = input
  765.         screenClass.channels[input] = b
  766.       else
  767.         print("That channel has already been taken")
  768.       end
  769.     else
  770.       print("That is not a valid number")
  771.     end
  772.     sleep(0.5)
  773.   end
  774.   if not modem.isOpen(b.receive) then modem.open(b.receive) end
  775.   b:updateDisplay()--Finish initialization process
  776.   b:reset()
  777.   b:pushScreenUpdates()
  778. end
  779. --Handshake will be handled in main loop
  780.  
  781. --[[Workflow
  782.   Wait for events
  783.   modem_message
  784.     if valid channel and valid message, update appropriate screen
  785.   key
  786.     if any letter, add to command string if room.
  787.     if enter key
  788.       if valid self command, execute command. Commands:
  789.         command [side] [command] --If only one screen, then don't need channel. Send a command to a turtle
  790.         screen [side] [channel] [theme] --Links a new screen to use.
  791.         remove [side] --Removes a screen
  792.         theme [themeName] --Sets the default theme
  793.         theme [side] [themeName] --Changes this screen's theme
  794.         savetheme [new name] [themeName]
  795.         color [side/theme] [colorName] [textColor] [backgroundColor]
  796.         side [side] --Sets a default side, added to prompts
  797.         set [string] --Sets a default command, added to display immediately
  798.         receive [side] [newChannel] --Changes the channel of the selected screen
  799.         send [side] [newChannel]
  800.         exit/quit/end
  801.   peripheral_detach
  802.     check what was lost, if modem, set to nil. If screen side, do screen:setSize()
  803.   peripheral
  804.     check if need modem, then set modem
  805.     prompt link screen?
  806.   monitor_resize
  807.     resize proper screen
  808.  
  809. ]]
  810.  
  811. --Modes: 1 - Sided, 2 - Not Sided, 3 - Both sided and not
  812. 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}
  813. while continue do
  814.   local event, par1, par2, par3, par4, par5 = os.pullEvent()
  815.   ----MESSAGE HANDLING----
  816.   if event == "modem_message" and screenClass.channels[par2] then --If we got a message for a screen that exists
  817.     local screen = screenClass.channels[par2] --For convenience
  818.     if not screen.send then --This is the handshake
  819.       debug("\nChecking handshake. Received: ",par4)
  820.       local flag = false
  821.       if par4 == expectedMessage then
  822.         screen.legacy = true --Accepts serialized tables
  823.         flag = true
  824.       elseif type(par4) == "table" and par4.message == expectedMessage and par4.fingerprint == expectedFingerprint then
  825.         screen.legacy = false
  826.         flag = true
  827.       end
  828.      
  829.       if flag then
  830.         debug("Screen ",screen.side," received a handshake")
  831.         screen.send = par3
  832.         screen:setSize() --Resets update method to proper since channel is set
  833.         debug("Sending back on ",screen.send)
  834.         transmit(screen.send,screen.receive, replyMessage)
  835.       end
  836.    
  837.     else --Everything else is for regular messages
  838.      
  839.       local rec
  840.       if screen.legacy then --We expect strings here
  841.         if type(par4) == "string" then --Otherwise its not ours
  842.           if par4 == "stop" then --This is the stop message. All other messages will be ending ones
  843.             screen.isDone = true
  844.           elseif textutils.unserialize(par4) then
  845.             rec = textutils.unserialize(par4)
  846.             rec.distance = par5
  847.           end
  848.         end
  849.       elseif type(par4) == "table" and par4.fingerprint == expectedFingerprint then --Otherwise, we check if it is valid message
  850.         rec = par4.message
  851.         if not type(rec) == "table" then debug("Message received did not contain table") end
  852.         if not par4.distance then --This is cool because it can add distances from the repeaters
  853.           rec.distance = par5
  854.         else
  855.           rec.distance = par4.distance + par5
  856.         end
  857.         if rec.isDone then
  858.           screen.isDone = true
  859.         end
  860.       end
  861.        
  862.       if rec then
  863.         rec.distance = math.floor(rec.distance)
  864.         rec.label = rec.label or "Quarry!"
  865.         screen.rec = rec --Set the table
  866.         --Updating screen occurs outside of the if
  867.         local toSend
  868.         if queuedMessage then
  869.           toSend = queuedMessage
  870.           queuedMessage = nil
  871.         else
  872.           toSend = replyMessage
  873.         end
  874.         transmit(screen.send,screen.receive, toSend, screen.legacy) --Send reply message for turtle
  875.       end
  876.      
  877.     end
  878.    
  879.     screen:updateDisplay() --isDone is queried inside this
  880.     screen:reset(screen.theme.background)
  881.     screen:pushScreenUpdates() --Actually write things to screen
  882.    
  883.  
  884.   ----KEY HANDLING----
  885.   elseif event == "key" and keyMap[par1] then
  886.     local key = par1
  887.     if key ~= keys.enter then --If we aren't submitting a command
  888.       if key == keys.backspace then
  889.         if #commandString > 0 then
  890.           commandString = commandString:sub(1,-2)
  891.         end
  892.       else
  893.         commandString = commandString..keyMap[key]
  894.       end
  895.     else --If we are submitting a command
  896.       local args = {}
  897.       for a in commandString:gmatch("%S+") do
  898.         args[#args+1] = a:lower()
  899.       end
  900.       local command = args[1]
  901.       if validCommands[command] then --If it is a valid command...
  902.         local commandType = validCommands[command]
  903.         if commandType == 1 or commandType == 3 then --If the command requires a "side" like transmitting commands, versus setting a default
  904.           if defaultSide then table.insert(args, 2, defaultSide) end
  905.           local screen = screenClass.sides[args[2]]
  906.           if screen then --If the side exists
  907.             if command == "command" and screen.send then --If sending command to the turtle
  908.               queuedMessage = table.concat(args," ", 3) --Tells message handler to send appropriate message
  909.               --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
  910.             end
  911.  
  912.             if command == "color" then
  913.               screen.theme:addColor(args[3],colors[args[4] or ""],colors[args[5] or ""])
  914.               updateAllScreens() --Because we are changing a theme color which others may have
  915.             end
  916.             if command == "theme" then
  917.               screen:setTheme(args[3])
  918.             end
  919.             if command == "send" then --This changes a send channel, and can also revert to handshake
  920.               local chan = checkChannel(tonumber(args[3]) or -1)
  921.               if chan then screen.send = chan else screen.send = nil end
  922.               screen:setSize() --If on handshake, resets screen
  923.             end
  924.             if command == "receive" then
  925.               local chan = checkChannel(tonumber(args[3]) or -1)
  926.               if chan and not screenClass.channels[chan] then
  927.                 modem.close(screen.receive)
  928.                 modem.open(chan)
  929.                 screenClass.channels[screen.receive] = nil
  930.                 screen.receive = chan
  931.                 screenClass.channels[chan] = screen
  932.               end
  933.             end
  934.            
  935.             if command == "remove" and screen.side ~= "computer" then --We don't want to remove the main display!
  936.               print()
  937.               screen:remove()
  938.             else --Because if removed it does stupid things
  939.               screen:reset()
  940.               screen:updateDisplay()
  941.               screen:pushScreenUpdates()
  942.             end
  943.           end
  944.         end
  945.         if commandType == 2 or commandType == 3 then--Does not require a screen side
  946.           if command == "screen" and peripheral.getType(args[2]) == "monitor" then
  947.             if checkChannel(tonumber(args[3])) then
  948.               local mon = screenClass.new(args[2], args[3], args[4]) --args[4] is the theme, and will default if doesn't exists
  949.               mon:setSize()
  950.               mon:updateDisplay()
  951.               mon:reset()
  952.               mon:pushScreenUpdates()
  953.             else
  954.               computer:tryAddRaw(computer.dim[2]-1, "Could not add, invalid channel: "..args[3], computer.theme.error, false, false, true)
  955.               computer:reset()
  956.               computer:pushScreenUpdates()
  957.               sleep(2) --So they can read this message
  958.             end
  959.           end
  960.           if command == "theme" then
  961.             screenClass:setTheme(args[2], true) --Otherwise this would set base theme to nil, erroring
  962.             updateAllScreens()
  963.           end
  964.           if command == "color" and themes[args[2]] then
  965.             themes[args[2]]:addColor(args[3],colors[args[4]],colors[args[5]])
  966.             updateAllScreens() --Because any screen could have this theme
  967.           end
  968.           if command == "side" then
  969.             if screenClass.sides[args[2]] then
  970.               defaultSide = args[2]
  971.             else
  972.               defaultSide = nil
  973.             end
  974.           end
  975.           if command == "set" then
  976.             if args[2] then
  977.               defaultCommand = table.concat(args," ",2)
  978.               defaultCommand = defaultCommand:upper()
  979.             else
  980.               defaultCommand = nil
  981.             end
  982.           end
  983.           if command == "savetheme" then
  984.             saveTheme(args[2], themes[args[3]])
  985.           end
  986.           if command == "quit" or command == "exit" or command == "end" then
  987.             continue = false
  988.           end
  989.         end
  990.       else
  991.         debug("\nInvalid Command")
  992.       end
  993.       if defaultCommand then commandString = defaultCommand.." " else commandString = "" end --Reset command string because it was sent
  994.     end
  995.    
  996.    
  997.     --Update computer display (computer is only one that displays command string
  998.     computer:updateDisplay() --Note: Computer's method automatically adds commandString to last line
  999.     if not continue then computer:tryAddRaw(computer.dim[2]-1,"Program Exiting", computer.theme.inverse, false, true, true) end
  1000.     computer:reset()
  1001.     computer:pushScreenUpdates()
  1002.    
  1003.   elseif event == "monitor_resize" then
  1004.     local screen = screenClass.sides[par1]
  1005.     if screen then
  1006.       screen:setSize()
  1007.       screen:updateDisplay()
  1008.       screen:reset()
  1009.       screen:pushScreenUpdates()
  1010.     end
  1011.  
  1012.   elseif event == "peripheral_detach" then
  1013.     local screen = screenClass.sides[par1]
  1014.     if screen then
  1015.       screen:remove()
  1016.     end
  1017.    
  1018.   elseif event == "peripheral" then
  1019.     --Maybe prompt to add a new screen? I don't know
  1020.  
  1021.   end
  1022.  
  1023.  
  1024.  
  1025.  
  1026.  
  1027. end
  1028.  
  1029. sleep(1.5)
  1030. for a in pairs(screenClass.channels) do
  1031.   modem.close(a)
  1032. end
  1033. for a, b in pairs(screenClass.sides) do
  1034.   b:setTextColor(colors.white)
  1035.   b:setBackgroundColor(colors.black)
  1036.   b.term.clear()
  1037.   b.term.setCursorPos(1,1)
  1038. end
  1039.  
  1040. local text --Fun :D
  1041. if computer.isComputer then text = "SUPER COMPUTER OS 9000"
  1042. elseif computer.isTurtle then text = "SUPER DIAMOND-MINING OS XXX"
  1043. elseif computer.isPocket then text = "PoCkEt OOS AMAYZE 65"
  1044. end
  1045. if text then
  1046.   computer:say(text, computer.theme.title,1)
  1047. end
  1048. --Down here shut down all the channels, remove the saved file, other cleanup stuff
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement