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