/
quarryReceiver.lua
1806 lines (1626 loc) · 69.1 KB
/
quarryReceiver.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
--Quarry Receiver Version 3.6.4
--Made by Civilwargeeky
--[[
Recent Changes:
Completely Remade!
]]
--Config
local doDebug = false --For testing purposes
local ySizes = 3 --There are 3 different Y Screen Sizes right now
local quadEnabled = false --This is for the quadrotors mod by Lyqyd
local autoRestart = false --If true, will reset screens instead of turning them off. For when reusing turtles.
--Initializing Program-Wide Variables
local expectedMessage = "Civil's Quarry" --Expected initial message
local expectedFingerprint = "quarry"
local replyMessage = "Turtle Quarry Receiver" --Message to respond to handshake with
local replyFingerprint = "quarryReceiver"
local stopMessage = "stop"
local expectedFingerprint = "quarry"
local themeFolder = "quarryResources/receiverThemes/"
local modemSide --User can specify a modem side, but it is not necessary
local modem --This will be the table for the modem
local computer --The main screen is special. It gets defined first :3
local continue = true --This keeps the main while loop going
local quadDirection = "north"
local quadDirections = {n = "north", s = "south", e = "east", w = "west"}
local quadBase, computerLocation
local tArgs = {...}
--These two are used by controller in main loop
local commandString = "" --This will be a command string sent to turtle. This var is stored for display
local lastCommand --If a command needs to be sent, this gets set
local defaultSide
local defaultCommand
local stationsList = {}
for i=1, #tArgs do --Parameters that must be set before rest of program for proper debugging
local val = tArgs[i]:lower()
if val == "-v" or val == "-verbose" then
doDebug = true
end
if val == "-q" or val == "-quiet" then
doDebug = false
end
end
local keyMap = {[57] = " ", [11] = "0", [12] = "_", [52] = ".", [82] = "0", [83] = "."} --This is for command string
for i=2,10 do keyMap[i] = tostring(i-1) end --Add top numbers
for a=0,2 do --All the numpad numbers
for b=0,2 do
keyMap[79-(4*a)+b] = tostring(b + 1 + a*3) --Trust me, this just works
end
end
for a,b in pairs(keys) do --Add all letters from keys api
if #a == 1 then
keyMap[b] = a:upper()
end
end
keyMap[keys.enter] = "enter"
keyMap[156] = "enter" --Numpad enter
keyMap[keys.backspace] = "backspace"
keyMap[200] = "up"
keyMap[208] = "down"
keyMap[203] = "left"
keyMap[205] = "right"
local helpResources = { --$$ is a new page
main = [[$$Hello and welcome to Quarry Receiver Help!
This goes over everything there is to know about the receiver
Use the arrow keys to navigate!
Press '0' to come back here!
Press 'q' to quit!
Press a section number at any time to go the beginning of that section
1. Basic Use
2. Parameters
3. Commands
4. Turtle Commands
$$A secret page!
You found it! Good job :)
]],
[[$$Your turtle and you!
To use this program, you need a wireless modem on both this computer and the turtle
Make sure they are attached to both the turtle and this computer
$$Using your new program!
Once you have done that, start the turtle and when it says "Rednet?", say "Yes"
Optionally, you can use the parameter "-rednet true"
Then remember the channel it tells you to open.
Come back to this computer, and run the program. Follow onscreen directions.
Optionally, you can use the parameter "-receiveChannel"
Check out the other help sections for more parameters
$$Adding Screens!
You can add screens with the "-screen" parameter or the "SCREEN" command
An example would be "SCREEN LEFT 2 BLUE" for a screen on the left side with channel 2 and blue theme
You can connect screens over wired modems. Attach a modem to the computer and screen, then right click the modem attached to the screen.
Then you can say "SCREEN MONITOR_0" or whatever it says
]],
[[$$Parameters!
note: <> means required, [] means optional
-help/help/-?/?/-usage/usage: That's this!
-autoRestart [t/f]: If true, the receiver will not exit when all quarries are done and will automagically reconnect to new quarries
With no argument, this is set to true.
-receiveChannel/channel <channel>: Sets the main screen's receive channel
-theme <name>: sets the "default" theme that screens use when they don't have a set theme
$$Parameters!
note: <> means required, [] means optional
-screen <side> [channel] [theme]: makes a new screen on the given side with channel and theme
example: -screen left 10 blue This adds a new screen on the left receiving channel 10 with a blue theme.
-station [side]: makes the screen a "station" that monitors all screens.
if no side, uses computer
-auto [channel list]: This finds all attached monitors and initializes them (with channels)
example: -auto 1 2 5 9 finds screens and gives them channels
$$Parameters!
note: <> means required, [] means optional
-colorEditor: makes the main screen a color editor that just prints the current colors. Good for theme making
current typeColors: default title, subtitle, pos, dim, extra, error, info, inverse, command, help, background
-modem <side>: Sets the modem side to side
-v/-verbose: turns on debug
-q/-quiet: turns off debug
]],
[[$$Commands!
COMMAND [screen] [text]: Sends text to the connected turtle. See turtle commands for valid commands
SCREEN [side] [channel] [theme]: Adds a screen. You can also specify the channel and theme of the screen.
REMOVE [screen]: Removes the selected screen (cannot remove the main screen)
THEME [screen] [name]: Sets the theme of the given screen. THEME [screen] resets the screen to default theme
$$Commands!
THEME [name]: Sets the default theme.
RECEIVE [screen] [channel]: Changes the receive channel of the given screen
SEND [screen] [channel]: Changes the send channel of the given screen (for whatever reason)
STATION [screen] [channel]: Sets the given screen to/from a station. If changing from a station, will set the screen's channel
$$Commands!
SET [text]: Sets a default command that can be backspaced. Useful for color editing or command sending
Use SET with nothing after to remove text
SIDE [screen]: Sets a default screen for "sided" commands.
Any command that takes a [screen] is sided
EXIT/QUIT: Quits the program gracefully
$$Commands!
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
SAVETHEME [themeName] [fileName]: Saves the given theme as fileName for later use
SAVETHEME [screen] [fileName]: Same as above but for a screen's theme
AUTO [channelList]: Automatically searches for nearby screens, providing them sequentially with channels if a channel list is given
Example Use: AUTO 1 2 5 9
$$Commands!
HELP: Displays this again!
VERBOSE: Turns debug on
QUIET: Turns debug off
]],
[[$$Turtle Commands!
Stop: Stops the turtle where it is
Return: The turtle will return to its starting point, drop off its load, and stop
Drop: Turtle will immediately go and drop its inventory
Pause: Pauses the turtle
Resume: Resumes paused turtles
Refuel: Turtle will schedule an emergency refuel
This could take from fuelChest, or quadCopter
or fuel in inventory (in that order)
]]
}
--Generic Functions--
local function debug(...)
--if doDebug then return print(...) end --Basic
if doDebug then
print("\nDEBUG: ",...)
os.pullEvent("char")
end
end
local function clearScreen(x,y, periph)
periph, x, y = periph or term, x or 1, y or 1
periph.clear()
periph.setCursorPos(x,y)
end
local function swapKeyValue(tab)
for a,b in pairs(tab) do
tab[b] = a
end
return tab
end
local function copyTable(tab)
local toRet = {}
for a,b in pairs(tab) do
toRet[a] = b
end
return toRet
end
local function checkChannel(num)
num = tonumber(num)
if not num then return false end
if 1 <= num and num <= 65535 then
return num
end
return false
end
local function truncate(text, xDim)
if #text <= xDim then return text end
return #text >= 4 and text:sub(1,xDim-3).."..." or text:sub(1,3)
end
local function align(text, xDim, direction, trunc)
text = tostring(text or "None")
if trunc == nil then trunc = true end
if #text >= xDim and trunc then return truncate(text,xDim) end
for i=1, xDim-#text do
if direction == "right" then
text = " "..text
elseif direction == "left" then
text = text.." "
end
end
return text
end
local function alignR(text, xDim, trunc)
return align(text, xDim, "right", trunc)
end
local function alignL(text, xDim, trunc)
return align(text, xDim, "left", trunc)
end
local function center(text, xDim, char)
if not xDim then error("Center: No dim given",2) end
char = char or " "
local a = (xDim-#text)/2
for i=1, a do
text = char..text..char
end
return #text == xDim and text or text..char --If not full length, add a space
end
local function leftRight(first, second, dim)
return alignL(tostring(first),dim-#tostring(second))..tostring(second)
end
local function roundNegative(num) --Rounds numbers up to 0
if num >= 0 then return num else return 0 end
end
local function testPeripheral(periph, periphFunc)
if type(periph) ~= "table" then return false end
if type(periph[periphFunc]) ~= "function" then return false end
if periph[periphFunc]() == nil then --Expects string because the function could access nil
return false
end
return true
end
local function initModem() --Sets up modem, returns true if modem exists
if not testPeripheral(modem, "isWireless") then
if modemSide then
if peripheral.getType(modemSide) == "modem" then
modem = peripheral.wrap(modemSide)
if modem.isWireless and not modem.isWireless() then --Apparently this is a thing
modem = nil
return false
end
return true
end
end
if peripheral.find then
modem = peripheral.find("modem", function(side, obj) return obj.isWireless() end)
end
return modem and true or false
end
return true
end
--COLOR/THEME RELATED
for a, b in pairs(colors) do --This is so commands color commands can be entered in one case
colors[a:lower()] = b
end
colors.none = 0 --For adding things
local requiredColors = {"default","title", "subtitle", "pos", "dim", "extra", "error", "info", "inverse", "command", "help", "background"}
local function checkColor(name, text, back) --Checks if a given color works
local flag = false
for a, b in ipairs(requiredColors) do
if b == name then
flag = true
break
end
end
if not flag or not (tonumber(text) or colors[text]) or not (tonumber(back) or colors[back]) then return false end
return true
end
local themes = {} --Loaded themes, gives each one a names
local function newTheme(name)
name = name:lower() or "none"
local self = {name = name}
self.addColor = function(self, colorName, text, back) --Colors are optional. Will default to "default" value. Make sure default is a color
if colorName == "default" and (not text or not back) then return self end
if not text then text = 0 end
if not back then back = 0 end
if not checkColor(colorName, text, back) then debug("Color check failed: ",name," ",text," ",back); return self end --Back or black because optional
colorName = colorName or "none"
self[colorName] = {text = text, background = back}
return self --Allows for chaining :)
end
themes[name] = self
return self
end
local function parseTheme(file)
local addedTheme = newTheme(file:match("^.-\n") or "newTheme") --Initializes the new theme to the first line
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
for line in file:gmatch("[^\n]+\n") do --Go through all the color lines (besides first one)
local args = {}
for word in line:gmatch("%S+") do
table.insert(args,word)
end
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
end
local flag = true --Make sure a theme has all required elements
for a,b in ipairs(requiredColors) do
if not addedTheme[b] then
flag = false
debug("Theme is missing color '",b,"'")
end
end
if not flag then
themes[addedTheme.name] = nil
debug("Failed to load theme")
return false
end
return addedTheme
end
--This is how adding colors will work
--regex for adding from file:
--(\w+) (\w+) (\w+)
-- \:addColor\(\"\1\"\, \2\, \3\)
newTheme("default")
:addColor("default",colors.white, colors.black)
:addColor("title", colors.green, colors.gray)
:addColor("subtitle", colors.white, colors.black)
:addColor("pos", colors.green, colors.black)
:addColor("dim", colors.lightBlue, colors.black)
:addColor("extra", colors.lightGray, colors.black)
:addColor("error", colors.red, colors.white)
:addColor("info", colors.blue, colors.lightGray)
:addColor("inverse", colors.yellow, colors.blue)
:addColor("command", colors.lightBlue, colors.black)
:addColor("help", colors.cyan, colors.black)
:addColor("background", colors.none, colors.none)
newTheme("blue")
:addColor("default",colors.white, colors.blue)
:addColor("title", colors.lightBlue, colors.gray)
:addColor("subtitle", 1, 2048)
:addColor("pos", 16, 2048)
:addColor("dim", colors.lime, 0)
:addColor("extra", 8, 2048)
:addColor("error", 8, 16384)
:addColor("info", 2048, 256)
:addColor("inverse", 2048, 1)
:addColor("command", 2048, 8)
:addColor("help", 16384, 1)
:addColor("background", 1, 2048)
newTheme("seagle")
:addColor("default",colors.white, colors.black)
:addColor("title", colors.white, colors.black)
:addColor("subtitle", colors.red, colors.black)
:addColor("pos", colors.gray, colors.black)
:addColor("dim", colors.lightBlue, colors.black)
:addColor("extra", colors.lightGray, colors.black)
:addColor("error", colors.red, colors.white)
:addColor("info", colors.blue, colors.lightGray)
:addColor("inverse", colors.yellow, colors.lightGray)
:addColor("command", colors.lightBlue, colors.black)
:addColor("help", colors.red, colors.white)
:addColor("background", colors.white, colors.black)
newTheme("random")
:addColor("default",colors.white, colors.black)
:addColor("title", colors.pink, colors.blue)
:addColor("subtitle", colors.black, colors.white)
:addColor("pos", colors.green, colors.black)
:addColor("dim", colors.lightBlue, colors.black)
:addColor("extra", colors.lightGray, colors.lightBlue)
:addColor("error", colors.white, colors.yellow)
:addColor("info", colors.blue, colors.lightGray)
:addColor("inverse", colors.yellow, colors.lightGray)
:addColor("command", colors.green, colors.lightGray)
:addColor("help", colors.white, colors.yellow)
:addColor("background", colors.white, colors.red)
newTheme("rainbow")
:addColor("dim", 32, 0)
:addColor("background", 16384, 0)
:addColor("extra", 2048, 0)
:addColor("info", 2048, 0)
:addColor("inverse", 32, 0)
:addColor("subtitle", 2, 0)
:addColor("title", 16384, 0)
:addColor("error", 1024, 0)
:addColor("default", 1, 512)
:addColor("command", 16, 0)
:addColor("pos", 16, 0)
:addColor("help", 2, 0)
newTheme("green")
:addColor("dim", 16384, 0)
:addColor("background", 0, 0)
:addColor("extra", 2048, 0)
:addColor("info", 32, 256)
:addColor("inverse", 8192, 1)
:addColor("subtitle", 1, 0)
:addColor("title", 8192, 128)
:addColor("error", 16384, 32768)
:addColor("default", 1, 8192)
:addColor("command", 2048, 32)
:addColor("pos", 16, 0)
:addColor("help", 512, 32768)
--If you modify a theme a bunch and want to save it
local function saveTheme(theme, fileName)
if not theme or not type(fileName) == "string" then return false end
local file = fs.open(fileName,"w")
if not file then return false end
file.writeLine(fileName)
for a,b in pairs(theme) do
if type(b) == "table" then --If it contains color objects
file.writeLine(a.." "..tostring(b.text).." "..tostring(b.background))
end
end
file.close()
return true
end
--BUTTON CLASS
local button = {}
button.checkPoint = function(buttons, pos) --Returns a command or nil
for a, b in pairs(buttons) do
if pos[2] == b.line then
if pos[1] >= b.xDim[1] and pos[1] <= b.xDim[2] then
return b.command
end
end
end
end
button.makeLine = function(buttons, sep, xDim)
local toRet = ""
for a, b in ipairs(buttons) do
toRet = toRet..center(b.text, (b.xDim[2]-b.xDim[1]))..sep
end
return toRet:sub(1,-2).."" --Take off the last sep
end
button.new = function(line, xStart, xEnd, command, display)
local toRet = {}
setmetatable(toRet, {__index = button})
toRet.line = line
toRet.xDim = {math.min(xStart, xEnd), math.max(xStart, xEnd)}
toRet.command = command
toRet.text = display
return toRet
end
--==SCREEN CLASS FUNCTIONS==
local screenClass = {} --This is the class for all monitor/screen objects
screenClass.screens = {} --A simply numbered list of screens
screenClass.sides = {} --A mapping of screens by their side attached
screenClass.channels = {} --A mapping of receiving channels that have screens attached. Used for the receiver part
screenClass.sizes = {{7,18,29,39,50}, {5,12,19} , computer = {51, 19}, turtle = {39,13}, pocket = {26,20}}
screenClass.setTextColor = function(self, color) --Accepts raw color
if color and self.term.isColor() then
self.textColor = color
self.term.setTextColor(color)
return true
end
return false
end
screenClass.setBackgroundColor = function(self, color) --Accepts raw color
if color and self.term.isColor() then
self.backgroundColor = color
self.term.setBackgroundColor(color)
return true
end
return false
end
screenClass.setColor = function(self, color) --Wrapper, accepts themecolor objects
if type(color) ~= "table" then error("Set color received a non-table",2) end
local text, back = color.text, color.background
if not text or text == 0 then text = self.theme.default.text end
if not back or back == 0 then back = self.theme.default.background end
return self:setTextColor(text) and self:setBackgroundColor(back)
end
screenClass.themeName = "default" --Setting super for fallback
screenClass.theme = themes.default
screenClass.rec = { --Initial values for all displayed numbers
label = "Quarry Bot",
id = 1,
percent = 0,
xPos = 0,
zPos = 0,
layersDone = 0,
x = 0,
z = 0,
layers = 0,
openSlots = 0,
mined = 0,
moved = 0,
chestFull = false,
isAtChest = false,
isGoingToNextLayer = false,
foundBedrock = false,
fuel = 0,
volume = 0,
distance = 0,
yPos = 0
}
screenClass.new = function(side, receive, themeFile)
local self = {}
setmetatable(self, {__index = screenClass}) --Establish Hierarchy
self.side = side
if side == "computer" then
self.term = term
else
self.term = peripheral.wrap(side)
if not (self.term and peripheral.getType(side) == "monitor") then --Don't create an object if it doesn't exist
if doDebug then
error("No monitor on side "..tostring(side))
end
self = nil --Save memory?
return false
end
end
--Channels and ids
self.receive = tonumber(receive) --Receive Channel
self.send = nil --Reply Channel, obtained in handshake
self.id = #screenClass.screens+1
--Colors
self.themeName = nil --Will be set by setTheme
self.theme = nil
self.isColor = self.term.isColor() --Just for convenience
--Other Screen Properties
self.dim = {self.term.getSize()} --Raw dimensions
--Initializations
self.isDone = false --Flag for when the turtle is done transmitting
self.size = {} --Screen Size, assigned in setSize
self.textColor = colors.white --Just placeholders until theme is loaded and run
self.backColor = colors.black
self.toPrint = {}
self.isComputer = false
self.isTurtle = false
self.isPocket = false
self.acceptsInput = false
self.legacy = false --Whether it expects tables or strings
self.rec = copyTable(screenClass.rec)
screenClass.screens[self.id] = self
screenClass.sides[self.side] = self
if self.receive then
modem.open(self.receive) --Modem should be defined by the time anything is open
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
end
self:setSize() --Finish Initialization
self:setTheme(themeFile)
return self
end
screenClass.remove = function(tab) --Cleanup function
if type(tab) == "number" then --Expects table, can take id (for no apparent reason)
tab = screenClass.screens[tab]
end
tab:removeStation()
if tab.side == "REMOVED" then return end
if tab.side == "computer" then error("Tried removing computer screen",2) end --This should never happen
tab:reset() --Clear screen
tab:say("Removed", tab.theme.info, 1) --Let everyone know whats up
screenClass.screens[tab.id] = {side = "REMOVED"} --Not nil because screw up len()
screenClass.sides[tab.side] = nil
tab:removeChannel()
end
--Init Functions
screenClass.removeChannel = function(self)
self.send = nil
if self.receive then
screenClass.channels[self.receive] = nil
if modem and modem.isOpen(self.receive) then
modem.close(self.receive)
end
self.receive = nil
end
self:setSize()
end
screenClass.setChannel = function(self, channel)
if self.isStation then return false end --Don't want to set channel station
self:removeChannel()
if type(channel) == "number" then
self.receive = channel
screenClass.channels[self.receive] = self
if modem and not modem.isOpen(channel) then modem.open(channel) end
end
self:setSize() --Sets proper draw function
end
screenClass.setStation = function(self) --Note: This only changes the "set" methods so that "update" methods remain intact per object :)
self:removeChannel()
if not self.isStation then --Just in case this gets called more than once
self.isStation = true
table.insert(stationsList,self)
end
self:setSize()
end
screenClass.removeStation = function(self)
if self.isStation then
for i=1, #stationsList do --No IDs so have to do a linear traversal
if stationsList[i] == self then table.remove(stationsList, i) end
end
end
self.isStation = false
self:setSize()
end
screenClass.setSize = function(self) --Sets screen size
if self.side ~= "computer" and not self.term then self.term = peripheral.wrap(self.side) end
if not self.term.getSize() then --If peripheral is having problems/not there. Don't go further than term, otherwise index nil (maybe?)
debug("There is no term...")
self.updateDisplay = function() end --Do nothing on screen update, overrides class
return true
elseif self.isStation then
self:setStationDisplay()
elseif not self.receive then
self:setBrokenDisplay() --This will prompt user to set channel
elseif self.send then --This allows for class inheritance
self:setNormalDisplay() --In case objects have special updateDisplay methods --Remove function in case it exists, defaults to super
else --If the screen needs to have a handshake display
self:setHandshakeDisplay()
end
self:resetButtons()
self.dim = { self.term.getSize()}
local tab = screenClass.sizes
for a=1, 2 do --Want x and y dim
for b=1, #tab[a] do --Go through all normal sizes, x and y individually
if tab[a][b] <= self.dim[a] then --This will set size higher until false
self.size[a] = b
end
end
end
local function isThing(toCheck, thing) --E.G. isThing(self.dim,"computer")
return toCheck[1] == tab[thing][1] and toCheck[2] == tab[thing][2]
end
self.isComputer = isThing(self.dim, "computer")
self.isTurtle = isThing(self.dim, "turtle")
self.isPocket = isThing(self.dim, "pocket")
self.acceptsInput = self.isComputer or self.isTurtle or self.isPocket
return self
end
screenClass.setTheme = function(self, themeName, stopReset)
if not themes[themeName] then --If we don't have it already, try to load it
local fileName = themeName or ".." --.. returns false and I don't think you can name a file this
if fs.exists(themeFolder) then fileName = themeFolder..fileName end
if fs.exists(fileName) then
debug("Loading theme: ",fileName)
local file = fs.open(fileName, "r")
if not file then debug("Could not load theme '",themeName,"' file not found") end
parseTheme(file.readAll()) --Parses the text to make a theme, returns theme
file.close()
self.themeName = themeName:lower() --We can now set our themeName to the fileName
else
--Resets theme to super
if not stopReset then --This exists so its possible to set default theme without breaking world
self.themeName = nil
self.theme = nil
end
return false
end
else
self.themeName = themeName:lower()
end
self.theme = themes[self.themeName] --Now the theme is loaded or the function doesn't get here
return true
end
--Adds text to the screen buffer
screenClass.tryAddRaw = function(self, line, text, color, ...) --This will try to add text if Y dimension is a certain size
local doAdd = {...} --booleans for small, medium, and large
if type(text) ~= "string" then error("tryAddRaw got "..type(text)..", expected string",2) end
if not text then
debug("tryAddRaw got no string on line ",line)
return false
end
if type(color) ~= "table" then error("tryAddRaw did not get a color",2) end
--color = color or {text = colors.white}
for i=1, ySizes do --As of now there are 3 Y sizes
local test = doAdd[i]
if test == nil then test = doAdd[#doAdd] end --Set it to the last known setting if doesn't exist
if test and self.size[2] == i then --If should add this text for this screen size and the monitor is this size
if #text <= self.dim[1] then
self.toPrint[line] = {text = text, color = color}
return true
else
debug("Tried adding '",text,"' on line ",line," but was too long: ",#text," vs ",self.dim[1])
end
end
end
return false
end
screenClass.tryAdd = function(self, text, color,...) --Just a wrapper
return self:tryAddRaw(#self.toPrint+1, text, color, ...)
end
screenClass.tryAddC = function(self, text, color, ...) --Centered text
return self:tryAdd(center(text, self.dim[1]), color, ...)
end
screenClass.reset = function(self,color)
color = color or self.theme.background
self:setColor(color)
self.term.clear()
self.term.setCursorPos(1,1)
end
screenClass.say = function(self, text, color, line)
local currColor = self.backgroundColor
color = color or debug("Printing ",text," but had no themeColor: ",self.theme.name) or {} --Set default for nice error, alert that errors occur
self:setColor(color)
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
if doDebug and #text > self.dim[1] then error("Tried printing: '"..text.."', but was too big") end
self.term.setCursorPos(1,line)
for i=1, self.dim[1]-#text do --This is so the whole line's background gets filled.
text = text.." "
end
self.term.write(text)
self.term.setCursorPos(1, line+1)
end
screenClass.pushScreenUpdates = function(self)
for i=1, self.dim[2] do
local tab = self.toPrint[i]
if tab then
self:say(tab.text, tab.color, i)
end
end
self.term.setCursorPos(1,self.dim[2]) --So we can see errors
end
screenClass.resetButtons = function(self)
self.buttons = {}
end
screenClass.addButton = function(self, button)
self.buttons[#self.buttons+1] = button
end
screenClass.updateNormal = function(self) --This is the normal updateDisplay function
local str = tostring
self.toPrint = {} --Reset table
local message, theme, x = self.rec, self.theme, self.dim[1]
if not self.isDone then --Normally
if self.size[1] == 1 then --Small Width Monitor
if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
self:tryAdd("Quarry!", theme.title, false, false, true)
end
self:tryAdd("-Fuel-", theme.subtitle , false, true, true)
if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
self:tryAdd("A lot", theme.extra, false, true, true)
end
self:tryAdd("--%%%--", theme.subtitle, false, true, true)
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
self:tryAdd(center(str(message.percent).."%", x), theme.pos, true, false) --I want it to be centered on 1x1
self:tryAdd("--Pos--", theme.subtitle, false, true, true)
self:tryAdd("X:"..alignR(str(message.xPos), 5), theme.pos, true)
self:tryAdd("Z:"..alignR(str(message.zPos), 5), theme.pos , true)
self:tryAdd("Y:"..alignR(str(message.layersDone), 5), theme.pos , true)
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
self:tryAdd(str(message.x).."x"..str(message.z), theme.dim , true, false)
end
self:tryAdd("--Dim--", theme.subtitle, false, true, true)
self:tryAdd("X:"..alignR(str(message.x), 5), theme.dim, false, true, true)
self:tryAdd("Z:"..alignR(str(message.z), 5), theme.dim, false, true, true)
self:tryAdd("Y:"..alignR(str(message.layers), 5), theme.dim, false, true, true)
self:tryAdd("-Extra-", theme.subtitle, false, false, true)
self:tryAdd(alignR(textutils.formatTime(os.time()):gsub(" ","").."", 7), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
self:tryAdd("Used:"..alignR(str(16-message.openSlots),2), theme.extra, false, false, true)
self:tryAdd("Dug"..alignR(str(message.mined), 4), theme.extra, false, false, true)
self:tryAdd("Mvd"..alignR(str(message.moved), 4), theme.extra, false, false, true)
if message.status then
self:tryAdd(alignL(message.status, x), theme.info, false, false, true)
end
if message.chestFull then
self:tryAdd("ChstFll", theme.error, false, false, true)
end
end
if self.size[1] == 2 then --Medium Monitor
if not self:tryAdd(message.label, theme.title, false, false, true) then --This will be a title, basically
self:tryAdd("Quarry!", theme.title, false, false, true)
end
self:tryAdd(center("Fuel",x,"-"), theme.subtitle , false, true, true)
if not self:tryAdd(str(message.fuel), theme.extra, false, true, true) then --The fuel number may be bigger than the screen
self.toPrint[#self.toPrint] = nil
self:tryAdd("A lot", theme.extra, false, true, true)
end
self:tryAdd(str(message.percent).."% Complete", theme.pos , true) --This can be an example. Print (receivedMessage).percent in blue on all different screen sizes
self:tryAdd(center("Pos",x,"-"), theme.subtitle, false, true, true)
self:tryAdd(leftRight("X Coordinate:",message.xPos, x), theme.pos, true)
self:tryAdd(leftRight("Z Coordinate:",message.zPos, x), theme.pos , true)
self:tryAdd(leftRight("On Layer:",message.layersDone, x), theme.pos , true)
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
self:tryAdd(str(message.x).."x"..str(message.z).."x"..str(message.layers), theme.dim , true, false)
end
self:tryAdd(center("Dim",x,"-"), theme.subtitle, false, true, true)
self:tryAdd(leftRight("Total X:", message.x, x), theme.dim, false, true, true)
self:tryAdd(leftRight("Total Z:", message.z, x), theme.dim, false, true, true)
self:tryAdd(leftRight("Total Layers:", message.layers, x), theme.dim, false, true, true)
self:tryAdd(leftRight("Volume", message.volume, x), theme.dim, false, false, true)
self:tryAdd(center("Extras",x,"-"), theme.subtitle, false, false, true)
self:tryAdd(leftRight("Time: ", textutils.formatTime(os.time()):gsub(" ","").."", x), theme.extra, false, false, true) --Adds the current time, formatted, without spaces.
self:tryAdd(leftRight("Used Slots:", 16-message.openSlots, x), theme.extra, false, false, true)
self:tryAdd(leftRight("Blocks Mined:", message.mined, x), theme.extra, false, false, true)
self:tryAdd(leftRight("Spaces Moved:", message.moved, x), theme.extra, false, false, not self.isPocket)
if message.status then
self:tryAdd(message.status, theme.info, false, false, true)
end
if message.chestFull then
self:tryAdd("Chest Full, Fix It", theme.error, false, true, true)
end
end
if self.size[1] >= 3 then --Large or larger screens
if not self:tryAdd(message.label..alignR(" Turtle #"..str(message.id),x-#message.label), theme.title, true) then
self:tryAdd("Your turtle's name is long...", theme.title, true)
end
self:tryAdd("Fuel: "..alignR(str(message.fuel),x-6), theme.extra, true)
self:tryAdd("Percentage Done: "..alignR(str(message.percent).."%",x-17), theme.pos, true)
local var1 = math.max(#str(message.x), #str(message.z), #str(message.layers))
local var2 = (x-6-var1+3)/3
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)
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)
self:tryAdd("Volume: "..str(message.volume), theme.dim, false, true, true)
self:tryAdd("",{}, false, false, true)
self:tryAdd(center("____---- EXTRAS ----____",x), theme.subtitle, false, false, true)
self:tryAdd(center("Time:"..alignR(textutils.formatTime(os.time()),10), x), theme.extra, false, true, true)
self:tryAdd(center("Current Day: "..str(os.day()), x), theme.extra, false, false, true)
self:tryAdd("Used Inventory Slots: "..alignR(str(16-message.openSlots),x-22), theme.extra, false, true, true)
self:tryAdd("Blocks Mined: "..alignR(str(message.mined),x-14), theme.extra, false, true, true)
self:tryAdd("Blocks Moved: "..alignR(str(message.moved),x-14), theme.extra, false, true, true)
self:tryAdd("Distance to Turtle: "..alignR(str(message.distance), x-20), theme.extra, false, false, true)
self:tryAdd("Actual Y Pos (Not Layer): "..alignR(str(message.yPos), x-26), theme.extra, false, false, true)
if message.chestFull then
self:tryAdd("Dropoff is Full, Please Fix", theme.error, false, true, true)
end
if message.foundBedrock then
self:tryAdd("Found Bedrock! Please Check!!", theme.error, false, true, true)
end
if message.status then
self:tryAdd("Status: "..message.status, theme.info, false, true, true)
end
if message.isAtChest then
self:tryAdd("Turtle is at home chest", theme.info, false, true, true)
end
if message.isGoingToNextLayer then
self:tryAdd("Turtle is going to next layer", theme.info, false, true, true)
end
end
if self.term.isColor() and ((self.size[2] >= 2 and self.size[1] >= 3) or self.isPocket) then
local line = self.acceptsInput and self.dim[2]-1 or self.dim[2]
local part = math.floor(x/4)
if #self.buttons == 0 then
self:addButton(button.new(line, part*0, part*1-1, "drop","Drop"))
self:addButton(button.new(line, part*1, part*2-1, "pause","Pause"))
self:addButton(button.new(line, part*2, part*3-1, "return","Return"))
self:addButton(button.new(line, part*3, part*4-1, "refuel","Refuel"))
end
self:tryAddRaw(line, button.makeLine(self.buttons,"|"):sub(1,self.isPocket and -2 or -1), theme.command, false, true) --Silly code because pocket breaks
end
else --If is done
if self.size[1] == 1 then --Special case for small monitors
self:tryAdd("Done", theme.title, true)
if not self:tryAdd("Dug"..alignR(str(message.mined),4, false), theme.pos, true) then
self:tryAdd("Dug", theme.pos, true)
self:tryAdd(alignR(str(message.mined),x), theme.pos, true)
end
if not self:tryAdd("Fuel"..alignR(str(message.fuel),3, false), theme.pos, true) then
self:tryAdd("Fuel", theme.pos, true)
self:tryAdd(alignR(str(message.fuel),x), theme.pos, true)
end
self:tryAdd("-------", theme.subtitle, false,true,true)
self:tryAdd("Turtle", theme.subtitle, false, true, true)
self:tryAdd(center("is", x), theme.subtitle, false, true, true)
self:tryAdd(center("Done!", x), theme.subtitle, false, true, true)
else
self:tryAdd("Done!", theme.title, true)
self:tryAdd("Curr Fuel: "..str(message.fuel), theme.pos, true)
if message.preciseTotals then
local tab = {}
for a,b in pairs(message.preciseTotals) do --Sorting the table
a = a:match(":(.+)")
if #tab == 0 then --Have to initialize or rest does nothing :)
tab[1] = {a,b}
else
for i=1, #tab do --This is a really simple sort. Probably not very efficient, but I don't care.
if b > tab[i][2] then --Gets the second value from the table, which is the itemCount
table.insert(tab, i, {a,b})
break
elseif i == #tab then --Insert at the end if not bigger than anything
table.insert(tab,{a,b})
end
end
end
end
for i=1, #tab do --Print all the blocks in order
local firstPart = "#"..tab[i][1]..": "
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
end
else
self:tryAdd("Blocks Dug: "..str(message.mined), theme.inverse, true)
self:tryAdd("Cobble Dug: "..str(message.cobble), theme.pos, false, true, true)
self:tryAdd("Fuel Dug: "..str(message.fuelblocks), theme.pos, false, true, true)
self:tryAdd("Others Dug: "..str(message.other), theme.pos, false, true, true)
end
end
end
end
screenClass.updateHandshake = function(self)
self.toPrint = {}
local half = math.ceil(self.dim[2]/2)
if self.size[1] == 1 then --Not relying on the parameter system because less calls
self:tryAddRaw(half-2, "Waiting", self.theme.error, true)
self:tryAddRaw(half-1, "For Msg", self.theme.error, true)
self:tryAddRaw(half, "On Chnl", self.theme.error, true)
self:tryAddRaw(half+1, tostring(self.receive), self.theme.error, true)
else
local str = "for"
if self.size[1] == 2 then str = "4" end--Just a small grammar change