mastermine.lua

3570 lines · 116.5 KB

Open raw

Copy & run

wget https://perlytiara.github.io/turtles.tips/raw/programs/MerlinLikeTheWizard/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 state.mine_dir_path = '/mine/' .. config.locations.mine_enter.x .. ',' .. config.locations.mine_enter.z .. '/'
2778 state.mine = {}
2779
2780 if not fs.exists(state.mine_dir_path) then
2781 fs.makeDir(state.mine_dir_path)
2782 end
2783
2784 if fs.exists(state.mine_dir_path .. 'on') then
2785 state.on = true
2786 end
2787
2788 for _, level_and_chance in pairs(config.mine_levels) do
2789 local level = level_and_chance.level
2790 state.mine[level] = {y = level}
2791
2792 -- START WITH AT LEAST A MAIN SHAFT
2793 state.mine[level].main_shaft = {}
2794 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'}
2795 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'}
2796
2797 -- FOR EACH STRIP IN /mine/<x,z>/<level>/ PUT INTO MEMORY
2798 local level_dir_path = state.mine_dir_path .. level .. '/'
2799 if not fs.exists(level_dir_path) then
2800 fs.makeDir(level_dir_path)
2801 else
2802 for _, file_name in pairs(fs.list(level_dir_path)) do
2803 if file_name:sub(1, 1) ~= '.' then
2804 local file = fs.open(level_dir_path .. file_name, 'r')
2805 if file == nil then
2806 error('Failed to open file ' .. level_dir_path .. file_name)
2807 else
2808 if file_name == 'main_shaft' then
2809 local xs = string.gmatch(file.readAll(), '[^,]+')
2810 state.mine[level].main_shaft = {}
2811 state.mine[level].main_shaft.west = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'west'}
2812 state.mine[level].main_shaft.east = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'east'}
2813 else
2814 local zs = string.gmatch(file.readAll(), '[^,]+')
2815 local x = tonumber(file_name)
2816 state.mine[level][x] = {}
2817 state.mine[level][x].north = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'north'}
2818 state.mine[level][x].south = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'south'}
2819 end
2820 file.close()
2821 end
2822 end
2823 end
2824 end
2825 end
2826
2827 state.turtles_dir_path = state.mine_dir_path .. 'turtles/'
2828
2829 if not fs.exists(state.turtles_dir_path) then
2830 fs.makeDir(state.turtles_dir_path)
2831 end
2832
2833 local turtle_pairs = {}
2834
2835 for _, turtle_id in pairs(fs.list(state.turtles_dir_path)) do
2836 if turtle_id:sub(1, 1) ~= '.' then
2837 turtle_id = tonumber(turtle_id)
2838 local turtle = {id = turtle_id}
2839 state.turtles[turtle_id] = turtle
2840 local turtle_dir_path = state.turtles_dir_path .. turtle_id .. '/'
2841 if fs.exists(turtle_dir_path .. 'strip') then
2842 local file = fs.open(turtle_dir_path .. 'strip', 'r')
2843 if file == nil then
2844 error('Failed to open file ' .. turtle_dir_path .. 'strip')
2845 end
2846 local strip_args = string.gmatch(file.readAll(), '[^,]+')
2847
2848 local level = tonumber(strip_args())
2849 local name = strip_args()
2850 if name ~= 'main_shaft' then
2851 name = tonumber(name)
2852 end
2853 local orientation = strip_args()
2854
2855 if state.mine[level] and state.mine[level][name] and state.mine[level][name][orientation] then
2856 turtle.strip = state.mine[level][name][orientation]
2857 if fs.exists(turtle_dir_path .. 'deployed') then
2858 file = fs.open(turtle_dir_path .. 'deployed', 'r')
2859 if file == nil then
2860 error('Failed to open file ' .. turtle_dir_path .. 'deployed')
2861 end
2862 turtle.steps_left = tonumber(file.readAll())
2863 if not turtle_pairs[turtle.strip] then
2864 turtle_pairs[turtle.strip] = {}
2865 end
2866 table.insert(turtle_pairs[turtle.strip], turtle)
2867 end
2868 end
2869
2870 end
2871 if fs.exists(turtle_dir_path .. 'halt') then
2872 turtle.state = 'halt'
2873 end
2874 end
2875 end
2876
2877 for strip, turtles in pairs(turtle_pairs) do
2878 if #turtles == 2 then
2879 strip.turtles = turtles
2880 turtles[1].pair = turtles[2]
2881 turtles[2].pair = turtles[1]
2882 elseif #turtles == 1 and not config.use_chunky_turtles then
2883 strip.turtles = turtles
2884 end
2885 end
2886end
2887
2888
2889function write_strip(level, name)
2890 -- RECORD THE STATE OF A STRIP AT A GIVEN level AND name TO /mine/<center>/<level>/<name>
2891 local file = fs.open(state.mine_dir_path .. level .. '/' .. name, 'w')
2892 if name == 'main_shaft' then
2893 file.write(state.mine[level][name].west.x .. ',' .. state.mine[level][name].east.x)
2894 else
2895 file.write(state.mine[level][name].north.z .. ',' .. state.mine[level][name].south.z)
2896 end
2897 file.close()
2898end
2899
2900
2901function write_turtle_strip(turtle, strip)
2902 local file = fs.open(state.turtles_dir_path .. turtle.id .. '/strip', 'w')
2903 file.write(strip.y .. ',' .. strip.name .. ',' .. strip.orientation)
2904 file.close()
2905end
2906
2907
2908function halt(turtle)
2909 add_task(turtle, {action = 'pass', end_state = 'halt'})
2910 fs.open(state.turtles_dir_path .. turtle.id .. '/halt', 'w').close()
2911end
2912
2913
2914function unhalt(turtle)
2915 fs.delete(state.turtles_dir_path .. turtle.id .. '/halt', 'w')
2916end
2917
2918
2919function update_strip(turtle)
2920 -- RECORD THAT A STRIP HAS BEEN EXPLORED TO TURTLE'S POSITION
2921 local strip = turtle.strip
2922 if strip then
2923 if strip.orientation == 'north' then
2924 strip.z = math.min(strip.z, turtle.data.location.z)
2925 elseif strip.orientation == 'south' then
2926 strip.z = math.max(strip.z, turtle.data.location.z)
2927 elseif strip.orientation == 'east' then
2928 strip.x = math.max(strip.x, turtle.data.location.x)
2929 elseif strip.orientation == 'west' then
2930 strip.x = math.min(strip.x, turtle.data.location.x)
2931 end
2932 write_strip(strip.y, strip.name)
2933 end
2934end
2935
2936
2937function expand_mine(level, x)
2938 if not state.mine[level][x] then
2939 state.mine[level][x] = {}
2940 state.mine[level][x].north = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'north'}
2941 state.mine[level][x].south = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'south'}
2942 write_strip(level, x)
2943 end
2944end
2945
2946
2947function gen_next_strip()
2948 local level = get_mining_level()
2949 state.next_strip = get_closest_free_strip(level)
2950 if state.next_strip then
2951 state.min_fuel = (basics.distance(state.next_strip, config.locations.mine_enter) + config.mission_length) * 3
2952 else
2953 state.min_fuel = nil
2954 end
2955end
2956
2957
2958function get_closest_free_strip(level)
2959 local west_x = config.locations.mine_enter.x
2960 local east_x = config.locations.mine_enter.x
2961 local offset_x = 0
2962 local min_x = state.mine[level].main_shaft.west.x
2963 local max_x = state.mine[level].main_shaft.east.x
2964
2965 local closest_strip
2966 local distance
2967 local z_distance
2968 local min_dist = inf
2969
2970 while west_x >= min_x and east_x <= max_x and offset_x < min_dist do
2971 for _, x in pairs({west_x, east_x}) do
2972 for _, z_side in pairs({'north', 'south'}) do
2973 expand_mine(level, x)
2974 strip = state.mine[level][x][z_side]
2975 if not strip.turtles then
2976 z_distance = math.abs(strip.z - config.locations.mine_enter.z)
2977 distance = z_distance + math.abs(strip.x - config.locations.mine_enter.x)
2978 if distance < min_dist then
2979 min_dist = distance
2980 closest_strip = strip
2981 end
2982 end
2983 end
2984 end
2985 offset_x = offset_x + config.grid_width
2986 west_x = config.locations.mine_enter.x - offset_x
2987 east_x = config.locations.mine_enter.x + offset_x
2988 end
2989
2990 for _, strip in pairs({state.mine[level].main_shaft.west, state.mine[level].main_shaft.east}) do
2991 if not strip.turtles then
2992 distance = math.abs(strip.x - config.locations.mine_enter.x)
2993 if distance <= min_dist then
2994 min_dist = distance
2995 closest_strip = strip
2996 end
2997 end
2998 end
2999
3000 return closest_strip
3001end
3002
3003
3004function get_mining_level()
3005 local n = 0
3006 local r = math.random()
3007 for _, level_and_chance in pairs(config.mine_levels) do
3008 n = n + level_and_chance.chance
3009 if n > r then
3010 return level_and_chance.level
3011 end
3012 end
3013end
3014
3015
3016function good_on_fuel(mining_turtle, chunky_turtle)
3017 local fuel_needed = math.ceil(basics.distance(mining_turtle.data.location, config.locations.mine_exit) * 1.5)
3018 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))
3019end
3020
3021
3022function follow(chunky_turtle)
3023 add_task(chunky_turtle, {
3024 action = 'go_to_strip',
3025 data = {chunky_turtle.strip},
3026 end_state = 'wait',
3027 })
3028end
3029
3030
3031function go_mine(mining_turtle)
3032 update_strip(mining_turtle)
3033 add_task(mining_turtle, {
3034 action = 'mine_vein',
3035 data = {mining_turtle.strip.orientation},
3036 })
3037 add_task(mining_turtle, {
3038 action = 'clear_gravity_blocks',
3039 })
3040 if config.use_chunky_turtles then
3041 add_task(mining_turtle, {
3042 action = 'go_to_strip',
3043 data = {mining_turtle.strip},
3044 end_state = 'wait',
3045 end_function = follow,
3046 end_function_args = {mining_turtle.pair},
3047 })
3048 else
3049 add_task(mining_turtle, {
3050 action = 'go_to_strip',
3051 data = {mining_turtle.strip},
3052 end_state = 'wait',
3053 })
3054 end
3055 mining_turtle.steps_left = mining_turtle.steps_left - 1
3056 local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w')
3057 file.write(mining_turtle.steps_left)
3058 file.close()
3059end
3060
3061
3062function free_turtle(turtle)
3063 if turtle.pair then
3064 fs.delete(state.turtles_dir_path .. turtle.id .. '/deployed')
3065 fs.delete(state.turtles_dir_path .. turtle.pair.id .. '/deployed')
3066 turtle.pair.pair = nil
3067 turtle.pair = nil
3068 turtle.strip.turtles = nil
3069 end
3070end
3071
3072
3073function pair_turtles_finish()
3074 state.pair_hold = nil
3075end
3076
3077
3078function pair_turtles_send(chunky_turtle)
3079 add_task(chunky_turtle, {
3080 action = 'go_to_mine_enter',
3081 end_function = pair_turtles_finish
3082 })
3083
3084 add_task(chunky_turtle, {
3085 action = 'go_to_strip',
3086 data = {chunky_turtle.strip},
3087 end_state = 'wait',
3088 })
3089end
3090
3091
3092function pair_turtles_begin(turtle1, turtle2)
3093 local mining_turtle
3094 local chunky_turtle
3095 if turtle1.data.turtle_type == 'mining' then
3096 if turtle2.data.turtle_type ~= 'chunky' then
3097 error('Incompatable turtles')
3098 end
3099 mining_turtle = turtle1
3100 chunky_turtle = turtle2
3101 elseif turtle1.data.turtle_type == 'chunky' then
3102 if turtle2.data.turtle_type ~= 'mining' then
3103 error('Incompatable turtles')
3104 end
3105 chunky_turtle = turtle1
3106 mining_turtle = turtle2
3107 end
3108
3109 local strip = state.next_strip
3110 local level = strip.level
3111
3112 if not strip then
3113 gen_next_strip()
3114 add_task(mining_turtle, {action = 'pass', end_state = 'idle'})
3115 add_task(chunky_turtle, {action = 'pass', end_state = 'idle'})
3116 return
3117 end
3118
3119 print('Pairing ' .. mining_turtle.id .. ' and ' .. chunky_turtle.id)
3120
3121 mining_turtle.pair = chunky_turtle
3122 chunky_turtle.pair = mining_turtle
3123
3124 state.pair_hold = {mining_turtle, chunky_turtle}
3125
3126 mining_turtle.steps_left = config.mission_length
3127
3128 strip.turtles = {mining_turtle, chunky_turtle}
3129
3130 for _, turtle in pairs(strip.turtles) do
3131 turtle.strip = strip
3132 write_turtle_strip(turtle, strip)
3133 add_task(turtle, {action = 'pass', end_state = 'trip'})
3134 end
3135
3136 fs.open(state.turtles_dir_path .. chunky_turtle.id .. '/deployed', 'w').close()
3137 local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w')
3138 file.write(mining_turtle.steps_left)
3139 file.close()
3140
3141 add_task(mining_turtle, {
3142 action = 'go_to_mine_enter',
3143 end_function = pair_turtles_send,
3144 end_function_args = {chunky_turtle}
3145 })
3146
3147 add_task(mining_turtle, {
3148 action = 'go_to_strip',
3149 data = {mining_turtle.strip},
3150 end_state = 'wait',
3151 })
3152
3153 gen_next_strip()
3154end
3155
3156
3157function solo_turtle_begin(turtle)
3158
3159 local strip = state.next_strip
3160 local level = strip.level
3161
3162 if not strip then
3163 gen_next_strip()
3164 add_task(turtle, {action = 'pass', end_state = 'idle'})
3165 return
3166 end
3167
3168 print('Assigning ' .. turtle.id)
3169
3170 turtle.steps_left = config.mission_length
3171
3172 strip.turtles = {turtle}
3173
3174 for _, turtle in pairs(strip.turtles) do
3175 turtle.strip = strip
3176 write_turtle_strip(turtle, strip)
3177 add_task(turtle, {action = 'pass', end_state = 'trip'})
3178 end
3179
3180 local file = fs.open(state.turtles_dir_path .. turtle.id .. '/deployed', 'w')
3181 file.write(turtle.steps_left)
3182 file.close()
3183
3184 add_task(turtle, {
3185 action = 'go_to_mine_enter',
3186 })
3187
3188 add_task(turtle, {
3189 action = 'go_to_strip',
3190 data = {turtle.strip},
3191 end_state = 'wait',
3192 })
3193
3194 gen_next_strip()
3195end
3196
3197
3198function check_pair_fuel(turtle)
3199 if state.min_fuel then
3200 if (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level <= state.min_fuel) then
3201 add_task(turtle, {action = 'prepare', data = {state.min_fuel}})
3202 else
3203 add_task(turtle, {action = 'pass', end_state = 'pair'})
3204 end
3205 else
3206 gen_next_strip()
3207 end
3208end
3209
3210
3211function send_turtle_up(turtle)
3212 if turtle.data.location.y < config.locations.mine_enter.y then
3213 if turtle.strip then
3214
3215 if turtle.data.turtle_type == 'chunky' and turtle.data.location.y == turtle.strip.y then
3216 add_task(turtle, {action = 'delay', data={3}})
3217 end
3218
3219 add_task(turtle, {action = 'go_to_mine_exit', data = {turtle.strip}})
3220 end
3221 end
3222end
3223
3224
3225function initialize_turtle(turtle)
3226 local data = {session_id, config}
3227
3228 if turtle.state ~= 'halt' then
3229 turtle.state = 'lost'
3230 end
3231 turtle.task_id = 2
3232 turtle.tasks = {}
3233 add_task(turtle, {action = 'initialize', data = data})
3234end
3235
3236
3237function add_task(turtle, task)
3238 if not task.data then
3239 task.data = {}
3240 end
3241 table.insert(turtle.tasks, task)
3242end
3243
3244
3245function send_tasks(turtle)
3246 local task = turtle.tasks[1]
3247 if task then
3248 local turtle_data = turtle.data
3249 if turtle_data.request_id == turtle.task_id and turtle.data.session_id == session_id then
3250 if turtle_data.success then
3251 if task.end_state then
3252 if turtle.state == 'halt' and task.end_state ~= 'halt' then
3253 unhalt(turtle)
3254 end
3255 turtle.state = task.end_state
3256 end
3257 if task.end_function then
3258 if task.end_function_args then
3259 task.end_function(unpack(task.end_function_args))
3260 else
3261 task.end_function()
3262 end
3263 end
3264 table.remove(turtle.tasks, 1)
3265 end
3266 turtle.task_id = turtle.task_id + 1
3267 elseif (not turtle_data.busy) and ((not task.epoch) or (task.epoch > os.clock()) or (task.epoch + config.task_timeout < os.clock())) then
3268 -- ONLY SEND INSTRUCTION AFTER <config.task_timeout> SECONDS HAVE PASSED
3269 task.epoch = os.clock()
3270 print(string.format('Sending %s directive to %d', task.action, turtle.id))
3271 rednet.send(turtle.id, {
3272 action = task.action,
3273 data = task.data,
3274 request_id = turtle_data.request_id
3275 }, 'mastermine')
3276 end
3277 end
3278end
3279
3280
3281function user_input(input)
3282 -- PROCESS USER INPUT FROM USER_INPUT TABLE
3283 while #state.user_input > 0 do
3284 local input = table.remove(state.user_input, 1)
3285 local next_word = string.gmatch(input, '%S+')
3286 local command = next_word()
3287 local turtle_id_string = next_word()
3288 local turtle_id
3289 local turtles = {}
3290 if turtle_id_string and turtle_id_string ~= '*' then
3291 turtle_id = tonumber(turtle_id_string)
3292 if state.turtles[turtle_id] then
3293 turtles = {state.turtles[turtle_id]}
3294 end
3295 else
3296 turtles = state.turtles
3297 end
3298 if command == 'turtle' then
3299 -- SEND COMMAND DIRECTLY TO TURTLE
3300 local action = next_word()
3301 local data = {}
3302 for user_arg in next_word do
3303 table.insert(data, user_arg)
3304 end
3305 for _, turtle in pairs(turtles) do
3306 halt(turtle)
3307 add_task(turtle, {
3308 action = action,
3309 data = data,
3310 })
3311 end
3312 elseif command == 'clear' then
3313 for _, turtle in pairs(turtles) do
3314 turtle.tasks = {}
3315 add_task(turtle, {action = 'pass'})
3316 end
3317 elseif command == 'shutdown' then
3318 -- REBOOT TURTLE
3319 for _, turtle in pairs(turtles) do
3320 turtle.tasks = {}
3321 add_task(turtle, {action = 'pass'})
3322 rednet.send(turtle.id, {
3323 action = 'shutdown',
3324 }, 'mastermine')
3325 end
3326 elseif command == 'reboot' then
3327 -- REBOOT TURTLE
3328 for _, turtle in pairs(turtles) do
3329 turtle.tasks = {}
3330 add_task(turtle, {action = 'pass'})
3331 rednet.send(turtle.id, {
3332 action = 'reboot',
3333 }, 'mastermine')
3334 end
3335 elseif command == 'update' then
3336 -- FEED TURTLE DINNER
3337 for _, turtle in pairs(turtles) do
3338 turtle.tasks = {}
3339 add_task(turtle, {action = 'pass'})
3340 rednet.send(turtle.id, {
3341 action = 'update',
3342 }, 'mastermine')
3343 end
3344 elseif command == 'return' then
3345 -- BRING TURTLE HOME
3346 for _, turtle in pairs(turtles) do
3347 turtle.tasks = {}
3348 add_task(turtle, {action = 'pass'})
3349 halt(turtle)
3350 send_turtle_up(turtle)
3351 add_task(turtle, {action = 'go_to_home'})
3352 end
3353 elseif command == 'halt' then
3354 -- HALT TURTLE(S)
3355 for _, turtle in pairs(turtles) do
3356 turtle.tasks = {}
3357 add_task(turtle, {action = 'pass'})
3358 halt(turtle)
3359 end
3360 elseif command == 'reset' then
3361 -- HALT TURTLE(S)
3362 for _, turtle in pairs(turtles) do
3363 turtle.tasks = {}
3364 add_task(turtle, {action = 'pass'})
3365 add_task(turtle, {action = 'pass', end_state = 'lost'})
3366 end
3367 elseif command == 'on' or command == 'go' then
3368 -- ACTIVATE MINING NETWORK
3369 if not turtle_id_string then
3370 for _, turtle in pairs(state.turtles) do
3371 turtle.tasks = {}
3372 add_task(turtle, {action = 'pass'})
3373 end
3374 state.on = true
3375 fs.open(state.mine_dir_path .. 'on', 'w').close()
3376 end
3377 elseif command == 'off' or command == 'stop' then
3378 -- STANDBY MINING NETWORK
3379 if not turtle_id_string then
3380 for _, turtle in pairs(state.turtles) do
3381 turtle.tasks = {}
3382 add_task(turtle, {action = 'pass'})
3383 free_turtle(turtle)
3384 end
3385 state.on = nil
3386 fs.delete(state.mine_dir_path .. 'on')
3387 end
3388 elseif command == 'hubshutdown' then
3389 -- STANDBY MINING NETWORK
3390 if not turtle_id_string then
3391 os.shutdown()
3392 end
3393 elseif command == 'hubreboot' then
3394 -- STANDBY MINING NETWORK
3395 if not turtle_id_string then
3396 os.reboot()
3397 end
3398 elseif command == 'hubupdate' then
3399 -- STANDBY MINING NETWORK
3400 if not turtle_id_string then
3401 os.run({}, '/update')
3402 end
3403 elseif command == 'debug' then
3404 -- DEBUG
3405 end
3406 end
3407end
3408
3409
3410function command_turtles()
3411 local turtles_for_pair = {}
3412
3413 for _, turtle in pairs(state.turtles) do
3414
3415 if turtle.data then
3416
3417 if turtle.data.session_id ~= session_id then
3418 -- BABY TURTLE NEEDS TO LEARN
3419 if (not turtle.tasks) or (not turtle.tasks[1]) or (not (turtle.tasks[1].action == 'initialize')) then
3420 initialize_turtle(turtle)
3421 end
3422 end
3423
3424 if #turtle.tasks > 0 then
3425 -- TURTLE IS BUSY
3426 send_tasks(turtle)
3427
3428 elseif not turtle.data.location then
3429 -- TURTLE NEEDS A MAP
3430 add_task(turtle, {action = 'calibrate'})
3431
3432 elseif turtle.state ~= 'halt' then
3433
3434 if turtle.state == 'park' then
3435 -- TURTLE FOUND PARKING
3436 if state.on and (config.use_chunky_turtles or turtle.data.turtle_type == 'mining') then
3437 add_task(turtle, {action = 'pass', end_state = 'idle'})
3438 end
3439
3440 elseif not state.on and turtle.state ~= 'idle' then
3441 -- TURTLE HAS TO STOP
3442 add_task(turtle, {action = 'pass', end_state = 'idle'})
3443
3444 elseif turtle.state == 'lost' then
3445 -- TURTLE IS CONFUSED
3446 if turtle.data.location.y < config.locations.mine_enter.y and (turtle.pair or not config.use_chunky_turtles) then
3447 add_task(turtle, {action = 'pass', end_state = 'trip'})
3448 add_task(turtle, {
3449 action = 'go_to_strip',
3450 data = {turtle.strip},
3451 end_state = 'wait'
3452 })
3453 else
3454 add_task(turtle, {action = 'pass', end_state = 'idle'})
3455 end
3456
3457 elseif turtle.state == 'idle' then
3458 -- TURTLE IS BORED
3459 free_turtle(turtle)
3460 if turtle.data.location.y < config.locations.mine_enter.y then
3461 send_turtle_up(turtle)
3462 elseif not basics.in_area(turtle.data.location, config.locations.control_room_area) then
3463 halt(turtle)
3464 elseif turtle.data.item_count > 0 or (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level < config.fuel_per_unit) then
3465 add_task(turtle, {action = 'prepare', data = {config.fuel_per_unit}})
3466 elseif state.on then
3467 add_task(turtle, {
3468 action = 'go_to_waiting_room',
3469 end_function = check_pair_fuel,
3470 end_function_args = {turtle},
3471 })
3472 else
3473 add_task(turtle, {action = 'go_to_home', end_state = 'park'})
3474 end
3475
3476 elseif turtle.state == 'pair' then
3477 -- TURTLE NEEDS A FRIEND
3478 if config.use_chunky_turtles then
3479 if not state.pair_hold then
3480 if not turtle.pair then
3481 table.insert(turtles_for_pair, turtle)
3482 end
3483 else
3484 if not (state.pair_hold[1].pair and state.pair_hold[2].pair) then
3485 state.pair_hold = nil
3486 end
3487 end
3488 else
3489 solo_turtle_begin(turtle)
3490 end
3491
3492 elseif turtle.state == 'wait' then
3493 -- TURTLE GO DO SOME WORK
3494 if turtle.pair then
3495 if turtle.data.turtle_type == 'mining' and turtle.pair.state == 'wait' then
3496 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
3497 add_task(turtle, {action = 'pass', end_state = 'idle'})
3498 add_task(turtle.pair, {action = 'pass', end_state = 'idle'})
3499 elseif turtle.data.empty_slot_count == 0 then
3500 add_task(turtle, {
3501 action = 'dump',
3502 data = {reverse_shift[turtle.strip.orientation]}
3503 })
3504 else
3505 add_task(turtle, {action = 'pass', end_state = 'mine'})
3506 add_task(turtle.pair, {action = 'pass', end_state = 'mine'})
3507 go_mine(turtle)
3508 end
3509 end
3510 elseif not config.use_chunky_turtles then
3511 if turtle.steps_left <= 0 or turtle.data.empty_slot_count == 0 or not good_on_fuel(turtle) then
3512 add_task(turtle, {action = 'pass', end_state = 'idle'})
3513 else
3514 add_task(turtle, {action = 'pass', end_state = 'mine'})
3515 go_mine(turtle)
3516 end
3517 else
3518 add_task(turtle, {action = 'pass', end_state = 'idle'})
3519 end
3520 elseif turtle.state == 'mine' then
3521 if config.use_chunky_turtles and not turtle.pair then
3522 add_task(turtle, {action = 'pass', end_state = 'idle'})
3523 end
3524 end
3525 end
3526 end
3527 end
3528 if #turtles_for_pair == 2 then
3529 pair_turtles_begin(turtles_for_pair[1], turtles_for_pair[2])
3530 end
3531end
3532
3533
3534function main()
3535 -- INCREASE SESSION ID BY ONE
3536 if fs.exists('/session_id') then
3537 session_id = tonumber(fs.open('/session_id', 'r').readAll()) + 1
3538 else
3539 session_id = 1
3540 end
3541 local file = fs.open('/session_id', 'w')
3542 file.write(session_id)
3543 file.close()
3544
3545 -- LOAD MINE INTO MEMORY
3546 load_mine()
3547
3548 -- FIND THE CLOSEST STRIP
3549 gen_next_strip()
3550
3551 local cycle = 0
3552 while true do
3553 print('Cycle: ' .. cycle)
3554 user_input() -- PROCESS USER INPUT
3555 command_turtles() -- COMMAND TURTLES
3556 sleep(0.1) -- DELAY 0.1 SECONDS
3557 cycle = cycle + 1
3558 end
3559end
3560
3561
3562main()]===],
3563}
3564
3565for k, v in pairs(files) do
3566 local file = fs.open(fs.combine(path, k), 'w')
3567 file.write(v)
3568 file.close()
3569end
3570