mastermine.lua

3656 lines · 123.9 KB

Open raw

Copy & run

wget https://perlytiara.github.io/turtles.tips/raw/programs/perlytiara/mastermine/_deploy_pastebin/mastermine.lua
1output_dir = ...
2if not output_dir then
3 output_dir = ''
4end
5path = shell.resolve(output_dir)
6if not fs.isDir(path) then
7 error(path .. ' is not a directory')
8end
9
10files = {
11 ["LICENSE"] = [===[MIT License
12
13Copyright (c) 2020 MerlinLikeTheWizard
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in all
23copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31SOFTWARE.]===],
32 ["README.md"] = [===[# Mastermine
33A fully automated strip mining network for ComputerCraft turtles!
34
35Here's all the code for anyone who is interested! Check out the tutorial below for installation instructions.
36
37Also, here are steps for a quick install via pastebin:
38
391. Place your advanced computer next to a disk drive with a blank disk in.
402. Run `pastebin get CtcSGkpc mastermine.lua`
413. Run `mastermine disk`
424. Run `disk/hub.lua`
43
44## Play with or without PeripheralsPlusOne
45
46I highly recommend using the PeripheralsPlusOne and its chunky turtles, but upon popular request I added the ability to disable the need for chunky turtle pairs. Just go to the config and set `use_chunky_turtles = false`
47
48## Video description:
49
50[![https://www.youtube.com/watch?v=2I2VXl9Pg6Q](https://img.youtube.com/vi/2I2VXl9Pg6Q/0.jpg)](https://www.youtube.com/watch?v=2I2VXl9Pg6Q)
51
52## Video tutorial:
53
54[![https://www.youtube.com/watch?v=2DTP1LXuiCg](https://img.youtube.com/vi/2DTP1LXuiCg/0.jpg)](https://www.youtube.com/watch?v=2DTP1LXuiCg)
55
56## User commands:
57
58* `on/go`
59* `off/stop`
60* `turtle <#> <action>`
61* `update <#>`
62* `reboot <#>`
63* `shutdown <#>`
64* `reset <#>`
65* `clear <#>`
66* `halt <#>`
67* `return <#>`
68* `hubupdate`
69* `hubreboot`
70* `hubshutdown`
71
72
73use `*` as notation for all turtles
74
75
76## Required mods:
77
78https://www.curseforge.com/minecraft/mc-mods/cc-tweaked
79
80https://github.com/rolandoislas/PeripheralsPlusOne
81
82Required by PeripheralsPlusOne: https://www.curseforge.com/minecraft/mc-mods/the-framework]===],
83 ["hub.lua"] = [===[if fs.exists('/disk/hub_files/session_id') then
84 fs.delete('/disk/hub_files/session_id')
85end
86if fs.exists('/session_id') then
87 fs.copy('/session_id', '/disk/hub_files/session_id')
88end
89if fs.exists('/disk/hub_files/mine') then
90 fs.delete('/disk/hub_files/mine')
91end
92if fs.exists('/mine') then
93 fs.copy('/mine', '/disk/hub_files/mine')
94end
95
96for _, filename in pairs(fs.list('/')) do
97 if filename ~= 'rom' and filename ~= 'disk' and filename ~= 'openp' and filename ~= 'ppp' and filename ~= 'persistent' then
98 fs.delete(filename)
99 end
100end
101for _, filename in pairs(fs.list('/disk/hub_files')) do
102 fs.copy('/disk/hub_files/' .. filename, '/' .. filename)
103end
104os.reboot()]===],
105 ["pocket.lua"] = [===[local src, dest = ...
106
107fs.copy(fs.combine(src, 'pocket_files/update'), fs.combine(dest, 'update'))
108file = fs.open(fs.combine(dest, 'hub_id'), 'w')
109file.write(os.getComputerID())
110file.close()]===],
111 ["turtle.lua"] = [===[for _, filename in pairs(fs.list('/')) do
112 if filename ~= 'rom' and filename ~= 'disk' and filename ~= 'openp' and filename ~= 'ppp' and filename ~= 'persistent' then
113 fs.delete(filename)
114 end
115end
116
117for _, filename in pairs(fs.list('/disk/turtle_files')) do
118 fs.copy('/disk/turtle_files/' .. filename, '/' .. filename)
119end
120
121print("Enter ID of Hub computer to link to: ")
122hub_id = tonumber(read())
123if hub_id == nil then
124 error("Invalid ID")
125end
126
127file = fs.open('/hub_id', 'w')
128file.write(hub_id)
129file.close()
130
131print("Linked")
132
133sleep(1)
134os.reboot()]===],
135 ["turtle_files/actions.lua"] = [===[inf = basics.inf
136str_xyz = basics.str_xyz
137
138--lua_print = print
139--log_file = fs.open('log.txt', 'w')
140--function print(thing)
141-- lua_print(thing)
142-- log_file.writeLine(thing)
143--end
144
145
146bumps = {
147 north = { 0, 0, -1},
148 south = { 0, 0, 1},
149 east = { 1, 0, 0},
150 west = {-1, 0, 0},
151}
152
153
154left_shift = {
155 north = 'west',
156 south = 'east',
157 east = 'north',
158 west = 'south',
159}
160
161
162right_shift = {
163 north = 'east',
164 south = 'west',
165 east = 'south',
166 west = 'north',
167}
168
169
170reverse_shift = {
171 north = 'south',
172 south = 'north',
173 east = 'west',
174 west = 'east',
175}
176
177
178move = {
179 forward = turtle.forward,
180 up = turtle.up,
181 down = turtle.down,
182 back = turtle.back,
183 left = turtle.turnLeft,
184 right = turtle.turnRight
185}
186
187
188detect = {
189 forward = turtle.detect,
190 up = turtle.detectUp,
191 down = turtle.detectDown
192}
193
194
195inspect = {
196 forward = turtle.inspect,
197 up = turtle.inspectUp,
198 down = turtle.inspectDown
199}
200
201
202dig = {
203 forward = turtle.dig,
204 up = turtle.digUp,
205 down = turtle.digDown
206}
207
208attack = {
209 forward = turtle.attack,
210 up = turtle.attackUp,
211 down = turtle.attackDown
212}
213
214
215getblock = {
216
217 up = function(pos, fac)
218 if not pos then pos = state.location end
219 if not fac then fac = state.orientation end
220 return {x = pos.x, y = pos.y + 1, z = pos.z}
221 end,
222
223 down = function(pos, fac)
224 if not pos then pos = state.location end
225 if not fac then fac = state.orientation end
226 return {x = pos.x, y = pos.y - 1, z = pos.z}
227 end,
228
229 forward = function(pos, fac)
230 if not pos then pos = state.location end
231 if not fac then fac = state.orientation end
232 local bump = bumps[fac]
233 return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]}
234 end,
235
236 back = function(pos, fac)
237 if not pos then pos = state.location end
238 if not fac then fac = state.orientation end
239 local bump = bumps[fac]
240 return {x = pos.x - bump[1], y = pos.y - bump[2], z = pos.z - bump[3]}
241 end,
242
243 left = function(pos, fac)
244 if not pos then pos = state.location end
245 if not fac then fac = state.orientation end
246 local bump = bumps[left_shift[fac]]
247 return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]}
248 end,
249
250 right = function(pos, fac)
251 if not pos then pos = state.location end
252 if not fac then fac = state.orientation end
253 local bump = bumps[right_shift[fac]]
254 return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]}
255 end,
256}
257
258
259function digblock(direction)
260 dig[direction]()
261 return true
262end
263
264
265function delay(duration)
266 sleep(duration)
267 return true
268end
269
270
271function up()
272 return go('up')
273end
274
275
276function forward()
277 return go('forward')
278end
279
280
281function down()
282 return go('down')
283end
284
285
286function back()
287 return go('back')
288end
289
290
291function left()
292 return go('left')
293end
294
295
296function right()
297 return go('right')
298end
299
300
301function follow_route(route)
302 for step in route:gmatch'.' do
303 if step == 'u' then
304 if not go('up') then return false end
305 elseif step == 'f' then
306 if not go('forward') then return false end
307 elseif step == 'd' then
308 if not go('down') then return false end
309 elseif step == 'b' then
310 if not go('back') then return false end
311 elseif step == 'l' then
312 if not go('left') then return false end
313 elseif step == 'r' then
314 if not go('right') then return false end
315 end
316 end
317 return true
318end
319
320
321function face(orientation)
322 if state.orientation == orientation then
323 return true
324 elseif right_shift[state.orientation] == orientation then
325 if not go('right') then return false end
326 elseif left_shift[state.orientation] == orientation then
327 if not go('left') then return false end
328 elseif right_shift[right_shift[state.orientation]] == orientation then
329 if not go('right') then return false end
330 if not go('right') then return false end
331 else
332 return false
333 end
334 return true
335end
336
337
338function log_movement(direction)
339 if direction == 'up' then
340 state.location.y = state.location.y +1
341 elseif direction == 'down' then
342 state.location.y = state.location.y -1
343 elseif direction == 'forward' then
344 bump = bumps[state.orientation]
345 state.location = {x = state.location.x + bump[1], y = state.location.y + bump[2], z = state.location.z + bump[3]}
346 elseif direction == 'back' then
347 bump = bumps[state.orientation]
348 state.location = {x = state.location.x - bump[1], y = state.location.y - bump[2], z = state.location.z - bump[3]}
349 elseif direction == 'left' then
350 state.orientation = left_shift[state.orientation]
351 elseif direction == 'right' then
352 state.orientation = right_shift[state.orientation]
353 end
354 return true
355end
356
357
358function go(direction, nodig)
359 if not nodig then
360 if detect[direction] then
361 if detect[direction]() then
362 safedig(direction)
363 end
364 end
365 end
366 if not move[direction] then
367 return false
368 end
369 if not move[direction]() then
370 if attack[direction] then
371 attack[direction]()
372 end
373 return false
374 end
375 log_movement(direction)
376 return true
377end
378
379
380function go_to_axis(axis, coordinate, nodig)
381 local delta = coordinate - state.location[axis]
382 if delta == 0 then
383 return true
384 end
385
386 if axis == 'x' then
387 if delta > 0 then
388 if not face('east') then return false end
389 else
390 if not face('west') then return false end
391 end
392 elseif axis == 'z' then
393 if delta > 0 then
394 if not face('south') then return false end
395 else
396 if not face('north') then return false end
397 end
398 end
399
400 for i = 1, math.abs(delta) do
401 if axis == 'y' then
402 if delta > 0 then
403 if not go('up', nodig) then return false end
404 else
405 if not go('down', nodig) then return false end
406 end
407 else
408 if not go('forward', nodig) then return false end
409 end
410 end
411 return true
412end
413
414
415function go_to(end_location, end_orientation, path, nodig)
416 if path then
417 for axis in path:gmatch'.' do
418 if not go_to_axis(axis, end_location[axis], nodig) then return false end
419 end
420 elseif end_location.path then
421 for axis in end_location.path:gmatch'.' do
422 if not go_to_axis(axis, end_location[axis], nodig) then return false end
423 end
424 else
425 return false
426 end
427 if end_orientation then
428 if not face(end_orientation) then return false end
429 elseif end_location.orientation then
430 if not face(end_location.orientation) then return false end
431 end
432 return true
433end
434
435
436function go_route(route, xyzo)
437 local xyz_string
438 if xyzo then
439 xyz_string = str_xyz(xyzo)
440 end
441 local location_str = basics.str_xyz(state.location)
442 while route[location_str] and location_str ~= xyz_string do
443 if not go_to(route[location_str], nil, 'xyz') then return false end
444 location_str = basics.str_xyz(state.location)
445 end
446 if xyzo then
447 if location_str ~= xyz_string then
448 return false
449 end
450 if xyzo.orientation then
451 if not face(xyzo.orientation) then return false end
452 end
453 end
454 return true
455end
456
457
458function go_to_home()
459 state.updated_not_home = nil
460 if basics.in_area(state.location, config.locations.home_area) then
461 return true
462 elseif basics.in_area(state.location, config.locations.greater_home_area) then
463 if not go_to_home_exit() then return false end
464 elseif basics.in_area(state.location, config.locations.waiting_room_area) then
465 if not go_to(config.locations.mine_exit, nil, config.paths.waiting_room_to_mine_exit, true) then return false end
466 elseif state.location.y < config.locations.mine_enter.y then
467 return false
468 end
469 if config.locations.main_loop_route[basics.str_xyz(state.location)] then
470 if not go_route(config.locations.main_loop_route, config.locations.home_enter) then return false end
471 elseif basics.in_area(state.location, config.locations.control_room_area) then
472 if not go_to(config.locations.home_enter, nil, config.paths.control_room_to_home_enter, true) then return false end
473 else
474 return false
475 end
476 if not forward() then return false end
477 while detect.down() do
478 if not forward() then return false end
479 end
480 if not down() then return false end
481 if not right() then return false end
482 if not right() then return false end
483 return true
484end
485
486
487function go_to_home_exit()
488 if basics.in_area(state.location, config.locations.greater_home_area) then
489 if not go_to(config.locations.home_exit, nil, config.paths.home_to_home_exit) then return false end
490 elseif config.locations.main_loop_route[basics.str_xyz(state.location)] then
491 if not go_route(config.locations.main_loop_route, config.locations.home_exit) then return false end
492 else
493 return false
494 end
495 return true
496end
497
498
499function go_to_item_drop()
500 if not config.locations.main_loop_route[basics.str_xyz(state.location)] then
501 if not go_to_home() then return false end
502 if not go_to_home_exit() then return false end
503 end
504 if not go_route(config.locations.main_loop_route, config.locations.item_drop) then return false end
505 return true
506end
507
508
509function go_to_refuel()
510 if not config.locations.main_loop_route[basics.str_xyz(state.location)] then
511 if not go_to_home() then return false end
512 if not go_to_home_exit() then return false end
513 end
514 if not go_route(config.locations.main_loop_route, config.locations.refuel) then return false end
515 return true
516end
517
518
519function go_to_waiting_room()
520 if not basics.in_area(state.location, config.locations.waiting_room_line_area) then
521 if not go_to_home() then return false end
522 end
523 if not go_to(config.locations.waiting_room, nil, config.paths.home_to_waiting_room) then return false end
524 return true
525end
526
527
528function go_to_mine_enter()
529 if not go_route(config.locations.waiting_room_to_mine_enter_route) then return false end
530 return true
531end
532
533
534function go_to_strip(strip)
535 if state.location.y < config.locations.mine_enter.y or basics.in_location(state.location, config.locations.mine_enter) then
536 if state.type == 'mining' then
537 local bump = bumps[strip.orientation]
538 strip = {
539 x = strip.x + bump[1],
540 y = strip.y + bump[2],
541 z = strip.z + bump[3],
542 orientation = strip.orientation
543 }
544 end
545 if not go_to(strip, nil, config.paths.mine_enter_to_strip) then return false end
546 return true
547 end
548end
549
550
551function go_to_mine_exit(strip)
552 if state.location.y < config.locations.mine_enter.y or (state.location.x == config.locations.mine_exit.x and state.location.z == config.locations.mine_exit.z) then
553 if state.location.x == config.locations.mine_enter.x and state.location.z == config.locations.mine_enter.z then
554 -- If directly under mine_enter, shift over to exit
555 if not go_to_axis('z', config.locations.mine_exit.z) then return false end
556 elseif state.location.x ~= config.locations.mine_exit.x or state.location.z ~= config.locations.mine_exit.z then
557 -- If NOT directly under mine_exit go to proper y
558 if not go_to_axis('y', strip.y + 1) then return false end
559 if state.location.z ~= config.locations.mine_enter.z and strip.z ~= config.locations.mine_enter.z then
560 -- If not in main_shaft, find your strip
561 if not go_to_axis('x', strip.x) then return false end
562 end
563 if state.location.x ~= config.locations.mine_exit.x then
564 -- If not in strip x = origin, go to main_shaft
565 if not go_to_axis('z', config.locations.mine_enter.z) then return false end
566 end
567 end
568 if not go_to(config.locations.mine_exit, nil, 'xzy') then return false end
569 return true
570 end
571end
572
573
574function safedig(direction)
575 -- DIG IF BLOCK NOT ON BLACKLIST
576 if not direction then
577 direction = 'forward'
578 end
579
580 local block_name = ({inspect[direction]()})[2].name
581 if block_name then
582 for _, word in pairs(config.dig_disallow) do
583 if string.find(string.lower(block_name), word) then
584 return false
585 end
586 end
587
588 return dig[direction]()
589 end
590 return true
591end
592
593
594function dump_items(omit)
595 for slot = 1, 16 do
596 if turtle.getItemCount(slot) > 0 and ((not omit) or (not omit[turtle.getItemDetail(slot).name])) then
597 turtle.select(slot)
598 if not turtle.drop() then return false end
599 end
600 end
601 return true
602end
603
604
605
606function prepare(min_fuel_amount)
607 if state.item_count > 0 then
608 if not go_to_item_drop() then return false end
609 if not dump_items(config.fuelnames) then return false end
610 end
611 local min_fuel_amount = min_fuel_amount + config.fuel_padding
612 if not go_to_refuel() then return false end
613 if not dump_items() then return false end
614 turtle.select(1)
615 if turtle.getFuelLevel() ~= 'unlimited' then
616 while turtle.getFuelLevel() < min_fuel_amount do
617 if not turtle.suck(math.min(64, math.ceil(min_fuel_amount / config.fuel_per_unit))) then return false end
618 turtle.refuel()
619 end
620 end
621 return true
622end
623
624
625function calibrate()
626 -- GEOPOSITION BY MOVING TO ADJACENT BLOCK AND BACK
627 local sx, sy, sz = gps.locate()
628-- if sx == config.interface.x and sy == config.interface.y and sz == config.interface.z then
629-- refuel()
630-- end
631 if not sx or not sy or not sz then
632 return false
633 end
634 for i = 1, 4 do
635 -- TRY TO FIND EMPTY ADJACENT BLOCK
636 if not turtle.detect() then
637 break
638 end
639 if not turtle.turnRight() then return false end
640 end
641 if turtle.detect() then
642 -- TRY TO DIG ADJACENT BLOCK
643 for i = 1, 4 do
644 safedig('forward')
645 if not turtle.detect() then
646 break
647 end
648 if not turtle.turnRight() then return false end
649 end
650 if turtle.detect() then
651 return false
652 end
653 end
654 if not turtle.forward() then return false end
655 local nx, ny, nz = gps.locate()
656 if nx == sx + 1 then
657 state.orientation = 'east'
658 elseif nx == sx - 1 then
659 state.orientation = 'west'
660 elseif nz == sz + 1 then
661 state.orientation = 'south'
662 elseif nz == sz - 1 then
663 state.orientation = 'north'
664 else
665 return false
666 end
667 state.location = {x = nx, y = ny, z = nz}
668 print('Calibrated to ' .. str_xyz(state.location, state.orientation))
669
670 back()
671
672 if basics.in_area(state.location, config.locations.home_area) then
673 face(left_shift[left_shift[config.locations.homes.increment]])
674 end
675
676 return true
677end
678
679
680function initialize(session_id, config_values)
681 -- INITIALIZE TURTLE
682
683 state.session_id = session_id
684
685 -- COPY CONFIG DATA INTO MEMORY
686 for k, v in pairs(config_values) do
687 config[k] = v
688 end
689
690 -- DETERMINE TURTLE TYPE
691 state.peripheral_left = peripheral.getType('left')
692 state.peripheral_right = peripheral.getType('right')
693 if state.peripheral_left == 'chunkLoader' or state.peripheral_right == 'chunkLoader' or state.peripheral_left == 'chunky' or state.peripheral_right == 'chunky' then
694 state.type = 'chunky'
695 for k, v in pairs(config.chunky_turtle_locations) do
696 config.locations[k] = v
697 end
698 else
699 state.type = 'mining'
700 for k, v in pairs(config.mining_turtle_locations) do
701 config.locations[k] = v
702 end
703 if state.peripheral_left == 'modem' then
704 state.peripheral_right = 'pick'
705 else
706 state.peripheral_left = 'pick'
707 end
708 end
709
710 state.request_id = 1
711 state.initialized = true
712 return true
713end
714
715
716function getcwd()
717 local running_program = shell.getRunningProgram()
718 local program_name = fs.getName(running_program)
719 return "/" .. running_program:sub(1, #running_program - #program_name)
720end
721
722
723function pass()
724 return true
725end
726
727
728function dump(direction)
729 if not face(direction) then return false end
730 if ({inspect.forward()})[2].name ~= 'computercraft:turtle_advanced' then
731 return false
732 end
733 for slot = 1, 16 do
734 if turtle.getItemCount(slot) > 0 then
735 turtle.select(slot)
736 turtle.drop()
737 end
738 end
739 return true
740end
741
742
743function checkTags(data)
744 if type(data.tags) ~= 'table' then
745 return false
746 end
747 if not config.blocktags then
748 return false
749 end
750 for k,v in pairs(data.tags) do
751 if config.blocktags[k] then
752 return true
753 end
754 end
755 return false
756end
757
758
759function detect_ore(direction)
760 local block = ({inspect[direction]()})[2]
761 if config.orenames[block.name] then
762 return true
763 elseif checkTags(block) then
764 return true
765 end
766 return false
767end
768
769
770function scan(valid, ores)
771 local checked_left = false
772 local checked_right = false
773
774 local f = str_xyz(getblock.forward())
775 local u = str_xyz(getblock.up())
776 local d = str_xyz(getblock.down())
777 local l = str_xyz(getblock.left())
778 local r = str_xyz(getblock.right())
779 local b = str_xyz(getblock.back())
780
781 if not valid[f] and valid[f] ~= false then
782 valid[f] = detect_ore('forward')
783 ores[f] = valid[f]
784 end
785 if not valid[u] and valid[u] ~= false then
786 valid[u] = detect_ore('up')
787 ores[u] = valid[u]
788 end
789 if not valid[d] and valid[d] ~= false then
790 valid[d] = detect_ore('down')
791 ores[d] = valid[d]
792 end
793 if not valid[l] and valid[l] ~= false then
794 left()
795 checked_left = true
796 valid[l] = detect_ore('forward')
797 ores[l] = valid[l]
798 end
799 if not valid[r] and valid[r] ~= false then
800 right()
801 if checked_left then
802 right()
803 end
804 checked_right = true
805 valid[r] = detect_ore('forward')
806 ores[r] = valid[r]
807 end
808 if not valid[b] and valid[b] ~= false then
809 if checked_right then
810 right()
811 elseif checked_left then
812 left()
813 else
814 right(2)
815 end
816 valid[b] = detect_ore('forward')
817 ores[b] = valid[b]
818 end
819end
820
821
822function fastest_route(area, pos, fac, end_locations)
823 local queue = {}
824 local explored = {}
825 table.insert(queue,
826 {
827 coords = {x = pos.x, y = pos.y, z = pos.z},
828 facing = fac,
829 path = '',
830 }
831 )
832 explored[str_xyz(pos, fac)] = true
833
834 while #queue > 0 do
835 local node = table.remove(queue, 1)
836 if end_locations[str_xyz(node.coords)] or end_locations[str_xyz(node.coords, node.facing)] then
837 return node.path
838 end
839 for _, step in pairs({
840 {coords = node.coords, facing = left_shift[node.facing], path = node.path .. 'l'},
841 {coords = node.coords, facing = right_shift[node.facing], path = node.path .. 'r'},
842 {coords = getblock.forward(node.coords, node.facing), facing = node.facing, path = node.path .. 'f'},
843 {coords = getblock.up(node.coords, node.facing), facing = node.facing, path = node.path .. 'u'},
844 {coords = getblock.down(node.coords, node.facing), facing = node.facing, path = node.path .. 'd'},
845 }) do
846 explore_string = str_xyz(step.coords, step.facing)
847 if not explored[explore_string] and (not area or area[str_xyz(step.coords)]) then
848 explored[explore_string] = true
849 table.insert(queue, step)
850 end
851 end
852 end
853end
854
855
856function mine_vein(direction)
857 if not face(direction) then return false end
858
859 -- Log starting location
860 local start = str_xyz({x = state.location.x, y = state.location.y, z = state.location.z}, state.orientation)
861
862 -- Begin block map
863 local valid = {}
864 local ores = {}
865 valid[str_xyz(state.location)] = true
866 valid[str_xyz(getblock.back(state.location, state.orientation))] = false
867 for i = 1, config.vein_max do
868
869 -- Scan adjacent
870 scan(valid, ores)
871
872 -- Search for nearest ore
873 local route = fastest_route(valid, state.location, state.orientation, ores)
874
875 -- Check if there is one
876 if not route then
877 break
878 end
879
880 -- Retrieve ore
881 turtle.select(1)
882 if not follow_route(route) then return false end
883 ores[str_xyz(state.location)] = nil
884
885 end
886
887 if not follow_route(fastest_route(valid, state.location, state.orientation, {[start] = true})) then return false end
888
889 if detect.up() then
890 safedig('up')
891 end
892
893 return true
894end
895
896
897function clear_gravity_blocks()
898 for _, direction in pairs({'forward', 'up'}) do
899 while config.gravitynames[ ({inspect[direction]()})[2].name ] do
900 safedig(direction)
901 sleep(1)
902 end
903 end
904 return true
905end]===],
906 ["turtle_files/basics.lua"] = [===[inf = 1e309
907
908bumps = {
909 north = { 0, 0, -1},
910 south = { 0, 0, 1},
911 east = { 1, 0, 0},
912 west = {-1, 0, 0},
913}
914
915left_shift = {
916 north = 'west',
917 south = 'east',
918 east = 'north',
919 west = 'south',
920}
921
922right_shift = {
923 north = 'east',
924 south = 'west',
925 east = 'south',
926 west = 'north',
927}
928
929reverse_shift = {
930 north = 'south',
931 south = 'north',
932 east = 'west',
933 west = 'east',
934}
935
936function dprint(thing)
937 -- PRINT; IF TABLE PRINT EACH ITEM
938 if type(thing) == 'table' then
939 for k, v in pairs(thing) do
940 print(tostring(k) .. ': ' .. tostring(v))
941 end
942 else
943 print(thing)
944 end
945 return true
946end
947
948
949function str_xyz(coords, facing)
950 if facing then
951 return coords.x .. ',' .. coords.y .. ',' .. coords.z .. ':' .. facing
952 else
953 return coords.x .. ',' .. coords.y .. ',' .. coords.z
954 end
955end
956
957
958function distance(point_1, point_2)
959 return math.abs(point_1.x - point_2.x)
960 + math.abs(point_1.y - point_2.y)
961 + math.abs(point_1.z - point_2.z)
962end
963
964
965function in_area(xyz, area)
966 return xyz.x <= area.max_x and xyz.x >= area.min_x and xyz.y <= area.max_y and xyz.y >= area.min_y and xyz.z <= area.max_z and xyz.z >= area.min_z
967end
968
969
970function in_location(xyzo, location)
971 for _, axis in pairs({'x', 'y', 'z'}) do
972 if location[axis] then
973 if location[axis] ~= xyzo[axis] then
974 return false
975 end
976 end
977 end
978 return true
979end]===],
980 ["turtle_files/config.lua"] = [===[]===],
981 ["turtle_files/mastermine.lua"] = [===[function parse_requests()
982 -- PROCESS ALL REDNET REQUESTS
983 while #state.requests > 0 do
984 local request = table.remove(state.requests, 1)
985 sender, message, protocol = request[1], request[2], request[3]
986 if message.action == 'shutdown' then
987 os.shutdown()
988 elseif message.action == 'reboot' then
989 os.reboot()
990 elseif message.action == 'update' then
991 os.run({}, '/update')
992 elseif message.request_id == -1 or message.request_id == state.request_id then -- MAKE SURE REQUEST IS CURRENT
993 if state.initialized or message.action == 'initialize' then
994 print('Directive: ' .. message.action)
995 state.busy = true
996 state.success = actions[message.action](unpack(message.data)) -- EXECUTE DESIRED FUNCTION WITH DESIRED ARGUMENTS
997 state.busy = false
998 if not state.success then
999 sleep(1)
1000 end
1001 state.request_id = state.request_id + 1
1002 end
1003 end
1004 end
1005end
1006
1007
1008function main()
1009 state.last_ping = os.clock()
1010 while true do
1011 parse_requests()
1012 sleep(0.3)
1013 end
1014end
1015
1016
1017main()]===],
1018 ["turtle_files/receive.lua"] = [===[-- CONTINUOUSLY RECIEVE REDNET MESSAGES
1019while true do
1020 signal = {rednet.receive('mastermine')}
1021 if signal[2].action == 'shutdown' then
1022 os.shutdown()
1023 elseif signal[2].action == 'reboot' then
1024 os.reboot()
1025 elseif signal[2].action == 'update' then
1026 os.run({}, '/update')
1027 else
1028 table.insert(state.requests, signal)
1029 end
1030end]===],
1031 ["turtle_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS
1032hub_id = tonumber(fs.open('/hub_id', 'r').readAll())
1033
1034while true do
1035
1036 state.item_count = 0
1037 state.empty_slot_count = 16
1038 for slot = 1, 16 do
1039 slot_item_count = turtle.getItemCount(slot)
1040 if slot_item_count > 0 then
1041 state.empty_slot_count = state.empty_slot_count - 1
1042 state.item_count = state.item_count + slot_item_count
1043 end
1044 end
1045
1046 rednet.send(hub_id, {
1047 session_id = state.session_id,
1048 request_id = state.request_id,
1049 turtle_type = state.type,
1050 peripheral_left = state.peripheral_left,
1051 peripheral_right = state.peripheral_right,
1052 updated_not_home = state.updated_not_home,
1053 location = state.location,
1054 orientation = state.orientation,
1055 fuel_level = turtle.getFuelLevel(),
1056 item_count = state.item_count,
1057 empty_slot_count = state.empty_slot_count,
1058 distance = state.distance,
1059 strip = state.strip,
1060 success = state.success,
1061 busy = state.busy,
1062 }, 'turtle_report')
1063
1064 sleep(0.5)
1065
1066end]===],
1067 ["turtle_files/startup.lua"] = [===[-- SET LABEL
1068os.setComputerLabel('Turtle ' .. os.getComputerID())
1069
1070-- INITIALIZE APIS
1071if fs.exists('/apis') then
1072 fs.delete('/apis')
1073end
1074fs.makeDir('/apis')
1075fs.copy('/config.lua', '/apis/config')
1076fs.copy('/state.lua', '/apis/state')
1077fs.copy('/basics.lua', '/apis/basics')
1078fs.copy('/actions.lua', '/apis/actions')
1079os.loadAPI('/apis/config')
1080os.loadAPI('/apis/state')
1081os.loadAPI('/apis/basics')
1082os.loadAPI('/apis/actions')
1083
1084
1085-- OPEN REDNET
1086for _, side in pairs({'back', 'top', 'left', 'right'}) do
1087 if peripheral.getType(side) == 'modem' then
1088 rednet.open(side)
1089 break
1090 end
1091end
1092
1093
1094-- IF UPDATED PRINT "UPDATED"
1095if fs.exists('/updated') then
1096 fs.delete('/updated')
1097 print('UPDATED')
1098 state.updated_not_home = true
1099end
1100
1101
1102-- LAUNCH PROGRAMS AS SEPARATE THREADS
1103multishell.launch({}, '/report.lua')
1104multishell.launch({}, '/receive.lua')
1105multishell.launch({}, '/mastermine.lua')
1106multishell.setTitle(2, 'report')
1107multishell.setTitle(3, 'receive')
1108multishell.setTitle(4, 'mastermine')]===],
1109 ["turtle_files/state.lua"] = [===[request_id = 1
1110requests = {}
1111busy = false]===],
1112 ["turtle_files/update"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll())
1113
1114print('Sending update request...')
1115rednet.send(hub_id, '/disk/turtle_files/', 'update_request')
1116local sender, message, protocal = rednet.receive('update_package')
1117
1118for _, file_name in pairs(fs.list('/')) do
1119 if file_name ~= 'rom' and file_name ~= 'persistent' then
1120 fs.delete(file_name)
1121 end
1122end
1123
1124for file_name, file_contents in pairs(message) do
1125 file = fs.open(file_name, 'w')
1126 file.write(file_contents)
1127 file.close()
1128end
1129
1130os.reboot()]===],
1131 ["turtle_files/updated"] = [===[]===],
1132 ["pocket_files/info.lua"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll())
1133
1134while true do
1135 sender, hub_state, _ = rednet.receive('hub_report')
1136 if sender == hub_id then
1137 term.clear()
1138 term.setCursorPos(1, 1)
1139 term.setTextColor(colors.white)
1140 term.write('POWER: ')
1141 if hub_state.on then
1142 term.setTextColor(colors.green)
1143 print('ON')
1144 else
1145 term.setTextColor(colors.red)
1146 print('OFF')
1147 end
1148 term.setTextColor(colors.white)
1149 term.write('TURTLES PARKED: ')
1150 if hub_state.turtles_parked >= hub_state.turtle_count then
1151 term.setTextColor(colors.green)
1152 else
1153 term.setTextColor(colors.red)
1154 end
1155 term.write(hub_state.turtles_parked)
1156 end
1157end]===],
1158 ["pocket_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS
1159hub_id = tonumber(fs.open('/hub_id', 'r').readAll())
1160
1161while true do
1162
1163 local x, y, z = gps.locate()
1164
1165 rednet.send(hub_id, {
1166 location = {x = x, y = y, z = z},
1167 }, 'pocket_report')
1168
1169 sleep(0.5)
1170
1171end]===],
1172 ["pocket_files/startup.lua"] = [===[-- SET LABEL
1173os.setComputerLabel('pocket ' .. os.getComputerID())
1174
1175
1176-- OPEN REDNET
1177rednet.open('back')
1178
1179
1180-- IF UPDATED PRINT "UPDATED"
1181if fs.exists('/updated') then
1182 fs.delete('/updated')
1183 print('UPDATED')
1184end
1185
1186
1187-- LAUNCH PROGRAMS AS SEPARATE THREADS
1188multishell.launch({}, '/user.lua')
1189multishell.launch({}, '/info.lua')
1190multishell.launch({}, '/report.lua')
1191multishell.setTitle(2, 'usr')
1192multishell.setTitle(3, 'info')
1193multishell.setTitle(4, 'rep')]===],
1194 ["pocket_files/update"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll())
1195rednet.open('back')
1196
1197print('Sending update request...')
1198rednet.broadcast('/disk/pocket_files/', 'update_request')
1199local sender, message, protocal = rednet.receive('update_package')
1200
1201for _, file_name in pairs(fs.list('/')) do
1202 if file_name ~= 'rom' and file_name ~= 'disk' and file_name ~= 'persistent' then
1203 fs.delete(file_name)
1204 end
1205end
1206
1207for file_name, file_contents in pairs(message) do
1208 file = fs.open(file_name, 'w')
1209 file.write(file_contents)
1210 file.close()
1211end
1212
1213os.reboot()]===],
1214 ["pocket_files/updated"] = [===[]===],
1215 ["pocket_files/user.lua"] = [===[-- CONTINUOUSLY AWAIT USER INPUT AND PLACE IN TABLE
1216while true do
1217 rednet.broadcast(read(), 'user_input')
1218end]===],
1219 ["hub_files/basics.lua"] = [===[inf = 1e309
1220
1221bumps = {
1222 north = { 0, 0, -1},
1223 south = { 0, 0, 1},
1224 east = { 1, 0, 0},
1225 west = {-1, 0, 0},
1226}
1227
1228left_shift = {
1229 north = 'west',
1230 south = 'east',
1231 east = 'north',
1232 west = 'south',
1233}
1234
1235right_shift = {
1236 north = 'east',
1237 south = 'west',
1238 east = 'south',
1239 west = 'north',
1240}
1241
1242reverse_shift = {
1243 north = 'south',
1244 south = 'north',
1245 east = 'west',
1246 west = 'east',
1247}
1248
1249function dprint(thing)
1250 -- PRINT; IF TABLE PRINT EACH ITEM
1251 if type(thing) == 'table' then
1252 for k, v in pairs(thing) do
1253 print(tostring(k) .. ': ' .. tostring(v))
1254 end
1255 else
1256 print(thing)
1257 end
1258 return true
1259end
1260
1261
1262function str_xyz(coords, facing)
1263 if facing then
1264 return coords.x .. ',' .. coords.y .. ',' .. coords.z .. ':' .. facing
1265 else
1266 return coords.x .. ',' .. coords.y .. ',' .. coords.z
1267 end
1268end
1269
1270
1271function distance(point_1, point_2)
1272 return math.abs(point_1.x - point_2.x)
1273 + math.abs(point_1.y - point_2.y)
1274 + math.abs(point_1.z - point_2.z)
1275end
1276
1277
1278function in_area(xyz, area)
1279 return xyz.x <= area.max_x and xyz.x >= area.min_x and xyz.y <= area.max_y and xyz.y >= area.min_y and xyz.z <= area.max_z and xyz.z >= area.min_z
1280end
1281
1282
1283function in_location(xyzo, location)
1284 for _, axis in pairs({'x', 'y', 'z'}) do
1285 if location[axis] then
1286 if location[axis] ~= xyzo[axis] then
1287 return false
1288 end
1289 end
1290 end
1291 return true
1292end]===],
1293 ["hub_files/config.lua"] = [===[inf = 1e309
1294
1295---==[ MINE ]==---
1296
1297
1298-- LOCATION OF THE CENTER OF THE MINE
1299-- the y value should be set to the height
1300-- 1 above the surface:
1301--
1302-- Y
1303-- ####### #######
1304-- ####### #######
1305mine_entrance = {x = 104, y = 76, z = 215}
1306c = mine_entrance
1307
1308
1309-- WHETHER OR NOT TURTLES NEED PAIRS
1310-- added this because a good number of
1311-- people were asking for the ability to
1312-- disale chunky turtles in case they
1313-- couldn't access the peripherals mod.
1314-- WARNING: not using chunky turtles will
1315-- result in narcoleptic turtles!
1316use_chunky_turtles = true
1317
1318
1319-- SPACE IN BLOCKS BETWEEN MINESHAFTS
1320-- too close means less chance of finding
1321-- ore veins, too far means longer commute
1322-- times for turtles.
1323grid_width = 8
1324
1325
1326-- MAXIMUM MINING AMOUNT PER TRIP
1327-- PER TURTLE
1328-- most efficient would be to make this
1329-- number huge, but turtles may be gone a
1330-- while (plus harder to recall).
1331mission_length = 150
1332
1333
1334-- MAXIMUM BLOCKS A TURTLE MINES IN A
1335-- SINGLE ORE VEIN
1336-- veins can contain multiple types of ore
1337-- and still count as one. also turtles will
1338-- continue on a vein even when their
1339-- inventory fills up, so this prevents them
1340-- losing too many rousources.
1341vein_max = 64
1342
1343
1344-- EXTRA FUEL FOR TURTLES TO BRING ALONG,
1345-- JUST IN CASE
1346fuel_padding = 30
1347
1348
1349-- FUEL PER ITEM
1350-- for coal default is 80. Other fuel sources
1351-- can be used without changing this number,
1352-- should be fine.
1353fuel_per_unit = 80
1354
1355
1356-- TIME AFTER LAST PING TO DECLARE TURTLE
1357-- DISCONNECTED
1358turtle_timeout = 5
1359
1360
1361-- TIME AFTER LAST PING TO DECLARE POCKET
1362-- COMPUTER DISCONNECTED
1363pocket_timeout = 5
1364
1365
1366-- TIME TO WAIT AFTER SENDING TASK WITH NO
1367-- RESPONSE TO RESEND
1368task_timeout = 0.5
1369
1370
1371-- EVERY BLOCK NAME CONTAINING ANY OF THESE
1372-- STRINGS WILL NOT BE MINED
1373-- e.g. "chest" will prevent "minecraft:trapped_chest".
1374-- ore types should not be put on this list,
1375-- but if not desired should be removed from
1376-- <orenames> below.
1377dig_disallow = {
1378 'computer',
1379 'chest',
1380 'chair',
1381}
1382
1383
1384mine_levels = {
1385 -- LEVELS INCLUDED IN THE MINE
1386 -- turtles will pick randomly with weight
1387 -- between each listed level.
1388 --
1389 -- Level chances should sum to 1.0
1390 -- e.g.
1391 --
1392 -- {level = 50, chance = 0.3},
1393 -- {level = 40, chance = 0.2},
1394 -- {level = 12, chance = 0.5},
1395
1396 {level = 63, chance = 1.0},
1397}
1398
1399
1400paths = {
1401 -- THE ORDER IN WHICH TURTLES WILL
1402 -- TRAVERSE AXES BETWEEN AREAS
1403 -- recommended not to change this one.
1404 home_to_home_exit = 'zyx',
1405 control_room_to_home_enter = 'yzx',
1406 home_to_waiting_room = 'zyx',
1407 waiting_room_to_mine_exit = 'yzx',
1408 mine_enter_to_strip = 'yxz',
1409}
1410
1411
1412locations = {
1413 -- THE VARIUS PLACES THE TURTLES MOVE
1414 -- BETWEEN
1415 -- coordinates are relative to the
1416 -- <mine_center> variable. areas are for
1417 -- altering turtle behavior to prevent
1418 -- collisions and stuff.
1419
1420 -- THE BLOCK TURTLES WILL GO TO BEFORE
1421 -- DECENDING
1422 mine_enter = {x = c.x+0, y = c.y+0, z = c.z+0},
1423
1424 -- THE BLOCK TURTLES WILL COME UP TO
1425 -- FROM THE MINE
1426 -- one block higher by default.
1427 mine_exit = {x = c.x+0, y = c.y+1, z = c.z+1},
1428
1429 -- THE BLOCK TURTLES GO TO IN ORDER
1430 -- TO ACCESS THE CHEST FOR ITEMS
1431 item_drop = {x = c.x+2, y = c.y+1, z = c.z+1, orientation = 'east'},
1432
1433 -- THE BLOCK TURTLES GO TO IN ORDER
1434 -- TO ACCESS THE CHEST FOR FUEL
1435 refuel = {x = c.x+2, y = c.y+1, z = c.z+0, orientation = 'east'},
1436
1437 -- THE AREA ENCOMPASSING TURTLE HOMES
1438 -- where they sleep.
1439 greater_home_area = {
1440 min_x = -inf,
1441 max_x = c.x-3,
1442 min_y = c.y+0,
1443 max_y = c.y+1,
1444 min_z = c.z-1,
1445 max_z = c.z+2
1446 },
1447
1448 -- THE ROOM WHERE THE MAGIC HAPPENS
1449 -- turtles can find there way home from
1450 -- here.
1451 control_room_area = {
1452 min_x = c.x-16,
1453 max_x = c.x+8,
1454 min_y = c.y+0,
1455 max_y = c.y+8,
1456 min_z = c.z-8,
1457 max_z = c.z+8
1458 },
1459
1460 -- WHERE TURTLES QUEUE TO BE PAIRED UP
1461 waiting_room_line_area = {
1462 min_x = -inf,
1463 max_x = c.x-2,
1464 min_y = c.y+0,
1465 max_y = c.y+0,
1466 min_z = c.z+0,
1467 max_z = c.z+1
1468 },
1469
1470 -- THE AREA ENCOMPASSING BOTH WHERE
1471 -- TURTLES PAIR UP, AND THE PATH THEY
1472 -- TAKE TO THE MINE ENTRANCE
1473 waiting_room_area = {
1474 min_x = c.x-2,
1475 max_x = c.x+0,
1476 min_y = c.y+0,
1477 max_y = c.y+0,
1478 min_z = c.z+0,
1479 max_z = c.z+1
1480 },
1481
1482 -- THE LOOP TURTLES GO IN BETWEEN THEIR
1483 -- HOMES, THE ITEM DROP STATION, AND THE
1484 -- REFUELING STATION
1485 -- routes work like linked lists.
1486 -- keys are current positions, and
1487 -- values are the associated ajecent
1488 -- blocks to move to. this loop should
1489 -- be closed, and it should not be
1490 -- possible for a collision to occur
1491 -- between a turtle following the loop,
1492 -- and a turtle pairing, traveling to
1493 -- the mine entrance, or any other
1494 -- movement.
1495 main_loop_route = {
1496
1497 -- MINING TURTLE HOME ENTER
1498 [c.x-1 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-2, y = c.y+1, z = c.z-1},
1499
1500 -- MINING TURTLE HOME EXIT
1501 [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-2, y = c.y+1, z = c.z+0},
1502
1503 -- CHUNKY TURTLE HOME EXIT
1504 [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+0] = {x = c.x-2, y = c.y+1, z = c.z+1},
1505
1506 -- CHUNKY TURTLE HOME ENTER
1507 [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x-2, y = c.y+1, z = c.z+2},
1508
1509 [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x-1, y = c.y+1, z = c.z+2},
1510 [c.x-1 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x+0, y = c.y+1, z = c.z+2},
1511 [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x+0, y = c.y+1, z = c.z+1},
1512 [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+1, y = c.y+1, z = c.z+1},
1513
1514 -- ITEM DROP STATION
1515 [c.x+1 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+2, y = c.y+1, z = c.z+1},
1516
1517 -- REFUELING STATION
1518 [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+2, y = c.y+1, z = c.z+0},
1519
1520 [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z+0] = {x = c.x+2, y = c.y+1, z = c.z-1},
1521 [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x+1, y = c.y+1, z = c.z-1},
1522 [c.x+1 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x+0, y = c.y+1, z = c.z-1},
1523 [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-1, y = c.y+1, z = c.z-1},
1524 },
1525}
1526
1527
1528mining_turtle_locations = {
1529 -- LOCATIONS THAT ARE SPECIFIC TO
1530 -- MINING TURTLES
1531
1532 -- TURTLE HOMES
1533 -- this is where the first turtle parking
1534 -- spot will be, and each following will
1535 -- be in the <increment> direction.
1536 homes = {x = c.x-3, y = c.y+0, z = c.z-3, increment = 'west'},
1537
1538 -- THE AREA ENCOMPASSING THE HOME
1539 -- LINE, AS WELL AS THE PATH TURTLES
1540 -- TAKE TO GET TO THEIR HOME
1541 home_area = {
1542 min_x = -inf,
1543 max_x = c.x-3,
1544 min_y = c.y+0,
1545 max_y = c.y+0,
1546 min_z = c.z-1,
1547 max_z = c.z-1
1548 },
1549
1550 -- WHERE TURTLES ENTER THE LINE TO
1551 -- GET TO THEIR HOME
1552 home_enter = {x = c.x-2, y = c.y+1, z = c.z-1, orientation = 'west'},
1553
1554 -- WHERE TURTLES EXIT THEIR HOMES
1555 home_exit = {x = c.x-2, y = c.y+1, z = c.z+0},
1556
1557 -- WHERE TURTLES WAIT TO BE PAIRED
1558 waiting_room = {x = c.x-2, y = c.y+0, z = c.z+0},
1559
1560 -- THE PATH TURTLES WILL TAKE AFTER
1561 -- PAIRING
1562 waiting_room_to_mine_enter_route = {
1563 [c.x-2 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x-1, y = c.y+0, z = c.z+0},
1564 [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x+0, y = c.y+0, z = c.z+0},
1565 }
1566}
1567
1568
1569chunky_turtle_locations = {
1570 -- LOCATIONS THAT ARE SPECIFIC TO
1571 -- MINING TURTLES
1572
1573 -- TURTLE HOMES
1574 -- this is where the first turtle parking
1575 -- spot will be, and each following will
1576 -- be in the <increment> direction.
1577 homes = {x = c.x-3, y = c.y+0, z = c.z+2, increment = 'west'},
1578
1579 -- THE AREA ENCOMPASSING THE HOME
1580 -- LINE, AS WELL AS THE PATH TURTLES
1581 -- TAKE TO GET TO THEIR HOME
1582 home_area = {
1583 min_x = -inf,
1584 max_x = c.x-3,
1585 min_y = c.y+0,
1586 max_y = c.y+0,
1587 min_z = c.z+2,
1588 max_z = c.z+2
1589 },
1590
1591 -- WHERE TURTLES ENTER THE LINE TO
1592 -- GET TO THEIR HOME
1593 home_enter = {x = c.x-2, y = c.y+1, z = c.z+2, orientation = 'west'},
1594
1595 -- WHERE TURTLES EXIT THEIR HOMES
1596 home_exit = {x = c.x-2, y = c.y+1, z = c.z+1},
1597
1598 -- WHERE TURTLES WAIT TO BE PAIRED
1599 waiting_room = {x = c.x-2, y = c.y+0, z = c.z+1},
1600
1601 -- THE PATH TURTLES WILL TAKE AFTER
1602 -- PAIRING
1603 waiting_room_to_mine_enter_route = {
1604 [c.x-2 .. ',' .. c.y+0 .. ',' .. c.z+1] = {x = c.x-1, y = c.y+0, z = c.z+1},
1605 [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+1] = {x = c.x-1, y = c.y+0, z = c.z+0},
1606 [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x+0, y = c.y+0, z = c.z+0},
1607 }
1608}
1609
1610
1611gravitynames = {
1612 -- ALL BLOCKS AFFECTED BY GRAVITY
1613 -- if a turtle sees these it will take
1614 -- extra care to make sure they're delt
1615 -- with. works at least a lot percent of
1616 -- the time
1617 ['minecraft:gravel'] = true,
1618 ['minecraft:sand'] = true,
1619}
1620
1621
1622orenames = {
1623 -- ALL THE BLOCKS A TURTLE CONSIDERS ORE
1624 -- a turtle will continue to mine out a
1625 -- vein until it reaches <vein_max> or
1626 -- it stops seeing blocks with names in
1627 -- this list. block names are exact.
1628 ['BigReactors:YelloriteOre'] = true,
1629 ['bigreactors:oreyellorite'] = true,
1630 ['DraconicEvolution:draconiumDust'] = true,
1631 ['DraconicEvolution:draconiumOre'] = true,
1632 ['Forestry:apatite'] = true,
1633 ['Forestry:resources'] = true,
1634 ['IC2:blockOreCopper'] = true,
1635 ['IC2:blockOreLead'] = true,
1636 ['IC2:blockOreTin'] = true,
1637 ['IC2:blockOreUran'] = true,
1638 ['ic2:resource'] = true,
1639 ['ProjRed|Core:projectred.core.part'] = true,
1640 ['ProjRed|Exploration:projectred.exploration.ore'] = true,
1641 ['TConstruct:SearedBrick'] = true,
1642 ['ThermalFoundation:Ore'] = true,
1643 ['thermalfoundation:ore'] = true,
1644 ['thermalfoundation:ore_fluid'] = true,
1645 ['thaumcraft:ore_amber'] = true,
1646 ['minecraft:coal'] = true,
1647 ['minecraft:coal_ore'] = true,
1648 ['minecraft:diamond'] = true,
1649 ['minecraft:diamond_ore'] = true,
1650 ['minecraft:dye'] = true,
1651 ['minecraft:emerald'] = true,
1652 ['minecraft:emerald_ore'] = true,
1653 ['minecraft:gold_ore'] = true,
1654 ['minecraft:iron_ore'] = true,
1655 ['minecraft:lapis_ore'] = true,
1656 ['minecraft:redstone'] = true,
1657 ['minecraft:redstone_ore'] = true,
1658 ['galacticraftcore:basic_block_core'] = true,
1659 ['mekanism:oreblock'] = true,
1660 ['appliedenergistics2:quartz_ore'] = true
1661}
1662
1663blocktags = {
1664 -- ALL BLOCKS WITH ONE OF THESE TAGS A TURTLE CONSIDERS ORE
1665 -- most mods categorize ores with the forge:ores tag.
1666 -- this is an easy way to detect all but a few ores,
1667 -- which don't posess this exact tag (for example certus quartzfrom AE2)
1668 ['forge:ores'] = true,
1669 -- adds Certus Quartz and Charged Certus Quartz
1670 ['forge:ores/certus_quartz'] = true
1671}
1672
1673fuelnames = {
1674 -- ITEMS THE TURTLE CONSIDERS FUEL
1675 ['minecraft:coal'] = true,
1676}
1677
1678
1679---==[ SCREEN ]==---
1680
1681
1682-- MAXIMUM ZOOM OUT (INVERSE) OF THE
1683-- MAP SCREEN
1684monitor_max_zoom_level = 5
1685
1686
1687-- DEFAULT ZOOM OF THE MAP SCREEN
1688-- 0 is [1 pixel : 1 block]
1689default_monitor_zoom_level = 0
1690
1691
1692-- CENTER OF THE MAP SCREEN
1693-- probably want the mine center
1694default_monitor_location = {x = c.x, z = c.z}]===],
1695 ["hub_files/events.lua"] = [===[while true do
1696 event = {os.pullEvent()}
1697 if event[1] == 'rednet_message' then
1698 local sender = event[2]
1699 local message = event[3]
1700 local protocol = event[4]
1701
1702 if protocol == 'user_input' then
1703 table.insert(state.user_input, message)
1704
1705 elseif protocol == 'turtle_report' then
1706 if not state.turtles[sender] then
1707 state.turtles[sender] = {id = sender}
1708 end
1709 state.turtles[sender].data = message
1710 state.turtles[sender].last_update = os.clock()
1711
1712 elseif protocol == 'pocket_report' then
1713 if not state.pockets[sender] then
1714 state.pockets[sender] = {id = sender}
1715 end
1716 state.pockets[sender].data = message
1717 state.pockets[sender].last_update = os.clock()
1718
1719 elseif protocol == 'update_request' then
1720 if fs.isDir(message) then
1721 local update_package = {}
1722 local queue = {''}
1723 while #queue > 0 do
1724 dir_name = table.remove(queue)
1725 path_name = fs.combine(message, dir_name)
1726 for _, object_name in pairs(fs.list(path_name)) do
1727 sub_dir_name = fs.combine(dir_name, object_name)
1728 sub_path_name = fs.combine(message, sub_dir_name)
1729 if fs.isDir(sub_path_name) then
1730 table.insert(queue, sub_dir_name)
1731 else
1732 local file = fs.open(sub_path_name, 'r')
1733 update_package[sub_dir_name] = file.readAll()
1734 file.close()
1735 end
1736 end
1737 end
1738 update_package.hub_id = os.getComputerID()
1739 rednet.send(sender, update_package, 'update_package')
1740 end
1741 end
1742
1743 elseif event[1] == 'monitor_touch' then
1744 if state.monitor_touches then
1745 table.insert(state.monitor_touches, {x = event[3], y = event[4]})
1746 end
1747 end
1748end]===],
1749 ["hub_files/monitor.lua"] = [===[menu_lines = {
1750 '# # ##### # # #####',
1751 '## ## # ## # #',
1752 '# # # # # # # ###',
1753 '# # # # ## #',
1754 '# # ##### # # #',
1755}
1756
1757decimals = {
1758 [0] = {
1759 '#####',
1760 '# #',
1761 '# #',
1762 '# #',
1763 '#####',
1764 },
1765 [1] = {
1766 '### ',
1767 ' # ',
1768 ' # ',
1769 ' # ',
1770 '#####',
1771 },
1772 [2] = {
1773 '#####',
1774 ' #',
1775 '#####',
1776 '# ',
1777 '#####',
1778 },
1779 [3] = {
1780 '#####',
1781 ' #',
1782 '#####',
1783 ' #',
1784 '#####',
1785 },
1786 [4] = {
1787 '# #',
1788 '# #',
1789 '#####',
1790 ' #',
1791 ' #',
1792 },
1793 [5] = {
1794 '#####',
1795 '# ',
1796 '#####',
1797 ' #',
1798 '#####',
1799 },
1800 [6] = {
1801 '#####',
1802 '# ',
1803 '#####',
1804 '# #',
1805 '#####',
1806 },
1807 [7] = {
1808 '#####',
1809 ' #',
1810 ' #',
1811 ' #',
1812 ' #',
1813 },
1814 [8] = {
1815 '#####',
1816 '# #',
1817 '#####',
1818 '# #',
1819 '#####',
1820 },
1821 [9] = {
1822 '#####',
1823 '# #',
1824 '#####',
1825 ' #',
1826 ' #',
1827 },
1828}
1829
1830function debug_print(string)
1831 term.redirect(monitor.restore_to)
1832 print(string)
1833 term.redirect(monitor)
1834end
1835
1836function turtle_viewer(turtle_ids)
1837 term.redirect(monitor)
1838
1839 local selected = 1
1840
1841 while true do
1842 local turtle_id = turtle_ids[selected]
1843 local turtle = state.turtles[turtle_id]
1844
1845 -- RESOLVE MONITOR TOUCHES, EITHER BY AFFECTING THE DISPLAY OR INSERTING INTO USER_INPUT TABLE
1846 while #state.monitor_touches > 0 do
1847 local monitor_touch = table.remove(state.monitor_touches)
1848 if monitor_touch.x == elements.left.x and monitor_touch.y == elements.left.y then
1849 selected = math.max(selected - 1, 1)
1850 elseif monitor_touch.x == elements.right.x and monitor_touch.y == elements.right.y then
1851 selected = math.min(selected + 1, #turtle_ids)
1852 elseif monitor_touch.x == elements.viewer_exit.x and monitor_touch.y == elements.viewer_exit.y then
1853 term.redirect(monitor.restore_to)
1854 return
1855 elseif monitor_touch.x == elements.turtle_return.x and monitor_touch.y == elements.turtle_return.y then
1856 table.insert(state.user_input, 'return ' .. turtle_id)
1857 elseif monitor_touch.x == elements.turtle_update.x and monitor_touch.y == elements.turtle_update.y then
1858 table.insert(state.user_input, 'update ' .. turtle_id)
1859 elseif monitor_touch.x == elements.turtle_reboot.x and monitor_touch.y == elements.turtle_reboot.y then
1860 table.insert(state.user_input, 'reboot ' .. turtle_id)
1861 elseif monitor_touch.x == elements.turtle_halt.x and monitor_touch.y == elements.turtle_halt.y then
1862 table.insert(state.user_input, 'halt ' .. turtle_id)
1863 elseif monitor_touch.x == elements.turtle_clear.x and monitor_touch.y == elements.turtle_clear.y then
1864 table.insert(state.user_input, 'clear ' .. turtle_id)
1865 elseif monitor_touch.x == elements.turtle_reset.x and monitor_touch.y == elements.turtle_reset.y then
1866 table.insert(state.user_input, 'reset ' .. turtle_id)
1867 elseif monitor_touch.x == elements.turtle_find.x and monitor_touch.y == elements.turtle_find.y then
1868 monitor_location.x = turtle.data.location.x
1869 monitor_location.z = turtle.data.location.z
1870 monitor_zoom_level = 0
1871 for level_index, level_and_chance in pairs(config.mine_levels) do
1872 if turtle.strip and level_and_chance.level == turtle.strip.y then
1873 monitor_level_index = level_index
1874 select_mine_level()
1875 break
1876 end
1877 end
1878 term.redirect(monitor.restore_to)
1879 return
1880 elseif monitor_touch.x == elements.turtle_forward.x and monitor_touch.y == elements.turtle_forward.y then
1881 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go forward')
1882 elseif monitor_touch.x == elements.turtle_back.x and monitor_touch.y == elements.turtle_back.y then
1883 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go back')
1884 elseif monitor_touch.x == elements.turtle_up.x and monitor_touch.y == elements.turtle_up.y then
1885 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go up')
1886 elseif monitor_touch.x == elements.turtle_down.x and monitor_touch.y == elements.turtle_down.y then
1887 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go down')
1888 elseif monitor_touch.x == elements.turtle_left.x and monitor_touch.y == elements.turtle_left.y then
1889 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go left')
1890 elseif monitor_touch.x == elements.turtle_right.x and monitor_touch.y == elements.turtle_right.y then
1891 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go right')
1892 elseif turtle.data.turtle_type == 'mining' then
1893 if monitor_touch.x == elements.turtle_dig_up.x and monitor_touch.y == elements.turtle_dig_up.y then
1894 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock up')
1895 elseif monitor_touch.x == elements.turtle_dig.x and monitor_touch.y == elements.turtle_dig.y then
1896 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock forward')
1897 elseif monitor_touch.x == elements.turtle_dig_down.x and monitor_touch.y == elements.turtle_dig_down.y then
1898 table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock down')
1899 end
1900 end
1901 end
1902
1903 turtle_id = turtle_ids[selected]
1904 turtle = state.turtles[turtle_id]
1905
1906 background_color = colors.black
1907 term.setBackgroundColor(background_color)
1908 monitor.clear()
1909
1910 if turtle.last_update + config.turtle_timeout < os.clock() then
1911 term.setCursorPos(elements.turtle_lost.x, elements.turtle_lost.y)
1912 term.setTextColor(colors.red)
1913 term.write('CONNECTION LOST')
1914 end
1915
1916 local x_position = elements.turtle_id.x
1917 for decimal_string in string.format('%04d', turtle_id):gmatch"." do
1918 for y_offset, line in pairs(decimals[tonumber(decimal_string)]) do
1919 term.setCursorPos(x_position, elements.turtle_id.y + y_offset - 1)
1920 for char in line:gmatch"." do
1921 if char == '#' then
1922 term.setBackgroundColor(colors.green)
1923 else
1924 term.setBackgroundColor(colors.black)
1925 end
1926 term.write(' ')
1927 end
1928 end
1929 x_position = x_position + 6
1930 end
1931
1932 term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y)
1933 term.setBackgroundColor(colors.yellow)
1934 term.write(' ')
1935 term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 1)
1936 term.setBackgroundColor(colors.yellow)
1937 term.write(' ')
1938 term.setBackgroundColor(colors.gray)
1939 term.write(' ')
1940 term.setBackgroundColor(colors.yellow)
1941 term.write(' ')
1942 term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 2)
1943 term.setBackgroundColor(colors.yellow)
1944 term.write(' ')
1945 term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 3)
1946 term.setBackgroundColor(colors.yellow)
1947 term.write(' ')
1948 term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 4)
1949 term.setBackgroundColor(colors.yellow)
1950 term.write(' ')
1951
1952 if turtle.data.peripheral_right == 'modem' then
1953 term.setBackgroundColor(colors.lightGray)
1954 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1)
1955 term.write(' ')
1956 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2)
1957 term.write(' ')
1958 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3)
1959 term.write(' ')
1960 elseif turtle.data.peripheral_right == 'pick' then
1961 term.setBackgroundColor(colors.cyan)
1962 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1)
1963 term.write(' ')
1964 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2)
1965 term.write(' ')
1966 term.setBackgroundColor(colors.brown)
1967 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3)
1968 term.write(' ')
1969 elseif turtle.data.peripheral_right == 'chunkLoader' then
1970 term.setBackgroundColor(colors.gray)
1971 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1)
1972 term.write(' ')
1973 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3)
1974 term.write(' ')
1975 term.setBackgroundColor(colors.blue)
1976 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2)
1977 term.write(' ')
1978 elseif turtle.data.peripheral_right == 'chunky' then
1979 term.setBackgroundColor(colors.white)
1980 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1)
1981 term.write(' ')
1982 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3)
1983 term.write(' ')
1984 term.setBackgroundColor(colors.red)
1985 term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2)
1986 term.write(' ')
1987 end
1988
1989 if turtle.data.peripheral_left == 'modem' then
1990 term.setBackgroundColor(colors.lightGray)
1991 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1)
1992 term.write(' ')
1993 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2)
1994 term.write(' ')
1995 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3)
1996 term.write(' ')
1997 elseif turtle.data.peripheral_left == 'pick' then
1998 term.setBackgroundColor(colors.cyan)
1999 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1)
2000 term.write(' ')
2001 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2)
2002 term.write(' ')
2003 term.setBackgroundColor(colors.brown)
2004 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3)
2005 term.write(' ')
2006 elseif turtle.data.peripheral_left == 'chunkLoader' then
2007 term.setBackgroundColor(colors.gray)
2008 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1)
2009 term.write(' ')
2010 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3)
2011 term.write(' ')
2012 term.setBackgroundColor(colors.blue)
2013 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2)
2014 term.write(' ')
2015 elseif turtle.data.peripheral_left == 'chunky' then
2016 term.setBackgroundColor(colors.white)
2017 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1)
2018 term.write(' ')
2019 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3)
2020 term.write(' ')
2021 term.setBackgroundColor(colors.red)
2022 term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2)
2023 term.write(' ')
2024 end
2025
2026 term.setBackgroundColor(background_color)
2027
2028 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y)
2029 term.setTextColor(colors.white)
2030 term.write('State: ')
2031 term.setTextColor(colors.green)
2032 term.write(turtle.state)
2033
2034 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 1)
2035 term.setTextColor(colors.white)
2036 term.write('X: ')
2037 term.setTextColor(colors.green)
2038 if turtle.data.location then
2039 term.write(turtle.data.location.x)
2040 end
2041
2042 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 2)
2043 term.setTextColor(colors.white)
2044 term.write('Y: ')
2045 term.setTextColor(colors.green)
2046 if turtle.data.location then
2047 term.write(turtle.data.location.y)
2048 end
2049
2050 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 3)
2051 term.setTextColor(colors.white)
2052 term.write('Z: ')
2053 term.setTextColor(colors.green)
2054 if turtle.data.location then
2055 term.write(turtle.data.location.z)
2056 end
2057
2058 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 4)
2059 term.setTextColor(colors.white)
2060 term.write('Facing: ')
2061 term.setTextColor(colors.green)
2062 term.write(turtle.data.orientation)
2063
2064 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 5)
2065 term.setTextColor(colors.white)
2066 term.write('Fuel: ')
2067 term.setTextColor(colors.green)
2068 term.write(turtle.data.fuel_level)
2069
2070 term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 6)
2071 term.setTextColor(colors.white)
2072 term.write('Items: ')
2073 term.setTextColor(colors.green)
2074 term.write(turtle.data.item_count)
2075
2076-- term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 7)
2077-- term.setTextColor(colors.white)
2078-- term.write('Dist: ')
2079-- term.setTextColor(colors.green)
2080-- term.write(turtle.data.distance)
2081
2082 term.setTextColor(colors.white)
2083
2084 term.setCursorPos(elements.turtle_return.x, elements.turtle_return.y)
2085 term.setBackgroundColor(colors.green)
2086 term.write('*')
2087 term.setBackgroundColor(colors.brown)
2088 term.write('-RETURN')
2089
2090 term.setCursorPos(elements.turtle_update.x, elements.turtle_update.y)
2091 term.setBackgroundColor(colors.green)
2092 term.write('*')
2093 term.setBackgroundColor(colors.brown)
2094 term.write('-UPDATE')
2095
2096 term.setCursorPos(elements.turtle_reboot.x, elements.turtle_reboot.y)
2097 term.setBackgroundColor(colors.green)
2098 term.write('*')
2099 term.setBackgroundColor(colors.brown)
2100 term.write('-REBOOT')
2101
2102 term.setCursorPos(elements.turtle_halt.x, elements.turtle_halt.y)
2103 term.setBackgroundColor(colors.green)
2104 term.write('*')
2105 term.setBackgroundColor(colors.brown)
2106 term.write('-HALT')
2107
2108 term.setCursorPos(elements.turtle_clear.x, elements.turtle_clear.y)
2109 term.setBackgroundColor(colors.green)
2110 term.write('*')
2111 term.setBackgroundColor(colors.brown)
2112 term.write('-CLEAR')
2113
2114 term.setCursorPos(elements.turtle_reset.x, elements.turtle_reset.y)
2115 term.setBackgroundColor(colors.green)
2116 term.write('*')
2117 term.setBackgroundColor(colors.brown)
2118 term.write('-RESET')
2119
2120 term.setCursorPos(elements.turtle_find.x, elements.turtle_find.y)
2121 term.setBackgroundColor(colors.green)
2122 term.write('*')
2123 term.setBackgroundColor(colors.brown)
2124 term.write('-FIND')
2125
2126 term.setCursorPos(elements.turtle_forward.x, elements.turtle_forward.y)
2127 term.setTextColor(colors.white)
2128 term.setBackgroundColor(colors.green)
2129 term.write('^')
2130 term.setTextColor(colors.gray)
2131 term.setBackgroundColor(background_color)
2132 term.write('-FORWARD')
2133
2134 term.setCursorPos(elements.turtle_back.x, elements.turtle_back.y)
2135 term.setTextColor(colors.white)
2136 term.setBackgroundColor(colors.green)
2137 term.write('V')
2138 term.setTextColor(colors.gray)
2139 term.setBackgroundColor(background_color)
2140 term.write('-BACK')
2141
2142 term.setCursorPos(elements.turtle_up.x, elements.turtle_up.y)
2143 term.setTextColor(colors.white)
2144 term.setBackgroundColor(colors.green)
2145 term.write('^')
2146 term.setTextColor(colors.gray)
2147 term.setBackgroundColor(background_color)
2148 term.write('-UP')
2149
2150 term.setCursorPos(elements.turtle_down.x, elements.turtle_down.y)
2151 term.setTextColor(colors.white)
2152 term.setBackgroundColor(colors.green)
2153 term.write('V')
2154 term.setTextColor(colors.gray)
2155 term.setBackgroundColor(background_color)
2156 term.write('-DOWN')
2157
2158 term.setCursorPos(elements.turtle_left.x, elements.turtle_left.y)
2159 term.setTextColor(colors.white)
2160 term.setBackgroundColor(colors.green)
2161 term.write('<')
2162 term.setTextColor(colors.gray)
2163 term.setBackgroundColor(background_color)
2164 term.write('-LEFT')
2165
2166 term.setCursorPos(elements.turtle_right.x, elements.turtle_right.y)
2167 term.setTextColor(colors.white)
2168 term.setBackgroundColor(colors.green)
2169 term.write('>')
2170 term.setTextColor(colors.gray)
2171 term.setBackgroundColor(background_color)
2172 term.write('-RIGHT')
2173
2174 term.setCursorPos(elements.turtle_dig_up.x, elements.turtle_dig_up.y)
2175 term.setTextColor(colors.white)
2176 if turtle.data.turtle_type == 'mining' then
2177 term.setBackgroundColor(colors.green)
2178 else
2179 term.setBackgroundColor(colors.gray)
2180 end
2181 term.write('^')
2182
2183 term.setCursorPos(elements.turtle_dig.x, elements.turtle_dig.y)
2184 term.setTextColor(colors.white)
2185 if turtle.data.turtle_type == 'mining' then
2186 term.setBackgroundColor(colors.green)
2187 else
2188 term.setBackgroundColor(colors.gray)
2189 end
2190 term.write('*')
2191 term.setTextColor(colors.gray)
2192 term.setBackgroundColor(background_color)
2193 term.write('-DIG')
2194
2195 term.setCursorPos(elements.turtle_dig_down.x, elements.turtle_dig_down.y)
2196 term.setTextColor(colors.white)
2197 if turtle.data.turtle_type == 'mining' then
2198 term.setBackgroundColor(colors.green)
2199 else
2200 term.setBackgroundColor(colors.gray)
2201 end
2202 term.write('v')
2203
2204 term.setTextColor(colors.white)
2205 if selected == 1 then
2206 term.setBackgroundColor(colors.gray)
2207 else
2208 term.setBackgroundColor(colors.green)
2209 end
2210 term.setCursorPos(elements.left.x, elements.left.y)
2211 term.write('<')
2212 if selected == #turtle_ids then
2213 term.setBackgroundColor(colors.gray)
2214 else
2215 term.setBackgroundColor(colors.green)
2216 end
2217 term.setCursorPos(elements.right.x, elements.right.y)
2218 term.write('>')
2219 term.setBackgroundColor(colors.red)
2220 term.setCursorPos(elements.viewer_exit.x, elements.viewer_exit.y)
2221 term.write('x')
2222
2223 monitor.setVisible(true)
2224 monitor.setVisible(false)
2225
2226 sleep(sleep_len)
2227 end
2228end
2229
2230
2231function menu()
2232 term.redirect(monitor)
2233
2234 while true do
2235 while #state.monitor_touches > 0 do
2236 local monitor_touch = table.remove(state.monitor_touches)
2237 if monitor_touch.x == elements.viewer_exit.x and monitor_touch.y == elements.viewer_exit.y then
2238 term.redirect(monitor.restore_to)
2239 return
2240 elseif monitor_touch.x == elements.menu_toggle.x and monitor_touch.y == elements.menu_toggle.y then
2241 if state.on then
2242 table.insert(state.user_input, 'off')
2243 else
2244 table.insert(state.user_input, 'on')
2245 end
2246 elseif monitor_touch.x == elements.menu_update.x and monitor_touch.y == elements.menu_update.y then
2247 table.insert(state.user_input, 'update')
2248 elseif monitor_touch.x == elements.menu_return.x and monitor_touch.y == elements.menu_return.y then
2249 table.insert(state.user_input, 'return')
2250 elseif monitor_touch.x == elements.menu_reboot.x and monitor_touch.y == elements.menu_reboot.y then
2251 table.insert(state.user_input, 'reboot')
2252 elseif monitor_touch.x == elements.menu_halt.x and monitor_touch.y == elements.menu_halt.y then
2253 table.insert(state.user_input, 'halt')
2254 elseif monitor_touch.x == elements.menu_clear.x and monitor_touch.y == elements.menu_clear.y then
2255 table.insert(state.user_input, 'clear')
2256 elseif monitor_touch.x == elements.menu_reset.x and monitor_touch.y == elements.menu_reset.y then
2257 table.insert(state.user_input, 'reset')
2258 end
2259 end
2260
2261 term.setBackgroundColor(colors.black)
2262 monitor.clear()
2263
2264 term.setTextColor(colors.white)
2265 term.setCursorPos(elements.menu_title.x, elements.menu_title.y)
2266 term.write('MASTER')
2267
2268 for y_offset, line in pairs(menu_lines) do
2269 term.setCursorPos(elements.menu_title.x, elements.menu_title.y + y_offset)
2270 for char in line:gmatch"." do
2271 if char == '#' then
2272 if state.on then
2273 term.setBackgroundColor(colors.lime)
2274 else
2275 term.setBackgroundColor(colors.red)
2276 end
2277 else
2278 term.setBackgroundColor(colors.black)
2279 end
2280 term.write(' ')
2281 end
2282 end
2283
2284 term.write('.lua')
2285
2286 term.setBackgroundColor(colors.red)
2287 term.setCursorPos(elements.viewer_exit.x, elements.viewer_exit.y)
2288 term.write('x')
2289 term.setBackgroundColor(colors.green)
2290 term.setCursorPos(elements.menu_toggle.x, elements.menu_toggle.y)
2291 term.write('*')
2292 term.setCursorPos(elements.menu_return.x, elements.menu_return.y)
2293 term.write('*')
2294 term.setCursorPos(elements.menu_update.x, elements.menu_update.y)
2295 term.write('*')
2296 term.setCursorPos(elements.menu_reboot.x, elements.menu_reboot.y)
2297 term.write('*')
2298 term.setCursorPos(elements.menu_halt.x, elements.menu_halt.y)
2299 term.write('*')
2300 term.setCursorPos(elements.menu_clear.x, elements.menu_clear.y)
2301 term.write('*')
2302 term.setCursorPos(elements.menu_reset.x, elements.menu_reset.y)
2303 term.write('*')
2304 term.setBackgroundColor(colors.brown)
2305 term.setCursorPos(elements.menu_toggle.x + 1, elements.menu_toggle.y)
2306 term.write('-TOGGLE POWER')
2307 term.setCursorPos(elements.menu_update.x + 1, elements.menu_update.y)
2308 term.write('-UPDATE')
2309 term.setCursorPos(elements.menu_return.x + 1, elements.menu_return.y)
2310 term.write('-RETURN')
2311 term.setCursorPos(elements.menu_reboot.x + 1, elements.menu_reboot.y)
2312 term.write('-REBOOT')
2313 term.setCursorPos(elements.menu_halt.x + 1, elements.menu_halt.y)
2314 term.write('-HALT')
2315 term.setCursorPos(elements.menu_clear.x + 1, elements.menu_clear.y)
2316 term.write('-CLEAR')
2317 term.setCursorPos(elements.menu_reset.x + 1, elements.menu_reset.y)
2318 term.write('-RESET')
2319
2320 monitor.setVisible(true)
2321 monitor.setVisible(false)
2322
2323 sleep(sleep_len)
2324 end
2325end
2326
2327
2328function draw_location(location, color)
2329 if location then
2330 local pixel = {
2331 -- x = monitor_width - math.floor((location.x - min_location.x) / zoom_factor),
2332 -- y = monitor_height - math.floor((location.z - min_location.z) / zoom_factor),
2333 x = math.floor((location.x - min_location.x) / zoom_factor),
2334 y = math.floor((location.z - min_location.z) / zoom_factor),
2335 }
2336 if pixel.x >= 1 and pixel.x <= monitor_width and pixel.y >= 1 and pixel.y <= monitor_height then
2337 if color then
2338 paintutils.drawPixel(pixel.x, pixel.y, color)
2339 end
2340 return pixel
2341 end
2342 end
2343end
2344
2345
2346function draw_monitor()
2347
2348 term.redirect(monitor)
2349 term.setBackgroundColor(colors.black)
2350 monitor.clear()
2351
2352 zoom_factor = math.pow(2, monitor_zoom_level)
2353 min_location = {
2354 x = monitor_location.x - math.floor(monitor_width * zoom_factor / 2) - 1,
2355 z = monitor_location.z - math.floor(monitor_height * zoom_factor / 2) - 1,
2356 }
2357
2358 local mined = {}
2359 local xz
2360 for x = min_location.x - ((min_location.x - config.locations.mine_enter.x) % config.grid_width), min_location.x + (monitor_width * zoom_factor), config.grid_width do
2361 for z = min_location.z, min_location.z + (monitor_height * zoom_factor), zoom_factor do
2362 xz = x .. ',' .. z
2363 if not mined[xz] then
2364 if z > config.locations.mine_enter.z then
2365 if monitor_level[x] and monitor_level[x].south.z > z then
2366 mined[xz] = true
2367 draw_location({x = x, z = z}, colors.lightGray)
2368 else
2369 draw_location({x = x, z = z}, colors.gray)
2370 end
2371 else
2372 if monitor_level[x] and monitor_level[x].north.z < z then
2373 mined[xz] = true
2374 draw_location({x = x, z = z}, colors.lightGray)
2375 else
2376 draw_location({x = x, z = z}, colors.gray)
2377 end
2378 end
2379 end
2380 end
2381 end
2382
2383 for x = min_location.x, min_location.x + (monitor_width * zoom_factor), zoom_factor do
2384 if x > monitor_level.main_shaft.west.x and x < monitor_level.main_shaft.east.x then
2385 draw_location({x = x, z = config.locations.mine_enter.z}, colors.lightGray)
2386 else
2387 draw_location({x = x, z = config.locations.mine_enter.z}, colors.gray)
2388 end
2389 end
2390
2391 local pixel
2392 local special = {}
2393
2394 pixel = draw_location(config.locations.mine_exit, colors.blue)
2395 if pixel then
2396 special[pixel.x .. ',' .. pixel.y] = colors.blue
2397 end
2398
2399 pixel = draw_location(config.locations.mine_enter, colors.blue)
2400 if pixel then
2401 special[pixel.x .. ',' .. pixel.y] = colors.blue
2402 end
2403
2404 -- DRAW STRIP ENDINGS
2405 for name, strip in pairs(monitor_level) do
2406 if name ~= 'y' then
2407 for _, strip_end in pairs(strip) do
2408 if strip_end.turtles then
2409 pixel = draw_location(strip_end, colors.green)
2410 if pixel then
2411 special[pixel.x .. ',' .. pixel.y] = colors.green
2412 end
2413 end
2414 end
2415 end
2416 end
2417
2418 term.setTextColor(colors.black)
2419 turtles = {}
2420 local str_pixel
2421 for _, turtle in pairs(state.turtles) do
2422 if turtle.data then
2423 local location = turtle.data.location
2424 if location and location.x and location.y then
2425 pixel = draw_location(location)
2426 if pixel then
2427 term.setCursorPos(pixel.x, pixel.y)
2428 str_pixel = pixel.x .. ',' .. pixel.y
2429 if special[str_pixel] then
2430 term.setBackgroundColor(special[str_pixel])
2431 elseif turtle.last_update + config.turtle_timeout < os.clock() then
2432 term.setBackgroundColor(colors.red)
2433 else
2434 term.setBackgroundColor(colors.yellow)
2435 end
2436 if not turtles[str_pixel] then
2437 turtles[str_pixel] = {turtle.id}
2438 term.write('-')
2439 else
2440 table.insert(turtles[str_pixel], turtle.id)
2441 if #turtles[str_pixel] <= 9 then
2442 term.write(#turtles[str_pixel])
2443 else
2444 term.write('+')
2445 end
2446 end
2447 end
2448 end
2449 end
2450 end
2451
2452 for _, pocket in pairs(state.pockets) do
2453 local location = pocket.data.location
2454 if location and location.x and location.y then
2455 pixel = draw_location(location)
2456 if pixel then
2457 term.setCursorPos(pixel.x, pixel.y)
2458 str_pixel = pixel.x .. ',' .. pixel.y
2459 if pocket.last_update + config.pocket_timeout < os.clock() then
2460 term.setBackgroundColor(colors.red)
2461 else
2462 term.setBackgroundColor(colors.green)
2463 end
2464 term.write('M')
2465 end
2466 end
2467 end
2468
2469 term.setTextColor(colors.white)
2470 term.setBackgroundColor(colors.green)
2471 term.setCursorPos(elements.menu.x, elements.menu.y)
2472 term.write('*')
2473 term.setCursorPos(elements.all_turtles.x, elements.all_turtles.y)
2474 term.write('*')
2475 term.setCursorPos(elements.mining_turtles.x, elements.mining_turtles.y)
2476 term.write('*')
2477 term.setCursorPos(elements.center.x, elements.center.y)
2478 term.write('*')
2479 term.setCursorPos(elements.up.x, elements.up.y)
2480 term.write('N')
2481 term.setCursorPos(elements.down.x, elements.down.y)
2482 term.write('S')
2483 term.setCursorPos(elements.left.x, elements.left.y)
2484 term.write('W')
2485 term.setCursorPos(elements.right.x, elements.right.y)
2486 term.write('E')
2487 term.setCursorPos(elements.level_up.x, elements.level_up.y)
2488 term.write('+')
2489 term.setCursorPos(elements.level_down.x, elements.level_down.y)
2490 term.write('-')
2491 term.setCursorPos(elements.zoom_in.x, elements.zoom_in.y)
2492 term.write('+')
2493 term.setCursorPos(elements.zoom_out.x, elements.zoom_out.y)
2494 term.write('-')
2495 term.setBackgroundColor(colors.brown)
2496 term.setCursorPos(elements.level_indicator.x, elements.level_indicator.y)
2497 term.write(string.format('LEVEL: %3d', monitor_level.y))
2498 term.setCursorPos(elements.zoom_indicator.x, elements.zoom_indicator.y)
2499 term.write('ZOOM: ' .. monitor_zoom_level)
2500 term.setCursorPos(elements.x_indicator.x, elements.x_indicator.y)
2501 term.write('X: ' .. monitor_location.x)
2502 term.setCursorPos(elements.z_indicator.x, elements.z_indicator.y)
2503 term.write('Z: ' .. monitor_location.z)
2504 term.setCursorPos(elements.center_indicator.x, elements.center_indicator.y)
2505 term.write('-CENTER')
2506 term.setCursorPos(elements.menu_indicator.x, elements.menu_indicator.y)
2507 term.write('-MENU')
2508 term.setCursorPos(elements.all_indicator.x, elements.all_indicator.y)
2509 term.write('ALL-')
2510 term.setCursorPos(elements.mining_indicator.x, elements.mining_indicator.y)
2511 term.write('MINING-')
2512
2513 term.redirect(monitor.restore_to)
2514end
2515
2516
2517function touch_monitor(monitor_touch)
2518 if monitor_touch.x == elements.up.x and monitor_touch.y == elements.up.y then
2519 monitor_location.z = monitor_location.z - zoom_factor
2520 elseif monitor_touch.x == elements.down.x and monitor_touch.y == elements.down.y then
2521 monitor_location.z = monitor_location.z + zoom_factor
2522 elseif monitor_touch.x == elements.left.x and monitor_touch.y == elements.left.y then
2523 monitor_location.x = monitor_location.x - zoom_factor
2524 elseif monitor_touch.x == elements.right.x and monitor_touch.y == elements.right.y then
2525 monitor_location.x = monitor_location.x + zoom_factor
2526 elseif monitor_touch.x == elements.level_up.x and monitor_touch.y == elements.level_up.y then
2527 monitor_level_index = math.min(monitor_level_index + 1, #config.mine_levels)
2528 select_mine_level()
2529 elseif monitor_touch.x == elements.level_down.x and monitor_touch.y == elements.level_down.y then
2530 monitor_level_index = math.max(monitor_level_index - 1, 1)
2531 select_mine_level()
2532 elseif monitor_touch.x == elements.zoom_in.x and monitor_touch.y == elements.zoom_in.y then
2533 monitor_zoom_level = math.max(monitor_zoom_level - 1, 0)
2534 elseif monitor_touch.x == elements.zoom_out.x and monitor_touch.y == elements.zoom_out.y then
2535 monitor_zoom_level = math.min(monitor_zoom_level + 1, config.monitor_max_zoom_level)
2536 elseif monitor_touch.x == elements.menu.x and monitor_touch.y == elements.menu.y then
2537 menu()
2538 elseif monitor_touch.x == elements.center.x and monitor_touch.y == elements.center.y then
2539 monitor_location = {x = config.default_monitor_location.x, z = config.default_monitor_location.z}
2540 elseif monitor_touch.x == elements.all_turtles.x and monitor_touch.y == elements.all_turtles.y then
2541 local turtle_ids = {}
2542 for _, turtle in pairs(state.turtles) do
2543 if turtle.data then
2544 table.insert(turtle_ids, turtle.id)
2545 end
2546 end
2547 if #turtle_ids then
2548 turtle_viewer(turtle_ids)
2549 end
2550 elseif monitor_touch.x == elements.mining_turtles.x and monitor_touch.y == elements.mining_turtles.y then
2551 local turtle_ids = {}
2552 for _, turtle in pairs(state.turtles) do
2553 if turtle.data and turtle.data.turtle_type == 'mining' then
2554 table.insert(turtle_ids, turtle.id)
2555 end
2556 end
2557 if #turtle_ids then
2558 turtle_viewer(turtle_ids)
2559 end
2560 else
2561 local str_pos = monitor_touch.x .. ',' .. monitor_touch.y
2562 if turtles[str_pos] then
2563 turtle_viewer(turtles[str_pos])
2564 end
2565 end
2566end
2567
2568
2569function init_elements()
2570 elements = {
2571 up = {x = math.ceil(monitor_width / 2), y = 1 },
2572 down = {x = math.ceil(monitor_width / 2), y = monitor_height },
2573 left = {x = 1, y = math.ceil(monitor_height / 2)},
2574 right = {x = monitor_width, y = math.ceil(monitor_height / 2)},
2575 level_up = {x = monitor_width, y = 1},
2576 level_down = {x = monitor_width - 11, y = 1},
2577 level_indicator = {x = monitor_width - 10, y = 1},
2578 zoom_in = {x = monitor_width, y = 2},
2579 zoom_out = {x = monitor_width - 8, y = 2},
2580 zoom_indicator = {x = monitor_width - 7, y = 2},
2581 all_turtles = {x = monitor_width, y = monitor_height-1},
2582 all_indicator = {x = monitor_width - 4, y = monitor_height-1},
2583 mining_turtles = {x = monitor_width, y = monitor_height},
2584 mining_indicator = {x = monitor_width - 7, y = monitor_height},
2585 menu = {x = 1, y = monitor_height},
2586 menu_indicator = {x = 2, y = monitor_height},
2587 center = {x = 1, y = 1},
2588 center_indicator = {x = 2, y = 1},
2589 x_indicator = {x = 1, y = 2},
2590 z_indicator = {x = 1, y = 3},
2591 viewer_exit = {x = 1, y = 1},
2592 turtle_face = {x = 5, y = 2},
2593 turtle_id = {x = 16, y = 2},
2594 turtle_lost = {x = 13, y = 1},
2595 turtle_data = {x = 4, y = 8},
2596 turtle_return = {x = 26, y = 8},
2597 turtle_update = {x = 26, y = 9},
2598 turtle_reboot = {x = 26, y = 10},
2599 turtle_halt = {x = 26, y = 11},
2600 turtle_clear = {x = 26, y = 12},
2601 turtle_reset = {x = 26, y = 13},
2602 turtle_find = {x = 26, y = 14},
2603 turtle_forward = {x = 10, y = 16},
2604 turtle_back = {x = 10, y = 18},
2605 turtle_up = {x = 23, y = 16},
2606 turtle_down = {x = 23, y = 18},
2607 turtle_left = {x = 6, y = 17},
2608 turtle_right = {x = 14, y = 17},
2609 turtle_dig_up = {x = 31, y = 16},
2610 turtle_dig = {x = 31, y = 17},
2611 turtle_dig_down = {x = 31, y = 18},
2612 menu_title = {x = 9, y = 3},
2613 menu_toggle = {x = 10, y = 11},
2614 menu_update = {x = 10, y = 13},
2615 menu_return = {x = 10, y = 14},
2616 menu_reboot = {x = 10, y = 15},
2617 menu_halt = {x = 10, y = 16},
2618 menu_clear = {x = 10, y = 17},
2619 menu_reset = {x = 10, y = 18},
2620 }
2621end
2622
2623
2624function select_mine_level()
2625 monitor_level = state.mine[config.mine_levels[monitor_level_index].level]
2626end
2627
2628
2629function step()
2630 while #state.monitor_touches > 0 do
2631 touch_monitor(table.remove(state.monitor_touches))
2632 end
2633 draw_monitor()
2634 monitor.setVisible(true)
2635 monitor.setVisible(false)
2636 sleep(sleep_len)
2637end
2638
2639
2640function main()
2641 sleep_len = 0.3
2642
2643 local attached = peripheral.find('monitor')
2644
2645 if not attached then
2646 error('No monitor connected.')
2647 end
2648
2649 monitor_size = {attached.getSize()}
2650 monitor_width = monitor_size[1]
2651 monitor_height = monitor_size[2]
2652
2653 if monitor_width < 29 or monitor_height < 12 then -- Must be at least that big
2654 return
2655 end
2656
2657 monitor = window.create(attached, 1, 1, monitor_width, monitor_height)
2658 monitor.restore_to = term.current()
2659 monitor.clear()
2660 monitor.setVisible(false)
2661 monitor.setCursorPos(1, 1)
2662
2663 monitor_location = {x = config.locations.mine_enter.x, z = config.locations.mine_enter.z}
2664 monitor_zoom_level = config.default_monitor_zoom_level
2665
2666 init_elements()
2667
2668 while not state.mine do
2669 sleep(0.5)
2670 end
2671
2672 monitor_level_index = 1
2673 select_mine_level()
2674
2675 state.monitor_touches = {}
2676 while true do
2677 local status, caught_error = pcall(step)
2678 if not status then
2679 term.redirect(monitor.restore_to)
2680 error(caught_error)
2681 end
2682 end
2683end
2684
2685
2686main()]===],
2687 ["hub_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS
2688while true do
2689
2690 turtles_parked = 0
2691 turtle_count = 0
2692 for _, turtle in pairs(state.turtles) do
2693 if turtle.state == 'park' then
2694 turtles_parked = turtles_parked + 1
2695 end
2696 turtle_count = turtle_count + 1
2697 end
2698
2699 rednet.broadcast({
2700 on = state.on,
2701 turtles_parked = turtles_parked,
2702 turtle_count = turtle_count,
2703 }, 'hub_report')
2704
2705 sleep(0.5)
2706
2707end]===],
2708 ["hub_files/session_id"] = [===[29.0]===],
2709 ["hub_files/startup.lua"] = [===[-- SET LABEL
2710os.setComputerLabel('Hub')
2711
2712-- INITIALIZE APIS
2713if fs.exists('/apis') then
2714 fs.delete('/apis')
2715end
2716fs.makeDir('/apis')
2717fs.copy('/config.lua', '/apis/config')
2718fs.copy('/state.lua', '/apis/state')
2719fs.copy('/basics.lua', '/apis/basics')
2720os.loadAPI('/apis/config')
2721os.loadAPI('/apis/state')
2722os.loadAPI('/apis/basics')
2723
2724
2725-- OPEN REDNET
2726for _, side in pairs({'back', 'top', 'left', 'right'}) do
2727 if peripheral.getType(side) == 'modem' then
2728 rednet.open(side)
2729 break
2730 end
2731end
2732
2733
2734-- IF UPDATED PRINT "UPDATED"
2735if fs.exists('/updated') then
2736 fs.delete('/updated')
2737 print('UPDATED')
2738 state.updated = true
2739end
2740
2741
2742-- LAUNCH PROGRAMS AS SEPARATE THREADS
2743multishell.launch({}, '/user.lua')
2744multishell.launch({}, '/report.lua')
2745multishell.launch({}, '/monitor.lua')
2746multishell.launch({}, '/events.lua')
2747multishell.launch({}, '/whosmineisitanyway.lua')
2748multishell.setTitle(2, 'user')
2749multishell.setTitle(3, 'report')
2750multishell.setTitle(4, 'monitor')
2751multishell.setTitle(5, 'events')
2752multishell.setTitle(6, 'whosmine')]===],
2753 ["hub_files/state.lua"] = [===[user_input = {}
2754turtles = {}
2755pockets = {}
2756homes = {}]===],
2757 ["hub_files/update"] = [===[os.run({}, '/disk/hub.lua')]===],
2758 ["hub_files/updated"] = [===[]===],
2759 ["hub_files/user.lua"] = [===[-- CONTINUOUSLY AWAIT USER INPUT AND PLACE IN TABLE
2760while true do
2761 table.insert(state.user_input, read())
2762end]===],
2763 ["hub_files/whosmineisitanyway.lua"] = [===[inf = basics.inf
2764str_xyz = basics.str_xyz
2765
2766
2767reverse_shift = {
2768 north = 'south',
2769 south = 'north',
2770 east = 'west',
2771 west = 'east',
2772}
2773
2774
2775function load_mine()
2776 -- LOAD MINE INTO state.mine FROM /mine/<x,z>/ DIRECTORY
2777 print("DEBUG: Loading mine from directory...")
2778 state.mine_dir_path = '/mine/' .. config.locations.mine_enter.x .. ',' .. config.locations.mine_enter.z .. '/'
2779 print("DEBUG: Mine directory path: " .. state.mine_dir_path)
2780 state.mine = {}
2781
2782 if not fs.exists(state.mine_dir_path) then
2783 print("DEBUG: Creating new mine directory")
2784 fs.makeDir(state.mine_dir_path)
2785 end
2786
2787 if fs.exists(state.mine_dir_path .. 'on') then
2788 print("DEBUG: Mine is set to ON")
2789 state.on = true
2790 else
2791 print("DEBUG: Mine is set to OFF")
2792 end
2793
2794 for _, level_and_chance in pairs(config.mine_levels) do
2795 local level = level_and_chance.level
2796 state.mine[level] = {y = level}
2797
2798 -- START WITH AT LEAST A MAIN SHAFT
2799 state.mine[level].main_shaft = {}
2800 state.mine[level].main_shaft.west = {name = 'main_shaft', x = config.locations.mine_enter.x, y = level, z = config.locations.mine_enter.z, orientation = 'west'}
2801 state.mine[level].main_shaft.east = {name = 'main_shaft', x = config.locations.mine_enter.x, y = level, z = config.locations.mine_enter.z, orientation = 'east'}
2802
2803 -- FOR EACH STRIP IN /mine/<x,z>/<level>/ PUT INTO MEMORY
2804 local level_dir_path = state.mine_dir_path .. level .. '/'
2805 if not fs.exists(level_dir_path) then
2806 fs.makeDir(level_dir_path)
2807 else
2808 for _, file_name in pairs(fs.list(level_dir_path)) do
2809 if file_name:sub(1, 1) ~= '.' then
2810 local file = fs.open(level_dir_path .. file_name, 'r')
2811 if file == nil then
2812 error('Failed to open file ' .. level_dir_path .. file_name)
2813 else
2814 if file_name == 'main_shaft' then
2815 local xs = string.gmatch(file.readAll(), '[^,]+')
2816 state.mine[level].main_shaft = {}
2817 state.mine[level].main_shaft.west = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'west'}
2818 state.mine[level].main_shaft.east = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'east'}
2819 else
2820 local zs = string.gmatch(file.readAll(), '[^,]+')
2821 local x = tonumber(file_name)
2822 state.mine[level][x] = {}
2823 state.mine[level][x].north = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'north'}
2824 state.mine[level][x].south = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'south'}
2825 end
2826 file.close()
2827 end
2828 end
2829 end
2830 end
2831 end
2832
2833 state.turtles_dir_path = state.mine_dir_path .. 'turtles/'
2834
2835 if not fs.exists(state.turtles_dir_path) then
2836 fs.makeDir(state.turtles_dir_path)
2837 end
2838
2839 local turtle_pairs = {}
2840
2841 for _, turtle_id in pairs(fs.list(state.turtles_dir_path)) do
2842 if turtle_id:sub(1, 1) ~= '.' then
2843 turtle_id = tonumber(turtle_id)
2844 local turtle = {id = turtle_id}
2845 state.turtles[turtle_id] = turtle
2846 local turtle_dir_path = state.turtles_dir_path .. turtle_id .. '/'
2847 if fs.exists(turtle_dir_path .. 'strip') then
2848 local file = fs.open(turtle_dir_path .. 'strip', 'r')
2849 if file == nil then
2850 error('Failed to open file ' .. turtle_dir_path .. 'strip')
2851 end
2852 local strip_args = string.gmatch(file.readAll(), '[^,]+')
2853
2854 local level = tonumber(strip_args())
2855 local name = strip_args()
2856 if name ~= 'main_shaft' then
2857 name = tonumber(name)
2858 end
2859 local orientation = strip_args()
2860
2861 if state.mine[level] and state.mine[level][name] and state.mine[level][name][orientation] then
2862 turtle.strip = state.mine[level][name][orientation]
2863 if fs.exists(turtle_dir_path .. 'deployed') then
2864 file = fs.open(turtle_dir_path .. 'deployed', 'r')
2865 if file == nil then
2866 error('Failed to open file ' .. turtle_dir_path .. 'deployed')
2867 end
2868 turtle.steps_left = tonumber(file.readAll())
2869 if not turtle_pairs[turtle.strip] then
2870 turtle_pairs[turtle.strip] = {}
2871 end
2872 table.insert(turtle_pairs[turtle.strip], turtle)
2873 end
2874 end
2875
2876 end
2877 if fs.exists(turtle_dir_path .. 'halt') then
2878 turtle.state = 'halt'
2879 end
2880 end
2881 end
2882
2883 for strip, turtles in pairs(turtle_pairs) do
2884 if #turtles == 2 then
2885 strip.turtles = turtles
2886 turtles[1].pair = turtles[2]
2887 turtles[2].pair = turtles[1]
2888 elseif #turtles == 1 and not config.use_chunky_turtles then
2889 strip.turtles = turtles
2890 end
2891 end
2892end
2893
2894
2895function write_strip(level, name)
2896 -- RECORD THE STATE OF A STRIP AT A GIVEN level AND name TO /mine/<center>/<level>/<name>
2897 local file = fs.open(state.mine_dir_path .. level .. '/' .. name, 'w')
2898 if name == 'main_shaft' then
2899 file.write(state.mine[level][name].west.x .. ',' .. state.mine[level][name].east.x)
2900 else
2901 file.write(state.mine[level][name].north.z .. ',' .. state.mine[level][name].south.z)
2902 end
2903 file.close()
2904end
2905
2906
2907function write_turtle_strip(turtle, strip)
2908 local file = fs.open(state.turtles_dir_path .. turtle.id .. '/strip', 'w')
2909 file.write(strip.y .. ',' .. strip.name .. ',' .. strip.orientation)
2910 file.close()
2911end
2912
2913
2914function halt(turtle)
2915 add_task(turtle, {action = 'pass', end_state = 'halt'})
2916 fs.open(state.turtles_dir_path .. turtle.id .. '/halt', 'w').close()
2917end
2918
2919
2920function unhalt(turtle)
2921 fs.delete(state.turtles_dir_path .. turtle.id .. '/halt', 'w')
2922end
2923
2924
2925function update_strip(turtle)
2926 -- RECORD THAT A STRIP HAS BEEN EXPLORED TO TURTLE'S POSITION
2927 local strip = turtle.strip
2928 if strip then
2929 if strip.orientation == 'north' then
2930 strip.z = math.min(strip.z, turtle.data.location.z)
2931 elseif strip.orientation == 'south' then
2932 strip.z = math.max(strip.z, turtle.data.location.z)
2933 elseif strip.orientation == 'east' then
2934 strip.x = math.max(strip.x, turtle.data.location.x)
2935 elseif strip.orientation == 'west' then
2936 strip.x = math.min(strip.x, turtle.data.location.x)
2937 end
2938 write_strip(strip.y, strip.name)
2939 end
2940end
2941
2942
2943function expand_mine(level, x)
2944 if not state.mine[level][x] then
2945 state.mine[level][x] = {}
2946 state.mine[level][x].north = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'north'}
2947 state.mine[level][x].south = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'south'}
2948 write_strip(level, x)
2949 end
2950end
2951
2952
2953function gen_next_strip()
2954 local level = get_mining_level()
2955 print("DEBUG: Generating next strip for level: " .. tostring(level))
2956 state.next_strip = get_closest_free_strip(level)
2957 if state.next_strip then
2958 print("DEBUG: Next strip found at " .. state.next_strip.x .. "," .. state.next_strip.y .. "," .. state.next_strip.z .. " facing " .. state.next_strip.orientation)
2959 state.min_fuel = (basics.distance(state.next_strip, config.locations.mine_enter) + config.mission_length) * 3
2960 print("DEBUG: Minimum fuel required: " .. state.min_fuel)
2961 else
2962 print("DEBUG: No available strips found")
2963 state.min_fuel = nil
2964 end
2965end
2966
2967
2968function get_closest_free_strip(level)
2969 local west_x = config.locations.mine_enter.x
2970 local east_x = config.locations.mine_enter.x
2971 local offset_x = 0
2972 local min_x = state.mine[level].main_shaft.west.x
2973 local max_x = state.mine[level].main_shaft.east.x
2974
2975 local closest_strip
2976 local distance
2977 local z_distance
2978 local min_dist = inf
2979
2980 while west_x >= min_x and east_x <= max_x and offset_x < min_dist do
2981 for _, x in pairs({west_x, east_x}) do
2982 for _, z_side in pairs({'north', 'south'}) do
2983 expand_mine(level, x)
2984 strip = state.mine[level][x][z_side]
2985 if not strip.turtles then
2986 z_distance = math.abs(strip.z - config.locations.mine_enter.z)
2987 distance = z_distance + math.abs(strip.x - config.locations.mine_enter.x)
2988 if distance < min_dist then
2989 min_dist = distance
2990 closest_strip = strip
2991 end
2992 end
2993 end
2994 end
2995 offset_x = offset_x + config.grid_width
2996 west_x = config.locations.mine_enter.x - offset_x
2997 east_x = config.locations.mine_enter.x + offset_x
2998 end
2999
3000 for _, strip in pairs({state.mine[level].main_shaft.west, state.mine[level].main_shaft.east}) do
3001 if not strip.turtles then
3002 distance = math.abs(strip.x - config.locations.mine_enter.x)
3003 if distance <= min_dist then
3004 min_dist = distance
3005 closest_strip = strip
3006 end
3007 end
3008 end
3009
3010 return closest_strip
3011end
3012
3013
3014function get_mining_level()
3015 local n = 0
3016 local r = math.random()
3017 for _, level_and_chance in pairs(config.mine_levels) do
3018 n = n + level_and_chance.chance
3019 if n > r then
3020 return level_and_chance.level
3021 end
3022 end
3023end
3024
3025
3026function good_on_fuel(mining_turtle, chunky_turtle)
3027 local fuel_needed = math.ceil(basics.distance(mining_turtle.data.location, config.locations.mine_exit) * 1.5)
3028 return (mining_turtle.data.fuel_level == "unlimited" or mining_turtle.data.fuel_level > fuel_needed) and ((not config.use_chunky_turtles) or (chunky_turtle.data.fuel_level == "unlimited" or chunky_turtle.data.fuel_level > fuel_needed))
3029end
3030
3031
3032function follow(chunky_turtle)
3033 add_task(chunky_turtle, {
3034 action = 'go_to_strip',
3035 data = {chunky_turtle.strip},
3036 end_state = 'wait',
3037 })
3038end
3039
3040
3041function go_mine(mining_turtle)
3042 print("DEBUG: Sending turtle " .. mining_turtle.id .. " to mine at strip " .. mining_turtle.strip.x .. "," .. mining_turtle.strip.y .. "," .. mining_turtle.strip.z)
3043 print("DEBUG: Mining direction: " .. mining_turtle.strip.orientation)
3044 print("DEBUG: Steps left before mining: " .. mining_turtle.steps_left)
3045
3046 update_strip(mining_turtle)
3047 add_task(mining_turtle, {
3048 action = 'mine_vein',
3049 data = {mining_turtle.strip.orientation},
3050 })
3051 add_task(mining_turtle, {
3052 action = 'clear_gravity_blocks',
3053 })
3054 if config.use_chunky_turtles then
3055 print("DEBUG: Using chunky turtles - setting up follow pattern")
3056 add_task(mining_turtle, {
3057 action = 'go_to_strip',
3058 data = {mining_turtle.strip},
3059 end_state = 'wait',
3060 end_function = follow,
3061 end_function_args = {mining_turtle.pair},
3062 })
3063 else
3064 print("DEBUG: Solo mining mode - no chunky turtle")
3065 add_task(mining_turtle, {
3066 action = 'go_to_strip',
3067 data = {mining_turtle.strip},
3068 end_state = 'wait',
3069 })
3070 end
3071 mining_turtle.steps_left = mining_turtle.steps_left - 1
3072 print("DEBUG: Steps remaining after mining: " .. mining_turtle.steps_left)
3073 local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w')
3074 file.write(mining_turtle.steps_left)
3075 file.close()
3076end
3077
3078
3079function free_turtle(turtle)
3080 if turtle.pair then
3081 fs.delete(state.turtles_dir_path .. turtle.id .. '/deployed')
3082 fs.delete(state.turtles_dir_path .. turtle.pair.id .. '/deployed')
3083 turtle.pair.pair = nil
3084 turtle.pair = nil
3085 turtle.strip.turtles = nil
3086 end
3087end
3088
3089
3090function pair_turtles_finish()
3091 state.pair_hold = nil
3092end
3093
3094
3095function pair_turtles_send(chunky_turtle)
3096 add_task(chunky_turtle, {
3097 action = 'go_to_mine_enter',
3098 end_function = pair_turtles_finish
3099 })
3100
3101 add_task(chunky_turtle, {
3102 action = 'go_to_strip',
3103 data = {chunky_turtle.strip},
3104 end_state = 'wait',
3105 })
3106end
3107
3108
3109function pair_turtles_begin(turtle1, turtle2)
3110 print("DEBUG: Beginning turtle pairing process")
3111 print("DEBUG: Turtle 1 - ID: " .. turtle1.id .. " Type: " .. tostring(turtle1.data.turtle_type))
3112 print("DEBUG: Turtle 2 - ID: " .. turtle2.id .. " Type: " .. tostring(turtle2.data.turtle_type))
3113
3114 local mining_turtle
3115 local chunky_turtle
3116 if turtle1.data.turtle_type == 'mining' then
3117 if turtle2.data.turtle_type ~= 'chunky' then
3118 error('Incompatable turtles')
3119 end
3120 mining_turtle = turtle1
3121 chunky_turtle = turtle2
3122 elseif turtle1.data.turtle_type == 'chunky' then
3123 if turtle2.data.turtle_type ~= 'mining' then
3124 error('Incompatable turtles')
3125 end
3126 chunky_turtle = turtle1
3127 mining_turtle = turtle2
3128 end
3129
3130 print("DEBUG: Mining turtle: " .. mining_turtle.id)
3131 print("DEBUG: Chunky turtle: " .. chunky_turtle.id)
3132
3133 local strip = state.next_strip
3134 local level = strip and strip.level or nil
3135
3136 if not strip then
3137 print("DEBUG: No strip available, regenerating and setting turtles to idle")
3138 gen_next_strip()
3139 add_task(mining_turtle, {action = 'pass', end_state = 'idle'})
3140 add_task(chunky_turtle, {action = 'pass', end_state = 'idle'})
3141 return
3142 end
3143
3144 print('DEBUG: Pairing ' .. mining_turtle.id .. ' and ' .. chunky_turtle.id .. ' for strip at ' .. strip.x .. ',' .. strip.y .. ',' .. strip.z)
3145
3146 mining_turtle.pair = chunky_turtle
3147 chunky_turtle.pair = mining_turtle
3148
3149 state.pair_hold = {mining_turtle, chunky_turtle}
3150
3151 mining_turtle.steps_left = config.mission_length
3152
3153 strip.turtles = {mining_turtle, chunky_turtle}
3154
3155 for _, turtle in pairs(strip.turtles) do
3156 turtle.strip = strip
3157 write_turtle_strip(turtle, strip)
3158 add_task(turtle, {action = 'pass', end_state = 'trip'})
3159 end
3160
3161 fs.open(state.turtles_dir_path .. chunky_turtle.id .. '/deployed', 'w').close()
3162 local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w')
3163 file.write(mining_turtle.steps_left)
3164 file.close()
3165
3166 add_task(mining_turtle, {
3167 action = 'go_to_mine_enter',
3168 end_function = pair_turtles_send,
3169 end_function_args = {chunky_turtle}
3170 })
3171
3172 add_task(mining_turtle, {
3173 action = 'go_to_strip',
3174 data = {mining_turtle.strip},
3175 end_state = 'wait',
3176 })
3177
3178 gen_next_strip()
3179end
3180
3181
3182function solo_turtle_begin(turtle)
3183 print("DEBUG: Beginning solo turtle assignment")
3184 print("DEBUG: Turtle ID: " .. turtle.id .. " Type: " .. tostring(turtle.data.turtle_type))
3185
3186 local strip = state.next_strip
3187 local level = strip and strip.level or nil
3188
3189 if not strip then
3190 print("DEBUG: No strip available for solo turtle, regenerating and setting to idle")
3191 gen_next_strip()
3192 add_task(turtle, {action = 'pass', end_state = 'idle'})
3193 return
3194 end
3195
3196 print('DEBUG: Assigning solo turtle ' .. turtle.id .. ' to strip at ' .. strip.x .. ',' .. strip.y .. ',' .. strip.z)
3197
3198 turtle.steps_left = config.mission_length
3199
3200 strip.turtles = {turtle}
3201
3202 for _, turtle in pairs(strip.turtles) do
3203 turtle.strip = strip
3204 write_turtle_strip(turtle, strip)
3205 add_task(turtle, {action = 'pass', end_state = 'trip'})
3206 end
3207
3208 local file = fs.open(state.turtles_dir_path .. turtle.id .. '/deployed', 'w')
3209 file.write(turtle.steps_left)
3210 file.close()
3211
3212 add_task(turtle, {
3213 action = 'go_to_mine_enter',
3214 })
3215
3216 add_task(turtle, {
3217 action = 'go_to_strip',
3218 data = {turtle.strip},
3219 end_state = 'wait',
3220 })
3221
3222 gen_next_strip()
3223end
3224
3225
3226function check_pair_fuel(turtle)
3227 print("DEBUG: Checking fuel for turtle " .. turtle.id)
3228 if state.min_fuel then
3229 print("DEBUG: Turtle " .. turtle.id .. " fuel level: " .. tostring(turtle.data.fuel_level) .. ", required: " .. state.min_fuel)
3230 if (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level <= state.min_fuel) then
3231 print("DEBUG: Turtle " .. turtle.id .. " needs fuel, sending to prepare")
3232 add_task(turtle, {action = 'prepare', data = {state.min_fuel}})
3233 else
3234 print("DEBUG: Turtle " .. turtle.id .. " has sufficient fuel, ready to pair")
3235 add_task(turtle, {action = 'pass', end_state = 'pair'})
3236 end
3237 else
3238 print("DEBUG: No minimum fuel set, generating next strip")
3239 gen_next_strip()
3240 end
3241end
3242
3243
3244function send_turtle_up(turtle)
3245 if turtle.data.location.y < config.locations.mine_enter.y then
3246 if turtle.strip then
3247
3248 if turtle.data.turtle_type == 'chunky' and turtle.data.location.y == turtle.strip.y then
3249 add_task(turtle, {action = 'delay', data={3}})
3250 end
3251
3252 add_task(turtle, {action = 'go_to_mine_exit', data = {turtle.strip}})
3253 end
3254 end
3255end
3256
3257
3258function initialize_turtle(turtle)
3259 print("DEBUG: Initializing turtle " .. turtle.id)
3260 local data = {session_id, config}
3261
3262 if turtle.state ~= 'halt' then
3263 print("DEBUG: Setting turtle " .. turtle.id .. " state to 'lost'")
3264 turtle.state = 'lost'
3265 else
3266 print("DEBUG: Turtle " .. turtle.id .. " remains in 'halt' state")
3267 end
3268 turtle.task_id = 2
3269 turtle.tasks = {}
3270 print("DEBUG: Sending initialize task to turtle " .. turtle.id)
3271 add_task(turtle, {action = 'initialize', data = data})
3272end
3273
3274
3275function add_task(turtle, task)
3276 if not task.data then
3277 task.data = {}
3278 end
3279 print("DEBUG: Adding task '" .. task.action .. "' to turtle " .. turtle.id .. " (tasks in queue: " .. (#turtle.tasks + 1) .. ")")
3280 table.insert(turtle.tasks, task)
3281end
3282
3283
3284function send_tasks(turtle)
3285 local task = turtle.tasks[1]
3286 if task then
3287 local turtle_data = turtle.data
3288 if turtle_data.request_id == turtle.task_id and turtle.data.session_id == session_id then
3289 if turtle_data.success then
3290 print("DEBUG: Task '" .. task.action .. "' completed successfully for turtle " .. turtle.id)
3291 if task.end_state then
3292 if turtle.state == 'halt' and task.end_state ~= 'halt' then
3293 print("DEBUG: Unhalting turtle " .. turtle.id)
3294 unhalt(turtle)
3295 end
3296 print("DEBUG: Changing turtle " .. turtle.id .. " state from '" .. turtle.state .. "' to '" .. task.end_state .. "'")
3297 turtle.state = task.end_state
3298 end
3299 if task.end_function then
3300 print("DEBUG: Executing end function for turtle " .. turtle.id)
3301 if task.end_function_args then
3302 task.end_function(unpack(task.end_function_args))
3303 else
3304 task.end_function()
3305 end
3306 end
3307 table.remove(turtle.tasks, 1)
3308 print("DEBUG: Task removed, turtle " .. turtle.id .. " has " .. #turtle.tasks .. " tasks remaining")
3309 else
3310 print("DEBUG: Task '" .. task.action .. "' failed for turtle " .. turtle.id)
3311 end
3312 turtle.task_id = turtle.task_id + 1
3313 elseif (not turtle_data.busy) and ((not task.epoch) or (task.epoch > os.clock()) or (task.epoch + config.task_timeout < os.clock())) then
3314 -- ONLY SEND INSTRUCTION AFTER <config.task_timeout> SECONDS HAVE PASSED
3315 task.epoch = os.clock()
3316 print(string.format('DEBUG: Sending %s directive to %d', task.action, turtle.id))
3317 rednet.send(turtle.id, {
3318 action = task.action,
3319 data = task.data,
3320 request_id = turtle_data.request_id
3321 }, 'mastermine')
3322 end
3323 end
3324end
3325
3326
3327function user_input(input)
3328 -- PROCESS USER INPUT FROM USER_INPUT TABLE
3329 while #state.user_input > 0 do
3330 local input = table.remove(state.user_input, 1)
3331 local next_word = string.gmatch(input, '%S+')
3332 local command = next_word()
3333 local turtle_id_string = next_word()
3334 local turtle_id
3335 local turtles = {}
3336 if turtle_id_string and turtle_id_string ~= '*' then
3337 turtle_id = tonumber(turtle_id_string)
3338 if state.turtles[turtle_id] then
3339 turtles = {state.turtles[turtle_id]}
3340 end
3341 else
3342 turtles = state.turtles
3343 end
3344 if command == 'turtle' then
3345 -- SEND COMMAND DIRECTLY TO TURTLE
3346 local action = next_word()
3347 local data = {}
3348 for user_arg in next_word do
3349 table.insert(data, user_arg)
3350 end
3351 for _, turtle in pairs(turtles) do
3352 halt(turtle)
3353 add_task(turtle, {
3354 action = action,
3355 data = data,
3356 })
3357 end
3358 elseif command == 'clear' then
3359 for _, turtle in pairs(turtles) do
3360 turtle.tasks = {}
3361 add_task(turtle, {action = 'pass'})
3362 end
3363 elseif command == 'shutdown' then
3364 -- REBOOT TURTLE
3365 for _, turtle in pairs(turtles) do
3366 turtle.tasks = {}
3367 add_task(turtle, {action = 'pass'})
3368 rednet.send(turtle.id, {
3369 action = 'shutdown',
3370 }, 'mastermine')
3371 end
3372 elseif command == 'reboot' then
3373 -- REBOOT TURTLE
3374 for _, turtle in pairs(turtles) do
3375 turtle.tasks = {}
3376 add_task(turtle, {action = 'pass'})
3377 rednet.send(turtle.id, {
3378 action = 'reboot',
3379 }, 'mastermine')
3380 end
3381 elseif command == 'update' then
3382 -- FEED TURTLE DINNER
3383 for _, turtle in pairs(turtles) do
3384 turtle.tasks = {}
3385 add_task(turtle, {action = 'pass'})
3386 rednet.send(turtle.id, {
3387 action = 'update',
3388 }, 'mastermine')
3389 end
3390 elseif command == 'return' then
3391 -- BRING TURTLE HOME
3392 for _, turtle in pairs(turtles) do
3393 turtle.tasks = {}
3394 add_task(turtle, {action = 'pass'})
3395 halt(turtle)
3396 send_turtle_up(turtle)
3397 add_task(turtle, {action = 'go_to_home'})
3398 end
3399 elseif command == 'halt' then
3400 -- HALT TURTLE(S)
3401 for _, turtle in pairs(turtles) do
3402 turtle.tasks = {}
3403 add_task(turtle, {action = 'pass'})
3404 halt(turtle)
3405 end
3406 elseif command == 'reset' then
3407 -- HALT TURTLE(S)
3408 for _, turtle in pairs(turtles) do
3409 turtle.tasks = {}
3410 add_task(turtle, {action = 'pass'})
3411 add_task(turtle, {action = 'pass', end_state = 'lost'})
3412 end
3413 elseif command == 'on' or command == 'go' then
3414 -- ACTIVATE MINING NETWORK
3415 if not turtle_id_string then
3416 for _, turtle in pairs(state.turtles) do
3417 turtle.tasks = {}
3418 add_task(turtle, {action = 'pass'})
3419 end
3420 state.on = true
3421 fs.open(state.mine_dir_path .. 'on', 'w').close()
3422 end
3423 elseif command == 'off' or command == 'stop' then
3424 -- STANDBY MINING NETWORK
3425 if not turtle_id_string then
3426 for _, turtle in pairs(state.turtles) do
3427 turtle.tasks = {}
3428 add_task(turtle, {action = 'pass'})
3429 free_turtle(turtle)
3430 end
3431 state.on = nil
3432 fs.delete(state.mine_dir_path .. 'on')
3433 end
3434 elseif command == 'hubshutdown' then
3435 -- STANDBY MINING NETWORK
3436 if not turtle_id_string then
3437 os.shutdown()
3438 end
3439 elseif command == 'hubreboot' then
3440 -- STANDBY MINING NETWORK
3441 if not turtle_id_string then
3442 os.reboot()
3443 end
3444 elseif command == 'hubupdate' then
3445 -- STANDBY MINING NETWORK
3446 if not turtle_id_string then
3447 os.run({}, '/update')
3448 end
3449 elseif command == 'debug' then
3450 -- DEBUG
3451 end
3452 end
3453end
3454
3455
3456function command_turtles()
3457 local turtles_for_pair = {}
3458
3459 for _, turtle in pairs(state.turtles) do
3460
3461 if turtle.data then
3462 print("DEBUG: Processing turtle " .. turtle.id .. " - State: " .. tostring(turtle.state) .. ", Type: " .. tostring(turtle.data.turtle_type))
3463
3464 if turtle.data.session_id ~= session_id then
3465 -- BABY TURTLE NEEDS TO LEARN
3466 print("DEBUG: Turtle " .. turtle.id .. " has wrong session ID, initializing")
3467 if (not turtle.tasks) or (not turtle.tasks[1]) or (not (turtle.tasks[1].action == 'initialize')) then
3468 initialize_turtle(turtle)
3469 end
3470 end
3471
3472 if #turtle.tasks > 0 then
3473 -- TURTLE IS BUSY
3474 print("DEBUG: Turtle " .. turtle.id .. " is busy with " .. #turtle.tasks .. " tasks")
3475 send_tasks(turtle)
3476
3477 elseif not turtle.data.location then
3478 -- TURTLE NEEDS A MAP
3479 print("DEBUG: Turtle " .. turtle.id .. " needs location calibration")
3480 add_task(turtle, {action = 'calibrate'})
3481
3482 elseif turtle.state ~= 'halt' then
3483
3484 if turtle.state == 'park' then
3485 -- TURTLE FOUND PARKING
3486 print("DEBUG: Turtle " .. turtle.id .. " is parked")
3487 if state.on and (config.use_chunky_turtles or turtle.data.turtle_type == 'mining') then
3488 print("DEBUG: Mine is on, moving turtle " .. turtle.id .. " from park to idle")
3489 add_task(turtle, {action = 'pass', end_state = 'idle'})
3490 end
3491
3492 elseif not state.on and turtle.state ~= 'idle' then
3493 -- TURTLE HAS TO STOP
3494 print("DEBUG: Mine is off, moving turtle " .. turtle.id .. " to idle")
3495 add_task(turtle, {action = 'pass', end_state = 'idle'})
3496
3497 elseif turtle.state == 'lost' then
3498 -- TURTLE IS CONFUSED
3499 print("DEBUG: Turtle " .. turtle.id .. " is lost")
3500 if turtle.data.location.y < config.locations.mine_enter.y and (turtle.pair or not config.use_chunky_turtles) then
3501 print("DEBUG: Lost turtle " .. turtle.id .. " is underground with pair/solo mode, resuming mining")
3502 add_task(turtle, {action = 'pass', end_state = 'trip'})
3503 add_task(turtle, {
3504 action = 'go_to_strip',
3505 data = {turtle.strip},
3506 end_state = 'wait'
3507 })
3508 else
3509 print("DEBUG: Lost turtle " .. turtle.id .. " going to idle")
3510 add_task(turtle, {action = 'pass', end_state = 'idle'})
3511 end
3512
3513 elseif turtle.state == 'idle' then
3514 -- TURTLE IS BORED
3515 print("DEBUG: Turtle " .. turtle.id .. " is idle")
3516 free_turtle(turtle)
3517 if turtle.data.location.y < config.locations.mine_enter.y then
3518 print("DEBUG: Idle turtle " .. turtle.id .. " is underground, sending up")
3519 send_turtle_up(turtle)
3520 elseif not basics.in_area(turtle.data.location, config.locations.control_room_area) then
3521 print("DEBUG: Idle turtle " .. turtle.id .. " is outside control room, halting")
3522 halt(turtle)
3523 elseif turtle.data.item_count > 0 or (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level < config.fuel_per_unit) then
3524 print("DEBUG: Idle turtle " .. turtle.id .. " needs supplies, preparing")
3525 add_task(turtle, {action = 'prepare', data = {config.fuel_per_unit}})
3526 elseif state.on then
3527 print("DEBUG: Idle turtle " .. turtle.id .. " ready for deployment, going to waiting room")
3528 add_task(turtle, {
3529 action = 'go_to_waiting_room',
3530 end_function = check_pair_fuel,
3531 end_function_args = {turtle},
3532 })
3533 else
3534 print("DEBUG: Idle turtle " .. turtle.id .. " going home to park (mine off)")
3535 add_task(turtle, {action = 'go_to_home', end_state = 'park'})
3536 end
3537
3538 elseif turtle.state == 'pair' then
3539 -- TURTLE NEEDS A FRIEND
3540 print("DEBUG: Turtle " .. turtle.id .. " is ready to pair")
3541 if config.use_chunky_turtles then
3542 if not state.pair_hold then
3543 if not turtle.pair then
3544 print("DEBUG: Adding turtle " .. turtle.id .. " to pairing queue")
3545 table.insert(turtles_for_pair, turtle)
3546 end
3547 else
3548 if not (state.pair_hold[1].pair and state.pair_hold[2].pair) then
3549 print("DEBUG: Clearing invalid pair hold")
3550 state.pair_hold = nil
3551 end
3552 end
3553 else
3554 print("DEBUG: Solo turtle mode, assigning turtle " .. turtle.id .. " individually")
3555 solo_turtle_begin(turtle)
3556 end
3557
3558 elseif turtle.state == 'wait' then
3559 -- TURTLE GO DO SOME WORK
3560 print("DEBUG: Turtle " .. turtle.id .. " is waiting for work")
3561 if turtle.pair then
3562 if turtle.data.turtle_type == 'mining' and turtle.pair.state == 'wait' then
3563 print("DEBUG: Mining turtle " .. turtle.id .. " and chunky turtle " .. turtle.pair.id .. " both waiting")
3564 if turtle.steps_left <= 0 or (turtle.data.empty_slot_count == 0 and turtle.pair.data.empty_slot_count == 0) or not good_on_fuel(turtle, turtle.pair) then
3565 print("DEBUG: Paired turtles " .. turtle.id .. "/" .. turtle.pair.id .. " finished mission (steps: " .. turtle.steps_left .. ", fuel good: " .. tostring(good_on_fuel(turtle, turtle.pair)) .. ")")
3566 add_task(turtle, {action = 'pass', end_state = 'idle'})
3567 add_task(turtle.pair, {action = 'pass', end_state = 'idle'})
3568 elseif turtle.data.empty_slot_count == 0 then
3569 print("DEBUG: Mining turtle " .. turtle.id .. " inventory full, dumping")
3570 add_task(turtle, {
3571 action = 'dump',
3572 data = {reverse_shift[turtle.strip.orientation]}
3573 })
3574 else
3575 print("DEBUG: Sending paired turtles " .. turtle.id .. "/" .. turtle.pair.id .. " to mine")
3576 add_task(turtle, {action = 'pass', end_state = 'mine'})
3577 add_task(turtle.pair, {action = 'pass', end_state = 'mine'})
3578 go_mine(turtle)
3579 end
3580 end
3581 elseif not config.use_chunky_turtles then
3582 if turtle.steps_left <= 0 or turtle.data.empty_slot_count == 0 or not good_on_fuel(turtle) then
3583 print("DEBUG: Solo turtle " .. turtle.id .. " finished mission (steps: " .. turtle.steps_left .. ", fuel good: " .. tostring(good_on_fuel(turtle)) .. ")")
3584 add_task(turtle, {action = 'pass', end_state = 'idle'})
3585 else
3586 print("DEBUG: Sending solo turtle " .. turtle.id .. " to mine")
3587 add_task(turtle, {action = 'pass', end_state = 'mine'})
3588 go_mine(turtle)
3589 end
3590 else
3591 print("DEBUG: Turtle " .. turtle.id .. " waiting but no valid pairing, going idle")
3592 add_task(turtle, {action = 'pass', end_state = 'idle'})
3593 end
3594 elseif turtle.state == 'mine' then
3595 print("DEBUG: Turtle " .. turtle.id .. " is in mine state")
3596 if config.use_chunky_turtles and not turtle.pair then
3597 print("DEBUG: Mining turtle " .. turtle.id .. " lost its pair, going idle")
3598 add_task(turtle, {action = 'pass', end_state = 'idle'})
3599 end
3600 end
3601 end
3602 end
3603 end
3604 if #turtles_for_pair == 2 then
3605 print("DEBUG: Found 2 turtles ready for pairing: " .. turtles_for_pair[1].id .. " and " .. turtles_for_pair[2].id)
3606 pair_turtles_begin(turtles_for_pair[1], turtles_for_pair[2])
3607 elseif #turtles_for_pair == 1 then
3608 print("DEBUG: Only 1 turtle waiting for pairing: " .. turtles_for_pair[1].id)
3609 elseif #turtles_for_pair > 2 then
3610 print("DEBUG: " .. #turtles_for_pair .. " turtles waiting for pairing")
3611 end
3612end
3613
3614
3615function main()
3616 -- INCREASE SESSION ID BY ONE
3617 if fs.exists('/session_id') then
3618 session_id = tonumber(fs.open('/session_id', 'r').readAll()) + 1
3619 else
3620 session_id = 1
3621 end
3622 print("DEBUG: Starting mastermine with session ID: " .. session_id)
3623 local file = fs.open('/session_id', 'w')
3624 file.write(session_id)
3625 file.close()
3626
3627 -- LOAD MINE INTO MEMORY
3628 load_mine()
3629
3630 -- FIND THE CLOSEST STRIP
3631 gen_next_strip()
3632
3633 print("DEBUG: Mastermine initialization complete")
3634 print("DEBUG: Mine status: " .. (state.on and "ON" or "OFF"))
3635 print("DEBUG: Use chunky turtles: " .. tostring(config.use_chunky_turtles))
3636
3637 local cycle = 0
3638 while true do
3639 print('DEBUG: Cycle: ' .. cycle .. ' (Active turtles: ' .. (function() local count = 0; for _ in pairs(state.turtles) do count = count + 1 end; return count end)() .. ')')
3640 user_input() -- PROCESS USER INPUT
3641 command_turtles() -- COMMAND TURTLES
3642 sleep(0.1) -- DELAY 0.1 SECONDS
3643 cycle = cycle + 1
3644 end
3645end
3646
3647
3648main()]===],
3649}
3650
3651for k, v in pairs(files) do
3652 local file = fs.open(fs.combine(path, k), 'w')
3653 file.write(v)
3654 file.close()
3655end
3656