output_dir = ... if not output_dir then output_dir = '' end path = shell.resolve(output_dir) if not fs.isDir(path) then error(path .. ' is not a directory') end files = { ["LICENSE"] = [===[MIT License Copyright (c) 2020 MerlinLikeTheWizard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.]===], ["README.md"] = [===[# Mastermine A fully automated strip mining network for ComputerCraft turtles! Here's all the code for anyone who is interested! Check out the tutorial below for installation instructions. Also, here are steps for a quick install via pastebin: 1. Place your advanced computer next to a disk drive with a blank disk in. 2. Run `pastebin get CtcSGkpc mastermine.lua` 3. Run `mastermine disk` 4. Run `disk/hub.lua` ## Play with or without PeripheralsPlusOne I 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` ## Video description: [![https://www.youtube.com/watch?v=2I2VXl9Pg6Q](https://img.youtube.com/vi/2I2VXl9Pg6Q/0.jpg)](https://www.youtube.com/watch?v=2I2VXl9Pg6Q) ## Video tutorial: [![https://www.youtube.com/watch?v=2DTP1LXuiCg](https://img.youtube.com/vi/2DTP1LXuiCg/0.jpg)](https://www.youtube.com/watch?v=2DTP1LXuiCg) ## User commands: * `on/go` * `off/stop` * `turtle <#> ` * `update <#>` * `reboot <#>` * `shutdown <#>` * `reset <#>` * `clear <#>` * `halt <#>` * `return <#>` * `hubupdate` * `hubreboot` * `hubshutdown` use `*` as notation for all turtles ## Required mods: https://www.curseforge.com/minecraft/mc-mods/cc-tweaked https://github.com/rolandoislas/PeripheralsPlusOne Required by PeripheralsPlusOne: https://www.curseforge.com/minecraft/mc-mods/the-framework]===], ["hub.lua"] = [===[if fs.exists('/disk/hub_files/session_id') then fs.delete('/disk/hub_files/session_id') end if fs.exists('/session_id') then fs.copy('/session_id', '/disk/hub_files/session_id') end if fs.exists('/disk/hub_files/mine') then fs.delete('/disk/hub_files/mine') end if fs.exists('/mine') then fs.copy('/mine', '/disk/hub_files/mine') end for _, filename in pairs(fs.list('/')) do if filename ~= 'rom' and filename ~= 'disk' and filename ~= 'openp' and filename ~= 'ppp' and filename ~= 'persistent' then fs.delete(filename) end end for _, filename in pairs(fs.list('/disk/hub_files')) do fs.copy('/disk/hub_files/' .. filename, '/' .. filename) end os.reboot()]===], ["pocket.lua"] = [===[local src, dest = ... fs.copy(fs.combine(src, 'pocket_files/update'), fs.combine(dest, 'update')) file = fs.open(fs.combine(dest, 'hub_id'), 'w') file.write(os.getComputerID()) file.close()]===], ["turtle.lua"] = [===[for _, filename in pairs(fs.list('/')) do if filename ~= 'rom' and filename ~= 'disk' and filename ~= 'openp' and filename ~= 'ppp' and filename ~= 'persistent' then fs.delete(filename) end end for _, filename in pairs(fs.list('/disk/turtle_files')) do fs.copy('/disk/turtle_files/' .. filename, '/' .. filename) end print("Enter ID of Hub computer to link to: ") hub_id = tonumber(read()) if hub_id == nil then error("Invalid ID") end file = fs.open('/hub_id', 'w') file.write(hub_id) file.close() print("Linked") sleep(1) os.reboot()]===], ["turtle_files/actions.lua"] = [===[inf = basics.inf str_xyz = basics.str_xyz --lua_print = print --log_file = fs.open('log.txt', 'w') --function print(thing) -- lua_print(thing) -- log_file.writeLine(thing) --end bumps = { north = { 0, 0, -1}, south = { 0, 0, 1}, east = { 1, 0, 0}, west = {-1, 0, 0}, } left_shift = { north = 'west', south = 'east', east = 'north', west = 'south', } right_shift = { north = 'east', south = 'west', east = 'south', west = 'north', } reverse_shift = { north = 'south', south = 'north', east = 'west', west = 'east', } move = { forward = turtle.forward, up = turtle.up, down = turtle.down, back = turtle.back, left = turtle.turnLeft, right = turtle.turnRight } detect = { forward = turtle.detect, up = turtle.detectUp, down = turtle.detectDown } inspect = { forward = turtle.inspect, up = turtle.inspectUp, down = turtle.inspectDown } dig = { forward = turtle.dig, up = turtle.digUp, down = turtle.digDown } attack = { forward = turtle.attack, up = turtle.attackUp, down = turtle.attackDown } getblock = { up = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end return {x = pos.x, y = pos.y + 1, z = pos.z} end, down = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end return {x = pos.x, y = pos.y - 1, z = pos.z} end, forward = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end local bump = bumps[fac] return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]} end, back = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end local bump = bumps[fac] return {x = pos.x - bump[1], y = pos.y - bump[2], z = pos.z - bump[3]} end, left = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end local bump = bumps[left_shift[fac]] return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]} end, right = function(pos, fac) if not pos then pos = state.location end if not fac then fac = state.orientation end local bump = bumps[right_shift[fac]] return {x = pos.x + bump[1], y = pos.y + bump[2], z = pos.z + bump[3]} end, } function digblock(direction) dig[direction]() return true end function delay(duration) sleep(duration) return true end function up() return go('up') end function forward() return go('forward') end function down() return go('down') end function back() return go('back') end function left() return go('left') end function right() return go('right') end function follow_route(route) for step in route:gmatch'.' do if step == 'u' then if not go('up') then return false end elseif step == 'f' then if not go('forward') then return false end elseif step == 'd' then if not go('down') then return false end elseif step == 'b' then if not go('back') then return false end elseif step == 'l' then if not go('left') then return false end elseif step == 'r' then if not go('right') then return false end end end return true end function face(orientation) if state.orientation == orientation then return true elseif right_shift[state.orientation] == orientation then if not go('right') then return false end elseif left_shift[state.orientation] == orientation then if not go('left') then return false end elseif right_shift[right_shift[state.orientation]] == orientation then if not go('right') then return false end if not go('right') then return false end else return false end return true end function log_movement(direction) if direction == 'up' then state.location.y = state.location.y +1 elseif direction == 'down' then state.location.y = state.location.y -1 elseif direction == 'forward' then bump = bumps[state.orientation] state.location = {x = state.location.x + bump[1], y = state.location.y + bump[2], z = state.location.z + bump[3]} elseif direction == 'back' then bump = bumps[state.orientation] state.location = {x = state.location.x - bump[1], y = state.location.y - bump[2], z = state.location.z - bump[3]} elseif direction == 'left' then state.orientation = left_shift[state.orientation] elseif direction == 'right' then state.orientation = right_shift[state.orientation] end return true end function go(direction, nodig) if not nodig then if detect[direction] then if detect[direction]() then safedig(direction) end end end if not move[direction] then return false end if not move[direction]() then if attack[direction] then attack[direction]() end return false end log_movement(direction) return true end function go_to_axis(axis, coordinate, nodig) local delta = coordinate - state.location[axis] if delta == 0 then return true end if axis == 'x' then if delta > 0 then if not face('east') then return false end else if not face('west') then return false end end elseif axis == 'z' then if delta > 0 then if not face('south') then return false end else if not face('north') then return false end end end for i = 1, math.abs(delta) do if axis == 'y' then if delta > 0 then if not go('up', nodig) then return false end else if not go('down', nodig) then return false end end else if not go('forward', nodig) then return false end end end return true end function go_to(end_location, end_orientation, path, nodig) if path then for axis in path:gmatch'.' do if not go_to_axis(axis, end_location[axis], nodig) then return false end end elseif end_location.path then for axis in end_location.path:gmatch'.' do if not go_to_axis(axis, end_location[axis], nodig) then return false end end else return false end if end_orientation then if not face(end_orientation) then return false end elseif end_location.orientation then if not face(end_location.orientation) then return false end end return true end function go_route(route, xyzo) local xyz_string if xyzo then xyz_string = str_xyz(xyzo) end local location_str = basics.str_xyz(state.location) while route[location_str] and location_str ~= xyz_string do if not go_to(route[location_str], nil, 'xyz') then return false end location_str = basics.str_xyz(state.location) end if xyzo then if location_str ~= xyz_string then return false end if xyzo.orientation then if not face(xyzo.orientation) then return false end end end return true end function go_to_home() state.updated_not_home = nil if basics.in_area(state.location, config.locations.home_area) then return true elseif basics.in_area(state.location, config.locations.greater_home_area) then if not go_to_home_exit() then return false end elseif basics.in_area(state.location, config.locations.waiting_room_area) then if not go_to(config.locations.mine_exit, nil, config.paths.waiting_room_to_mine_exit, true) then return false end elseif state.location.y < config.locations.mine_enter.y then return false end if config.locations.main_loop_route[basics.str_xyz(state.location)] then if not go_route(config.locations.main_loop_route, config.locations.home_enter) then return false end elseif basics.in_area(state.location, config.locations.control_room_area) then if not go_to(config.locations.home_enter, nil, config.paths.control_room_to_home_enter, true) then return false end else return false end if not forward() then return false end while detect.down() do if not forward() then return false end end if not down() then return false end if not right() then return false end if not right() then return false end return true end function go_to_home_exit() if basics.in_area(state.location, config.locations.greater_home_area) then if not go_to(config.locations.home_exit, nil, config.paths.home_to_home_exit) then return false end elseif config.locations.main_loop_route[basics.str_xyz(state.location)] then if not go_route(config.locations.main_loop_route, config.locations.home_exit) then return false end else return false end return true end function go_to_item_drop() if not config.locations.main_loop_route[basics.str_xyz(state.location)] then if not go_to_home() then return false end if not go_to_home_exit() then return false end end if not go_route(config.locations.main_loop_route, config.locations.item_drop) then return false end return true end function go_to_refuel() if not config.locations.main_loop_route[basics.str_xyz(state.location)] then if not go_to_home() then return false end if not go_to_home_exit() then return false end end if not go_route(config.locations.main_loop_route, config.locations.refuel) then return false end return true end function go_to_waiting_room() if not basics.in_area(state.location, config.locations.waiting_room_line_area) then if not go_to_home() then return false end end if not go_to(config.locations.waiting_room, nil, config.paths.home_to_waiting_room) then return false end return true end function go_to_mine_enter() if not go_route(config.locations.waiting_room_to_mine_enter_route) then return false end return true end function go_to_strip(strip) if state.location.y < config.locations.mine_enter.y or basics.in_location(state.location, config.locations.mine_enter) then if state.type == 'mining' then local bump = bumps[strip.orientation] strip = { x = strip.x + bump[1], y = strip.y + bump[2], z = strip.z + bump[3], orientation = strip.orientation } end if not go_to(strip, nil, config.paths.mine_enter_to_strip) then return false end return true end end function go_to_mine_exit(strip) 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 if state.location.x == config.locations.mine_enter.x and state.location.z == config.locations.mine_enter.z then -- If directly under mine_enter, shift over to exit if not go_to_axis('z', config.locations.mine_exit.z) then return false end elseif state.location.x ~= config.locations.mine_exit.x or state.location.z ~= config.locations.mine_exit.z then -- If NOT directly under mine_exit go to proper y if not go_to_axis('y', strip.y + 1) then return false end if state.location.z ~= config.locations.mine_enter.z and strip.z ~= config.locations.mine_enter.z then -- If not in main_shaft, find your strip if not go_to_axis('x', strip.x) then return false end end if state.location.x ~= config.locations.mine_exit.x then -- If not in strip x = origin, go to main_shaft if not go_to_axis('z', config.locations.mine_enter.z) then return false end end end if not go_to(config.locations.mine_exit, nil, 'xzy') then return false end return true end end function safedig(direction) -- DIG IF BLOCK NOT ON BLACKLIST if not direction then direction = 'forward' end local block_name = ({inspect[direction]()})[2].name if block_name then for _, word in pairs(config.dig_disallow) do if string.find(string.lower(block_name), word) then return false end end return dig[direction]() end return true end function dump_items(omit) for slot = 1, 16 do if turtle.getItemCount(slot) > 0 and ((not omit) or (not omit[turtle.getItemDetail(slot).name])) then turtle.select(slot) if not turtle.drop() then return false end end end return true end function prepare(min_fuel_amount) if state.item_count > 0 then if not go_to_item_drop() then return false end if not dump_items(config.fuelnames) then return false end end local min_fuel_amount = min_fuel_amount + config.fuel_padding if not go_to_refuel() then return false end if not dump_items() then return false end turtle.select(1) if turtle.getFuelLevel() ~= 'unlimited' then while turtle.getFuelLevel() < min_fuel_amount do if not turtle.suck(math.min(64, math.ceil(min_fuel_amount / config.fuel_per_unit))) then return false end turtle.refuel() end end return true end function calibrate() -- GEOPOSITION BY MOVING TO ADJACENT BLOCK AND BACK local sx, sy, sz = gps.locate() -- if sx == config.interface.x and sy == config.interface.y and sz == config.interface.z then -- refuel() -- end if not sx or not sy or not sz then return false end for i = 1, 4 do -- TRY TO FIND EMPTY ADJACENT BLOCK if not turtle.detect() then break end if not turtle.turnRight() then return false end end if turtle.detect() then -- TRY TO DIG ADJACENT BLOCK for i = 1, 4 do safedig('forward') if not turtle.detect() then break end if not turtle.turnRight() then return false end end if turtle.detect() then return false end end if not turtle.forward() then return false end local nx, ny, nz = gps.locate() if nx == sx + 1 then state.orientation = 'east' elseif nx == sx - 1 then state.orientation = 'west' elseif nz == sz + 1 then state.orientation = 'south' elseif nz == sz - 1 then state.orientation = 'north' else return false end state.location = {x = nx, y = ny, z = nz} print('Calibrated to ' .. str_xyz(state.location, state.orientation)) back() if basics.in_area(state.location, config.locations.home_area) then face(left_shift[left_shift[config.locations.homes.increment]]) end return true end function initialize(session_id, config_values) -- INITIALIZE TURTLE state.session_id = session_id -- COPY CONFIG DATA INTO MEMORY for k, v in pairs(config_values) do config[k] = v end -- DETERMINE TURTLE TYPE state.peripheral_left = peripheral.getType('left') state.peripheral_right = peripheral.getType('right') if state.peripheral_left == 'chunkLoader' or state.peripheral_right == 'chunkLoader' or state.peripheral_left == 'chunky' or state.peripheral_right == 'chunky' then state.type = 'chunky' for k, v in pairs(config.chunky_turtle_locations) do config.locations[k] = v end else state.type = 'mining' for k, v in pairs(config.mining_turtle_locations) do config.locations[k] = v end if state.peripheral_left == 'modem' then state.peripheral_right = 'pick' else state.peripheral_left = 'pick' end end state.request_id = 1 state.initialized = true return true end function getcwd() local running_program = shell.getRunningProgram() local program_name = fs.getName(running_program) return "/" .. running_program:sub(1, #running_program - #program_name) end function pass() return true end function dump(direction) if not face(direction) then return false end if ({inspect.forward()})[2].name ~= 'computercraft:turtle_advanced' then return false end for slot = 1, 16 do if turtle.getItemCount(slot) > 0 then turtle.select(slot) turtle.drop() end end return true end function checkTags(data) if type(data.tags) ~= 'table' then return false end if not config.blocktags then return false end for k,v in pairs(data.tags) do if config.blocktags[k] then return true end end return false end function detect_ore(direction) local block = ({inspect[direction]()})[2] if config.orenames[block.name] then return true elseif checkTags(block) then return true end return false end function scan(valid, ores) local checked_left = false local checked_right = false local f = str_xyz(getblock.forward()) local u = str_xyz(getblock.up()) local d = str_xyz(getblock.down()) local l = str_xyz(getblock.left()) local r = str_xyz(getblock.right()) local b = str_xyz(getblock.back()) if not valid[f] and valid[f] ~= false then valid[f] = detect_ore('forward') ores[f] = valid[f] end if not valid[u] and valid[u] ~= false then valid[u] = detect_ore('up') ores[u] = valid[u] end if not valid[d] and valid[d] ~= false then valid[d] = detect_ore('down') ores[d] = valid[d] end if not valid[l] and valid[l] ~= false then left() checked_left = true valid[l] = detect_ore('forward') ores[l] = valid[l] end if not valid[r] and valid[r] ~= false then right() if checked_left then right() end checked_right = true valid[r] = detect_ore('forward') ores[r] = valid[r] end if not valid[b] and valid[b] ~= false then if checked_right then right() elseif checked_left then left() else right(2) end valid[b] = detect_ore('forward') ores[b] = valid[b] end end function fastest_route(area, pos, fac, end_locations) local queue = {} local explored = {} table.insert(queue, { coords = {x = pos.x, y = pos.y, z = pos.z}, facing = fac, path = '', } ) explored[str_xyz(pos, fac)] = true while #queue > 0 do local node = table.remove(queue, 1) if end_locations[str_xyz(node.coords)] or end_locations[str_xyz(node.coords, node.facing)] then return node.path end for _, step in pairs({ {coords = node.coords, facing = left_shift[node.facing], path = node.path .. 'l'}, {coords = node.coords, facing = right_shift[node.facing], path = node.path .. 'r'}, {coords = getblock.forward(node.coords, node.facing), facing = node.facing, path = node.path .. 'f'}, {coords = getblock.up(node.coords, node.facing), facing = node.facing, path = node.path .. 'u'}, {coords = getblock.down(node.coords, node.facing), facing = node.facing, path = node.path .. 'd'}, }) do explore_string = str_xyz(step.coords, step.facing) if not explored[explore_string] and (not area or area[str_xyz(step.coords)]) then explored[explore_string] = true table.insert(queue, step) end end end end function mine_vein(direction) if not face(direction) then return false end -- Log starting location local start = str_xyz({x = state.location.x, y = state.location.y, z = state.location.z}, state.orientation) -- Begin block map local valid = {} local ores = {} valid[str_xyz(state.location)] = true valid[str_xyz(getblock.back(state.location, state.orientation))] = false for i = 1, config.vein_max do -- Scan adjacent scan(valid, ores) -- Search for nearest ore local route = fastest_route(valid, state.location, state.orientation, ores) -- Check if there is one if not route then break end -- Retrieve ore turtle.select(1) if not follow_route(route) then return false end ores[str_xyz(state.location)] = nil end if not follow_route(fastest_route(valid, state.location, state.orientation, {[start] = true})) then return false end if detect.up() then safedig('up') end return true end function clear_gravity_blocks() for _, direction in pairs({'forward', 'up'}) do while config.gravitynames[ ({inspect[direction]()})[2].name ] do safedig(direction) sleep(1) end end return true end]===], ["turtle_files/basics.lua"] = [===[inf = 1e309 bumps = { north = { 0, 0, -1}, south = { 0, 0, 1}, east = { 1, 0, 0}, west = {-1, 0, 0}, } left_shift = { north = 'west', south = 'east', east = 'north', west = 'south', } right_shift = { north = 'east', south = 'west', east = 'south', west = 'north', } reverse_shift = { north = 'south', south = 'north', east = 'west', west = 'east', } function dprint(thing) -- PRINT; IF TABLE PRINT EACH ITEM if type(thing) == 'table' then for k, v in pairs(thing) do print(tostring(k) .. ': ' .. tostring(v)) end else print(thing) end return true end function str_xyz(coords, facing) if facing then return coords.x .. ',' .. coords.y .. ',' .. coords.z .. ':' .. facing else return coords.x .. ',' .. coords.y .. ',' .. coords.z end end function distance(point_1, point_2) return math.abs(point_1.x - point_2.x) + math.abs(point_1.y - point_2.y) + math.abs(point_1.z - point_2.z) end function in_area(xyz, area) 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 end function in_location(xyzo, location) for _, axis in pairs({'x', 'y', 'z'}) do if location[axis] then if location[axis] ~= xyzo[axis] then return false end end end return true end]===], ["turtle_files/config.lua"] = [===[]===], ["turtle_files/mastermine.lua"] = [===[function parse_requests() -- PROCESS ALL REDNET REQUESTS while #state.requests > 0 do local request = table.remove(state.requests, 1) sender, message, protocol = request[1], request[2], request[3] if message.action == 'shutdown' then os.shutdown() elseif message.action == 'reboot' then os.reboot() elseif message.action == 'update' then os.run({}, '/update') elseif message.request_id == -1 or message.request_id == state.request_id then -- MAKE SURE REQUEST IS CURRENT if state.initialized or message.action == 'initialize' then print('Directive: ' .. message.action) state.busy = true state.success = actions[message.action](unpack(message.data)) -- EXECUTE DESIRED FUNCTION WITH DESIRED ARGUMENTS state.busy = false if not state.success then sleep(1) end state.request_id = state.request_id + 1 end end end end function main() state.last_ping = os.clock() while true do parse_requests() sleep(0.3) end end main()]===], ["turtle_files/receive.lua"] = [===[-- CONTINUOUSLY RECIEVE REDNET MESSAGES while true do signal = {rednet.receive('mastermine')} if signal[2].action == 'shutdown' then os.shutdown() elseif signal[2].action == 'reboot' then os.reboot() elseif signal[2].action == 'update' then os.run({}, '/update') else table.insert(state.requests, signal) end end]===], ["turtle_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS hub_id = tonumber(fs.open('/hub_id', 'r').readAll()) while true do state.item_count = 0 state.empty_slot_count = 16 for slot = 1, 16 do slot_item_count = turtle.getItemCount(slot) if slot_item_count > 0 then state.empty_slot_count = state.empty_slot_count - 1 state.item_count = state.item_count + slot_item_count end end rednet.send(hub_id, { session_id = state.session_id, request_id = state.request_id, turtle_type = state.type, peripheral_left = state.peripheral_left, peripheral_right = state.peripheral_right, updated_not_home = state.updated_not_home, location = state.location, orientation = state.orientation, fuel_level = turtle.getFuelLevel(), item_count = state.item_count, empty_slot_count = state.empty_slot_count, distance = state.distance, strip = state.strip, success = state.success, busy = state.busy, }, 'turtle_report') sleep(0.5) end]===], ["turtle_files/startup.lua"] = [===[-- SET LABEL os.setComputerLabel('Turtle ' .. os.getComputerID()) -- INITIALIZE APIS if fs.exists('/apis') then fs.delete('/apis') end fs.makeDir('/apis') fs.copy('/config.lua', '/apis/config') fs.copy('/state.lua', '/apis/state') fs.copy('/basics.lua', '/apis/basics') fs.copy('/actions.lua', '/apis/actions') os.loadAPI('/apis/config') os.loadAPI('/apis/state') os.loadAPI('/apis/basics') os.loadAPI('/apis/actions') -- OPEN REDNET for _, side in pairs({'back', 'top', 'left', 'right'}) do if peripheral.getType(side) == 'modem' then rednet.open(side) break end end -- IF UPDATED PRINT "UPDATED" if fs.exists('/updated') then fs.delete('/updated') print('UPDATED') state.updated_not_home = true end -- LAUNCH PROGRAMS AS SEPARATE THREADS multishell.launch({}, '/report.lua') multishell.launch({}, '/receive.lua') multishell.launch({}, '/mastermine.lua') multishell.setTitle(2, 'report') multishell.setTitle(3, 'receive') multishell.setTitle(4, 'mastermine')]===], ["turtle_files/state.lua"] = [===[request_id = 1 requests = {} busy = false]===], ["turtle_files/update"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll()) print('Sending update request...') rednet.send(hub_id, '/disk/turtle_files/', 'update_request') local sender, message, protocal = rednet.receive('update_package') for _, file_name in pairs(fs.list('/')) do if file_name ~= 'rom' and file_name ~= 'persistent' then fs.delete(file_name) end end for file_name, file_contents in pairs(message) do file = fs.open(file_name, 'w') file.write(file_contents) file.close() end os.reboot()]===], ["turtle_files/updated"] = [===[]===], ["pocket_files/info.lua"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll()) while true do sender, hub_state, _ = rednet.receive('hub_report') if sender == hub_id then term.clear() term.setCursorPos(1, 1) term.setTextColor(colors.white) term.write('POWER: ') if hub_state.on then term.setTextColor(colors.green) print('ON') else term.setTextColor(colors.red) print('OFF') end term.setTextColor(colors.white) term.write('TURTLES PARKED: ') if hub_state.turtles_parked >= hub_state.turtle_count then term.setTextColor(colors.green) else term.setTextColor(colors.red) end term.write(hub_state.turtles_parked) end end]===], ["pocket_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS hub_id = tonumber(fs.open('/hub_id', 'r').readAll()) while true do local x, y, z = gps.locate() rednet.send(hub_id, { location = {x = x, y = y, z = z}, }, 'pocket_report') sleep(0.5) end]===], ["pocket_files/startup.lua"] = [===[-- SET LABEL os.setComputerLabel('pocket ' .. os.getComputerID()) -- OPEN REDNET rednet.open('back') -- IF UPDATED PRINT "UPDATED" if fs.exists('/updated') then fs.delete('/updated') print('UPDATED') end -- LAUNCH PROGRAMS AS SEPARATE THREADS multishell.launch({}, '/user.lua') multishell.launch({}, '/info.lua') multishell.launch({}, '/report.lua') multishell.setTitle(2, 'usr') multishell.setTitle(3, 'info') multishell.setTitle(4, 'rep')]===], ["pocket_files/update"] = [===[hub_id = tonumber(fs.open('/hub_id', 'r').readAll()) rednet.open('back') print('Sending update request...') rednet.broadcast('/disk/pocket_files/', 'update_request') local sender, message, protocal = rednet.receive('update_package') for _, file_name in pairs(fs.list('/')) do if file_name ~= 'rom' and file_name ~= 'disk' and file_name ~= 'persistent' then fs.delete(file_name) end end for file_name, file_contents in pairs(message) do file = fs.open(file_name, 'w') file.write(file_contents) file.close() end os.reboot()]===], ["pocket_files/updated"] = [===[]===], ["pocket_files/user.lua"] = [===[-- CONTINUOUSLY AWAIT USER INPUT AND PLACE IN TABLE while true do rednet.broadcast(read(), 'user_input') end]===], ["hub_files/basics.lua"] = [===[inf = 1e309 bumps = { north = { 0, 0, -1}, south = { 0, 0, 1}, east = { 1, 0, 0}, west = {-1, 0, 0}, } left_shift = { north = 'west', south = 'east', east = 'north', west = 'south', } right_shift = { north = 'east', south = 'west', east = 'south', west = 'north', } reverse_shift = { north = 'south', south = 'north', east = 'west', west = 'east', } function dprint(thing) -- PRINT; IF TABLE PRINT EACH ITEM if type(thing) == 'table' then for k, v in pairs(thing) do print(tostring(k) .. ': ' .. tostring(v)) end else print(thing) end return true end function str_xyz(coords, facing) if facing then return coords.x .. ',' .. coords.y .. ',' .. coords.z .. ':' .. facing else return coords.x .. ',' .. coords.y .. ',' .. coords.z end end function distance(point_1, point_2) return math.abs(point_1.x - point_2.x) + math.abs(point_1.y - point_2.y) + math.abs(point_1.z - point_2.z) end function in_area(xyz, area) 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 end function in_location(xyzo, location) for _, axis in pairs({'x', 'y', 'z'}) do if location[axis] then if location[axis] ~= xyzo[axis] then return false end end end return true end]===], ["hub_files/config.lua"] = [===[inf = 1e309 ---==[ MINE ]==--- -- LOCATION OF THE CENTER OF THE MINE -- the y value should be set to the height -- 1 above the surface: -- -- Y -- ####### ####### -- ####### ####### mine_entrance = {x = 104, y = 76, z = 215} c = mine_entrance -- WHETHER OR NOT TURTLES NEED PAIRS -- added this because a good number of -- people were asking for the ability to -- disale chunky turtles in case they -- couldn't access the peripherals mod. -- WARNING: not using chunky turtles will -- result in narcoleptic turtles! use_chunky_turtles = false -- SPACE IN BLOCKS BETWEEN MINESHAFTS -- too close means less chance of finding -- ore veins, too far means longer commute -- times for turtles. grid_width = 8 -- MAXIMUM MINING AMOUNT PER TRIP -- PER TURTLE -- most efficient would be to make this -- number huge, but turtles may be gone a -- while (plus harder to recall). mission_length = 150 -- MAXIMUM BLOCKS A TURTLE MINES IN A -- SINGLE ORE VEIN -- veins can contain multiple types of ore -- and still count as one. also turtles will -- continue on a vein even when their -- inventory fills up, so this prevents them -- losing too many rousources. vein_max = 64 -- EXTRA FUEL FOR TURTLES TO BRING ALONG, -- JUST IN CASE fuel_padding = 30 -- FUEL PER ITEM -- for coal default is 80. Other fuel sources -- can be used without changing this number, -- should be fine. fuel_per_unit = 80 -- TIME AFTER LAST PING TO DECLARE TURTLE -- DISCONNECTED turtle_timeout = 5 -- TIME AFTER LAST PING TO DECLARE POCKET -- COMPUTER DISCONNECTED pocket_timeout = 5 -- TIME TO WAIT AFTER SENDING TASK WITH NO -- RESPONSE TO RESEND task_timeout = 0.5 -- EVERY BLOCK NAME CONTAINING ANY OF THESE -- STRINGS WILL NOT BE MINED -- e.g. "chest" will prevent "minecraft:trapped_chest". -- ore types should not be put on this list, -- but if not desired should be removed from -- below. dig_disallow = { 'computer', 'chest', 'chair', } mine_levels = { -- LEVELS INCLUDED IN THE MINE -- turtles will pick randomly with weight -- between each listed level. -- -- Level chances should sum to 1.0 -- e.g. -- -- {level = 50, chance = 0.3}, -- {level = 40, chance = 0.2}, -- {level = 12, chance = 0.5}, {level = 63, chance = 1.0}, } paths = { -- THE ORDER IN WHICH TURTLES WILL -- TRAVERSE AXES BETWEEN AREAS -- recommended not to change this one. home_to_home_exit = 'zyx', control_room_to_home_enter = 'yzx', home_to_waiting_room = 'zyx', waiting_room_to_mine_exit = 'yzx', mine_enter_to_strip = 'yxz', } locations = { -- THE VARIUS PLACES THE TURTLES MOVE -- BETWEEN -- coordinates are relative to the -- variable. areas are for -- altering turtle behavior to prevent -- collisions and stuff. -- THE BLOCK TURTLES WILL GO TO BEFORE -- DECENDING mine_enter = {x = c.x+0, y = c.y+0, z = c.z+0}, -- THE BLOCK TURTLES WILL COME UP TO -- FROM THE MINE -- one block higher by default. mine_exit = {x = c.x+0, y = c.y+1, z = c.z+1}, -- THE BLOCK TURTLES GO TO IN ORDER -- TO ACCESS THE CHEST FOR ITEMS item_drop = {x = c.x+2, y = c.y+1, z = c.z+1, orientation = 'east'}, -- THE BLOCK TURTLES GO TO IN ORDER -- TO ACCESS THE CHEST FOR FUEL refuel = {x = c.x+2, y = c.y+1, z = c.z+0, orientation = 'east'}, -- THE AREA ENCOMPASSING TURTLE HOMES -- where they sleep. greater_home_area = { min_x = -inf, max_x = c.x-3, min_y = c.y+0, max_y = c.y+1, min_z = c.z-1, max_z = c.z+2 }, -- THE ROOM WHERE THE MAGIC HAPPENS -- turtles can find there way home from -- here. control_room_area = { min_x = c.x-16, max_x = c.x+8, min_y = c.y+0, max_y = c.y+8, min_z = c.z-8, max_z = c.z+8 }, -- WHERE TURTLES QUEUE TO BE PAIRED UP waiting_room_line_area = { min_x = -inf, max_x = c.x-2, min_y = c.y+0, max_y = c.y+0, min_z = c.z+0, max_z = c.z+1 }, -- THE AREA ENCOMPASSING BOTH WHERE -- TURTLES PAIR UP, AND THE PATH THEY -- TAKE TO THE MINE ENTRANCE waiting_room_area = { min_x = c.x-2, max_x = c.x+0, min_y = c.y+0, max_y = c.y+0, min_z = c.z+0, max_z = c.z+1 }, -- THE LOOP TURTLES GO IN BETWEEN THEIR -- HOMES, THE ITEM DROP STATION, AND THE -- REFUELING STATION -- routes work like linked lists. -- keys are current positions, and -- values are the associated ajecent -- blocks to move to. this loop should -- be closed, and it should not be -- possible for a collision to occur -- between a turtle following the loop, -- and a turtle pairing, traveling to -- the mine entrance, or any other -- movement. main_loop_route = { -- MINING TURTLE HOME ENTER [c.x-1 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-2, y = c.y+1, z = c.z-1}, -- MINING TURTLE HOME EXIT [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-2, y = c.y+1, z = c.z+0}, -- CHUNKY TURTLE HOME EXIT [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+0] = {x = c.x-2, y = c.y+1, z = c.z+1}, -- CHUNKY TURTLE HOME ENTER [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x-2, y = c.y+1, z = c.z+2}, [c.x-2 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x-1, y = c.y+1, z = c.z+2}, [c.x-1 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x+0, y = c.y+1, z = c.z+2}, [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z+2] = {x = c.x+0, y = c.y+1, z = c.z+1}, [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+1, y = c.y+1, z = c.z+1}, -- ITEM DROP STATION [c.x+1 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+2, y = c.y+1, z = c.z+1}, -- REFUELING STATION [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z+1] = {x = c.x+2, y = c.y+1, z = c.z+0}, [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z+0] = {x = c.x+2, y = c.y+1, z = c.z-1}, [c.x+2 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x+1, y = c.y+1, z = c.z-1}, [c.x+1 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x+0, y = c.y+1, z = c.z-1}, [c.x+0 .. ',' .. c.y+1 .. ',' .. c.z-1] = {x = c.x-1, y = c.y+1, z = c.z-1}, }, } mining_turtle_locations = { -- LOCATIONS THAT ARE SPECIFIC TO -- MINING TURTLES -- TURTLE HOMES -- this is where the first turtle parking -- spot will be, and each following will -- be in the direction. homes = {x = c.x-3, y = c.y+0, z = c.z-3, increment = 'west'}, -- THE AREA ENCOMPASSING THE HOME -- LINE, AS WELL AS THE PATH TURTLES -- TAKE TO GET TO THEIR HOME home_area = { min_x = -inf, max_x = c.x-3, min_y = c.y+0, max_y = c.y+0, min_z = c.z-1, max_z = c.z-1 }, -- WHERE TURTLES ENTER THE LINE TO -- GET TO THEIR HOME home_enter = {x = c.x-2, y = c.y+1, z = c.z-1, orientation = 'west'}, -- WHERE TURTLES EXIT THEIR HOMES home_exit = {x = c.x-2, y = c.y+1, z = c.z+0}, -- WHERE TURTLES WAIT TO BE PAIRED waiting_room = {x = c.x-2, y = c.y+0, z = c.z+0}, -- THE PATH TURTLES WILL TAKE AFTER -- PAIRING waiting_room_to_mine_enter_route = { [c.x-2 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x-1, y = c.y+0, z = c.z+0}, [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x+0, y = c.y+0, z = c.z+0}, } } chunky_turtle_locations = { -- LOCATIONS THAT ARE SPECIFIC TO -- MINING TURTLES -- TURTLE HOMES -- this is where the first turtle parking -- spot will be, and each following will -- be in the direction. homes = {x = c.x-3, y = c.y+0, z = c.z+2, increment = 'west'}, -- THE AREA ENCOMPASSING THE HOME -- LINE, AS WELL AS THE PATH TURTLES -- TAKE TO GET TO THEIR HOME home_area = { min_x = -inf, max_x = c.x-3, min_y = c.y+0, max_y = c.y+0, min_z = c.z+2, max_z = c.z+2 }, -- WHERE TURTLES ENTER THE LINE TO -- GET TO THEIR HOME home_enter = {x = c.x-2, y = c.y+1, z = c.z+2, orientation = 'west'}, -- WHERE TURTLES EXIT THEIR HOMES home_exit = {x = c.x-2, y = c.y+1, z = c.z+1}, -- WHERE TURTLES WAIT TO BE PAIRED waiting_room = {x = c.x-2, y = c.y+0, z = c.z+1}, -- THE PATH TURTLES WILL TAKE AFTER -- PAIRING waiting_room_to_mine_enter_route = { [c.x-2 .. ',' .. c.y+0 .. ',' .. c.z+1] = {x = c.x-1, y = c.y+0, z = c.z+1}, [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+1] = {x = c.x-1, y = c.y+0, z = c.z+0}, [c.x-1 .. ',' .. c.y+0 .. ',' .. c.z+0] = {x = c.x+0, y = c.y+0, z = c.z+0}, } } gravitynames = { -- ALL BLOCKS AFFECTED BY GRAVITY -- if a turtle sees these it will take -- extra care to make sure they're delt -- with. works at least a lot percent of -- the time ['minecraft:gravel'] = true, ['minecraft:sand'] = true, } orenames = { -- ALL THE BLOCKS A TURTLE CONSIDERS ORE -- a turtle will continue to mine out a -- vein until it reaches or -- it stops seeing blocks with names in -- this list. block names are exact. ['BigReactors:YelloriteOre'] = true, ['bigreactors:oreyellorite'] = true, ['ThermalFoundation:Ore'] = true, ['thermalfoundation:ore'] = true, ['thermalfoundation:ore_fluid'] = true, ['thaumcraft:ore_amber'] = true, ['minecraft:coal'] = true, ['minecraft:coal_ore'] = true, ['minecraft:diamond'] = true, ['minecraft:diamond_ore'] = true, ['minecraft:dye'] = true, ['minecraft:emerald'] = true, ['minecraft:emerald_ore'] = true, ['minecraft:gold_ore'] = true, ['minecraft:iron_ore'] = true, ['minecraft:lapis_ore'] = true, ['minecraft:redstone'] = true, ['minecraft:redstone_ore'] = true, ['galacticraftcore:basic_block_core'] = true, ['mekanism:oreblock'] = true, ['appliedenergistics2:quartz_ore'] = true, ['indrev:copper_ore'] = true, ['indrev:tin_ore'] = true, ['indrev:lead_ore'] = true, ['indrev:silver_ore'] = true, ['indrev:tungsten_ore'] = true, ['modern_industrialization:copper_ore'] = true, ['modern_industrialization:tin_ore'] = true, ['modern_industrialization:lead_ore'] = true, ['modern_industrialization:silver_ore'] = true, ['modern_industrialization:nickel_ore'] = true, ['techreborn:bauxite_ore'] = true, ['techreborn:copper_ore'] = true, ['techreborn:galena_ore'] = true, ['techreborn:iridium_ore'] = true, ['techreborn:lead_ore'] = true, ['techreborn:ruby_ore'] = true, ['techreborn:sapphire_ore'] = true, ['techreborn:silver_ore'] = true, ['techreborn:tin_ore'] = true, ['powah:uraninite_ore'] = true, ['create:zinc_ore'] = true, ['minecraft:deepslate_coal_ore'] = true, ['minecraft:deepslate_copper_ore'] = true, ['minecraft:deepslate_diamond_ore'] = true, ['minecraft:deepslate_emerald_ore'] = true, ['minecraft:deepslate_gold_ore'] = true, ['minecraft:deepslate_iron_ore'] = true, ['minecraft:deepslate_lapis_ore'] = true, ['minecraft:deepslate_redstone_ore'] = true } blocktags = { -- ALL BLOCKS WITH ONE OF THESE TAGS A TURTLE CONSIDERS ORE -- most mods categorize ores with the forge:ores tag. -- this is an easy way to detect all but a few ores, -- which don't posess this exact tag (for example certus quartzfrom AE2) ['forge:ores'] = true, -- adds Certus Quartz and Charged Certus Quartz ['forge:ores/certus_quartz'] = true } fuelnames = { -- ITEMS THE TURTLE CONSIDERS FUEL ['minecraft:coal'] = true, } ---==[ SCREEN ]==--- -- MAXIMUM ZOOM OUT (INVERSE) OF THE -- MAP SCREEN monitor_max_zoom_level = 5 -- DEFAULT ZOOM OF THE MAP SCREEN -- 0 is [1 pixel : 1 block] default_monitor_zoom_level = 0 -- CENTER OF THE MAP SCREEN -- probably want the mine center default_monitor_location = {x = c.x, z = c.z}]===], ["hub_files/events.lua"] = [===[while true do event = {os.pullEvent()} if event[1] == 'rednet_message' then local sender = event[2] local message = event[3] local protocol = event[4] if protocol == 'user_input' then table.insert(state.user_input, message) elseif protocol == 'turtle_report' then if not state.turtles[sender] then state.turtles[sender] = {id = sender} end state.turtles[sender].data = message state.turtles[sender].last_update = os.clock() elseif protocol == 'pocket_report' then if not state.pockets[sender] then state.pockets[sender] = {id = sender} end state.pockets[sender].data = message state.pockets[sender].last_update = os.clock() elseif protocol == 'update_request' then if fs.isDir(message) then local update_package = {} local queue = {''} while #queue > 0 do dir_name = table.remove(queue) path_name = fs.combine(message, dir_name) for _, object_name in pairs(fs.list(path_name)) do sub_dir_name = fs.combine(dir_name, object_name) sub_path_name = fs.combine(message, sub_dir_name) if fs.isDir(sub_path_name) then table.insert(queue, sub_dir_name) else local file = fs.open(sub_path_name, 'r') update_package[sub_dir_name] = file.readAll() file.close() end end end update_package.hub_id = os.getComputerID() rednet.send(sender, update_package, 'update_package') end end elseif event[1] == 'monitor_touch' then if state.monitor_touches then table.insert(state.monitor_touches, {x = event[3], y = event[4]}) end end end]===], ["hub_files/monitor.lua"] = [===[menu_lines = { '# # ##### # # #####', '## ## # ## # #', '# # # # # # # ###', '# # # # ## #', '# # ##### # # #', } decimals = { [0] = { '#####', '# #', '# #', '# #', '#####', }, [1] = { '### ', ' # ', ' # ', ' # ', '#####', }, [2] = { '#####', ' #', '#####', '# ', '#####', }, [3] = { '#####', ' #', '#####', ' #', '#####', }, [4] = { '# #', '# #', '#####', ' #', ' #', }, [5] = { '#####', '# ', '#####', ' #', '#####', }, [6] = { '#####', '# ', '#####', '# #', '#####', }, [7] = { '#####', ' #', ' #', ' #', ' #', }, [8] = { '#####', '# #', '#####', '# #', '#####', }, [9] = { '#####', '# #', '#####', ' #', ' #', }, } function debug_print(string) term.redirect(monitor.restore_to) print(string) term.redirect(monitor) end function turtle_viewer(turtle_ids) term.redirect(monitor) local selected = 1 while true do local turtle_id = turtle_ids[selected] local turtle = state.turtles[turtle_id] -- RESOLVE MONITOR TOUCHES, EITHER BY AFFECTING THE DISPLAY OR INSERTING INTO USER_INPUT TABLE while #state.monitor_touches > 0 do local monitor_touch = table.remove(state.monitor_touches) if monitor_touch.x == elements.left.x and monitor_touch.y == elements.left.y then selected = math.max(selected - 1, 1) elseif monitor_touch.x == elements.right.x and monitor_touch.y == elements.right.y then selected = math.min(selected + 1, #turtle_ids) elseif monitor_touch.x == elements.viewer_exit.x and monitor_touch.y == elements.viewer_exit.y then term.redirect(monitor.restore_to) return elseif monitor_touch.x == elements.turtle_return.x and monitor_touch.y == elements.turtle_return.y then table.insert(state.user_input, 'return ' .. turtle_id) elseif monitor_touch.x == elements.turtle_update.x and monitor_touch.y == elements.turtle_update.y then table.insert(state.user_input, 'update ' .. turtle_id) elseif monitor_touch.x == elements.turtle_reboot.x and monitor_touch.y == elements.turtle_reboot.y then table.insert(state.user_input, 'reboot ' .. turtle_id) elseif monitor_touch.x == elements.turtle_halt.x and monitor_touch.y == elements.turtle_halt.y then table.insert(state.user_input, 'halt ' .. turtle_id) elseif monitor_touch.x == elements.turtle_clear.x and monitor_touch.y == elements.turtle_clear.y then table.insert(state.user_input, 'clear ' .. turtle_id) elseif monitor_touch.x == elements.turtle_reset.x and monitor_touch.y == elements.turtle_reset.y then table.insert(state.user_input, 'reset ' .. turtle_id) elseif monitor_touch.x == elements.turtle_find.x and monitor_touch.y == elements.turtle_find.y then monitor_location.x = turtle.data.location.x monitor_location.z = turtle.data.location.z monitor_zoom_level = 0 for level_index, level_and_chance in pairs(config.mine_levels) do if turtle.strip and level_and_chance.level == turtle.strip.y then monitor_level_index = level_index select_mine_level() break end end term.redirect(monitor.restore_to) return elseif monitor_touch.x == elements.turtle_forward.x and monitor_touch.y == elements.turtle_forward.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go forward') elseif monitor_touch.x == elements.turtle_back.x and monitor_touch.y == elements.turtle_back.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go back') elseif monitor_touch.x == elements.turtle_up.x and monitor_touch.y == elements.turtle_up.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go up') elseif monitor_touch.x == elements.turtle_down.x and monitor_touch.y == elements.turtle_down.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go down') elseif monitor_touch.x == elements.turtle_left.x and monitor_touch.y == elements.turtle_left.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go left') elseif monitor_touch.x == elements.turtle_right.x and monitor_touch.y == elements.turtle_right.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' go right') elseif turtle.data.turtle_type == 'mining' then if monitor_touch.x == elements.turtle_dig_up.x and monitor_touch.y == elements.turtle_dig_up.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock up') elseif monitor_touch.x == elements.turtle_dig.x and monitor_touch.y == elements.turtle_dig.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock forward') elseif monitor_touch.x == elements.turtle_dig_down.x and monitor_touch.y == elements.turtle_dig_down.y then table.insert(state.user_input, 'turtle ' .. turtle_id .. ' digblock down') end end end turtle_id = turtle_ids[selected] turtle = state.turtles[turtle_id] background_color = colors.black term.setBackgroundColor(background_color) monitor.clear() if turtle.last_update + config.turtle_timeout < os.clock() then term.setCursorPos(elements.turtle_lost.x, elements.turtle_lost.y) term.setTextColor(colors.red) term.write('CONNECTION LOST') end local x_position = elements.turtle_id.x for decimal_string in string.format('%04d', turtle_id):gmatch"." do for y_offset, line in pairs(decimals[tonumber(decimal_string)]) do term.setCursorPos(x_position, elements.turtle_id.y + y_offset - 1) for char in line:gmatch"." do if char == '#' then term.setBackgroundColor(colors.green) else term.setBackgroundColor(colors.black) end term.write(' ') end end x_position = x_position + 6 end term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y) term.setBackgroundColor(colors.yellow) term.write(' ') term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 1) term.setBackgroundColor(colors.yellow) term.write(' ') term.setBackgroundColor(colors.gray) term.write(' ') term.setBackgroundColor(colors.yellow) term.write(' ') term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 2) term.setBackgroundColor(colors.yellow) term.write(' ') term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 3) term.setBackgroundColor(colors.yellow) term.write(' ') term.setCursorPos(elements.turtle_face.x + 1, elements.turtle_face.y + 4) term.setBackgroundColor(colors.yellow) term.write(' ') if turtle.data.peripheral_right == 'modem' then term.setBackgroundColor(colors.lightGray) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2) term.write(' ') term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3) term.write(' ') elseif turtle.data.peripheral_right == 'pick' then term.setBackgroundColor(colors.cyan) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2) term.write(' ') term.setBackgroundColor(colors.brown) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3) term.write(' ') elseif turtle.data.peripheral_right == 'chunkLoader' then term.setBackgroundColor(colors.gray) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3) term.write(' ') term.setBackgroundColor(colors.blue) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2) term.write(' ') elseif turtle.data.peripheral_right == 'chunky' then term.setBackgroundColor(colors.white) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 3) term.write(' ') term.setBackgroundColor(colors.red) term.setCursorPos(elements.turtle_face.x, elements.turtle_face.y + 2) term.write(' ') end if turtle.data.peripheral_left == 'modem' then term.setBackgroundColor(colors.lightGray) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2) term.write(' ') term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3) term.write(' ') elseif turtle.data.peripheral_left == 'pick' then term.setBackgroundColor(colors.cyan) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2) term.write(' ') term.setBackgroundColor(colors.brown) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3) term.write(' ') elseif turtle.data.peripheral_left == 'chunkLoader' then term.setBackgroundColor(colors.gray) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3) term.write(' ') term.setBackgroundColor(colors.blue) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2) term.write(' ') elseif turtle.data.peripheral_left == 'chunky' then term.setBackgroundColor(colors.white) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 1) term.write(' ') term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 3) term.write(' ') term.setBackgroundColor(colors.red) term.setCursorPos(elements.turtle_face.x + 8, elements.turtle_face.y + 2) term.write(' ') end term.setBackgroundColor(background_color) term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y) term.setTextColor(colors.white) term.write('State: ') term.setTextColor(colors.green) term.write(turtle.state) term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 1) term.setTextColor(colors.white) term.write('X: ') term.setTextColor(colors.green) if turtle.data.location then term.write(turtle.data.location.x) end term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 2) term.setTextColor(colors.white) term.write('Y: ') term.setTextColor(colors.green) if turtle.data.location then term.write(turtle.data.location.y) end term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 3) term.setTextColor(colors.white) term.write('Z: ') term.setTextColor(colors.green) if turtle.data.location then term.write(turtle.data.location.z) end term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 4) term.setTextColor(colors.white) term.write('Facing: ') term.setTextColor(colors.green) term.write(turtle.data.orientation) term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 5) term.setTextColor(colors.white) term.write('Fuel: ') term.setTextColor(colors.green) term.write(turtle.data.fuel_level) term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 6) term.setTextColor(colors.white) term.write('Items: ') term.setTextColor(colors.green) term.write(turtle.data.item_count) -- term.setCursorPos(elements.turtle_data.x, elements.turtle_data.y + 7) -- term.setTextColor(colors.white) -- term.write('Dist: ') -- term.setTextColor(colors.green) -- term.write(turtle.data.distance) term.setTextColor(colors.white) term.setCursorPos(elements.turtle_return.x, elements.turtle_return.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-RETURN') term.setCursorPos(elements.turtle_update.x, elements.turtle_update.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-UPDATE') term.setCursorPos(elements.turtle_reboot.x, elements.turtle_reboot.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-REBOOT') term.setCursorPos(elements.turtle_halt.x, elements.turtle_halt.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-HALT') term.setCursorPos(elements.turtle_clear.x, elements.turtle_clear.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-CLEAR') term.setCursorPos(elements.turtle_reset.x, elements.turtle_reset.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-RESET') term.setCursorPos(elements.turtle_find.x, elements.turtle_find.y) term.setBackgroundColor(colors.green) term.write('*') term.setBackgroundColor(colors.brown) term.write('-FIND') term.setCursorPos(elements.turtle_forward.x, elements.turtle_forward.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('^') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-FORWARD') term.setCursorPos(elements.turtle_back.x, elements.turtle_back.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('V') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-BACK') term.setCursorPos(elements.turtle_up.x, elements.turtle_up.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('^') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-UP') term.setCursorPos(elements.turtle_down.x, elements.turtle_down.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('V') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-DOWN') term.setCursorPos(elements.turtle_left.x, elements.turtle_left.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('<') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-LEFT') term.setCursorPos(elements.turtle_right.x, elements.turtle_right.y) term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.write('>') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-RIGHT') term.setCursorPos(elements.turtle_dig_up.x, elements.turtle_dig_up.y) term.setTextColor(colors.white) if turtle.data.turtle_type == 'mining' then term.setBackgroundColor(colors.green) else term.setBackgroundColor(colors.gray) end term.write('^') term.setCursorPos(elements.turtle_dig.x, elements.turtle_dig.y) term.setTextColor(colors.white) if turtle.data.turtle_type == 'mining' then term.setBackgroundColor(colors.green) else term.setBackgroundColor(colors.gray) end term.write('*') term.setTextColor(colors.gray) term.setBackgroundColor(background_color) term.write('-DIG') term.setCursorPos(elements.turtle_dig_down.x, elements.turtle_dig_down.y) term.setTextColor(colors.white) if turtle.data.turtle_type == 'mining' then term.setBackgroundColor(colors.green) else term.setBackgroundColor(colors.gray) end term.write('v') term.setTextColor(colors.white) if selected == 1 then term.setBackgroundColor(colors.gray) else term.setBackgroundColor(colors.green) end term.setCursorPos(elements.left.x, elements.left.y) term.write('<') if selected == #turtle_ids then term.setBackgroundColor(colors.gray) else term.setBackgroundColor(colors.green) end term.setCursorPos(elements.right.x, elements.right.y) term.write('>') term.setBackgroundColor(colors.red) term.setCursorPos(elements.viewer_exit.x, elements.viewer_exit.y) term.write('x') monitor.setVisible(true) monitor.setVisible(false) sleep(sleep_len) end end function menu() term.redirect(monitor) while true do while #state.monitor_touches > 0 do local monitor_touch = table.remove(state.monitor_touches) if monitor_touch.x == elements.viewer_exit.x and monitor_touch.y == elements.viewer_exit.y then term.redirect(monitor.restore_to) return elseif monitor_touch.x == elements.menu_toggle.x and monitor_touch.y == elements.menu_toggle.y then if state.on then table.insert(state.user_input, 'off') else table.insert(state.user_input, 'on') end elseif monitor_touch.x == elements.menu_update.x and monitor_touch.y == elements.menu_update.y then table.insert(state.user_input, 'update') elseif monitor_touch.x == elements.menu_return.x and monitor_touch.y == elements.menu_return.y then table.insert(state.user_input, 'return') elseif monitor_touch.x == elements.menu_reboot.x and monitor_touch.y == elements.menu_reboot.y then table.insert(state.user_input, 'reboot') elseif monitor_touch.x == elements.menu_halt.x and monitor_touch.y == elements.menu_halt.y then table.insert(state.user_input, 'halt') elseif monitor_touch.x == elements.menu_clear.x and monitor_touch.y == elements.menu_clear.y then table.insert(state.user_input, 'clear') elseif monitor_touch.x == elements.menu_reset.x and monitor_touch.y == elements.menu_reset.y then table.insert(state.user_input, 'reset') end end term.setBackgroundColor(colors.black) monitor.clear() term.setTextColor(colors.white) term.setCursorPos(elements.menu_title.x, elements.menu_title.y) term.write('MASTER') for y_offset, line in pairs(menu_lines) do term.setCursorPos(elements.menu_title.x, elements.menu_title.y + y_offset) for char in line:gmatch"." do if char == '#' then if state.on then term.setBackgroundColor(colors.lime) else term.setBackgroundColor(colors.red) end else term.setBackgroundColor(colors.black) end term.write(' ') end end term.write('.lua') term.setBackgroundColor(colors.red) term.setCursorPos(elements.viewer_exit.x, elements.viewer_exit.y) term.write('x') term.setBackgroundColor(colors.green) term.setCursorPos(elements.menu_toggle.x, elements.menu_toggle.y) term.write('*') term.setCursorPos(elements.menu_return.x, elements.menu_return.y) term.write('*') term.setCursorPos(elements.menu_update.x, elements.menu_update.y) term.write('*') term.setCursorPos(elements.menu_reboot.x, elements.menu_reboot.y) term.write('*') term.setCursorPos(elements.menu_halt.x, elements.menu_halt.y) term.write('*') term.setCursorPos(elements.menu_clear.x, elements.menu_clear.y) term.write('*') term.setCursorPos(elements.menu_reset.x, elements.menu_reset.y) term.write('*') term.setBackgroundColor(colors.brown) term.setCursorPos(elements.menu_toggle.x + 1, elements.menu_toggle.y) term.write('-TOGGLE POWER') term.setCursorPos(elements.menu_update.x + 1, elements.menu_update.y) term.write('-UPDATE') term.setCursorPos(elements.menu_return.x + 1, elements.menu_return.y) term.write('-RETURN') term.setCursorPos(elements.menu_reboot.x + 1, elements.menu_reboot.y) term.write('-REBOOT') term.setCursorPos(elements.menu_halt.x + 1, elements.menu_halt.y) term.write('-HALT') term.setCursorPos(elements.menu_clear.x + 1, elements.menu_clear.y) term.write('-CLEAR') term.setCursorPos(elements.menu_reset.x + 1, elements.menu_reset.y) term.write('-RESET') monitor.setVisible(true) monitor.setVisible(false) sleep(sleep_len) end end function draw_location(location, color) if location then local pixel = { -- x = monitor_width - math.floor((location.x - min_location.x) / zoom_factor), -- y = monitor_height - math.floor((location.z - min_location.z) / zoom_factor), x = math.floor((location.x - min_location.x) / zoom_factor), y = math.floor((location.z - min_location.z) / zoom_factor), } if pixel.x >= 1 and pixel.x <= monitor_width and pixel.y >= 1 and pixel.y <= monitor_height then if color then paintutils.drawPixel(pixel.x, pixel.y, color) end return pixel end end end function draw_monitor() term.redirect(monitor) term.setBackgroundColor(colors.black) monitor.clear() zoom_factor = math.pow(2, monitor_zoom_level) min_location = { x = monitor_location.x - math.floor(monitor_width * zoom_factor / 2) - 1, z = monitor_location.z - math.floor(monitor_height * zoom_factor / 2) - 1, } local mined = {} local xz 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 for z = min_location.z, min_location.z + (monitor_height * zoom_factor), zoom_factor do xz = x .. ',' .. z if not mined[xz] then if z > config.locations.mine_enter.z then if monitor_level[x] and monitor_level[x].south.z > z then mined[xz] = true draw_location({x = x, z = z}, colors.lightGray) else draw_location({x = x, z = z}, colors.gray) end else if monitor_level[x] and monitor_level[x].north.z < z then mined[xz] = true draw_location({x = x, z = z}, colors.lightGray) else draw_location({x = x, z = z}, colors.gray) end end end end end for x = min_location.x, min_location.x + (monitor_width * zoom_factor), zoom_factor do if x > monitor_level.main_shaft.west.x and x < monitor_level.main_shaft.east.x then draw_location({x = x, z = config.locations.mine_enter.z}, colors.lightGray) else draw_location({x = x, z = config.locations.mine_enter.z}, colors.gray) end end local pixel local special = {} pixel = draw_location(config.locations.mine_exit, colors.blue) if pixel then special[pixel.x .. ',' .. pixel.y] = colors.blue end pixel = draw_location(config.locations.mine_enter, colors.blue) if pixel then special[pixel.x .. ',' .. pixel.y] = colors.blue end -- DRAW STRIP ENDINGS for name, strip in pairs(monitor_level) do if name ~= 'y' then for _, strip_end in pairs(strip) do if strip_end.turtles then pixel = draw_location(strip_end, colors.green) if pixel then special[pixel.x .. ',' .. pixel.y] = colors.green end end end end end term.setTextColor(colors.black) turtles = {} local str_pixel for _, turtle in pairs(state.turtles) do if turtle.data then local location = turtle.data.location if location and location.x and location.y then pixel = draw_location(location) if pixel then term.setCursorPos(pixel.x, pixel.y) str_pixel = pixel.x .. ',' .. pixel.y if special[str_pixel] then term.setBackgroundColor(special[str_pixel]) elseif turtle.last_update + config.turtle_timeout < os.clock() then term.setBackgroundColor(colors.red) else term.setBackgroundColor(colors.yellow) end if not turtles[str_pixel] then turtles[str_pixel] = {turtle.id} term.write('-') else table.insert(turtles[str_pixel], turtle.id) if #turtles[str_pixel] <= 9 then term.write(#turtles[str_pixel]) else term.write('+') end end end end end end for _, pocket in pairs(state.pockets) do local location = pocket.data.location if location and location.x and location.y then pixel = draw_location(location) if pixel then term.setCursorPos(pixel.x, pixel.y) str_pixel = pixel.x .. ',' .. pixel.y if pocket.last_update + config.pocket_timeout < os.clock() then term.setBackgroundColor(colors.red) else term.setBackgroundColor(colors.green) end term.write('M') end end end term.setTextColor(colors.white) term.setBackgroundColor(colors.green) term.setCursorPos(elements.menu.x, elements.menu.y) term.write('*') term.setCursorPos(elements.all_turtles.x, elements.all_turtles.y) term.write('*') term.setCursorPos(elements.mining_turtles.x, elements.mining_turtles.y) term.write('*') term.setCursorPos(elements.center.x, elements.center.y) term.write('*') term.setCursorPos(elements.up.x, elements.up.y) term.write('N') term.setCursorPos(elements.down.x, elements.down.y) term.write('S') term.setCursorPos(elements.left.x, elements.left.y) term.write('W') term.setCursorPos(elements.right.x, elements.right.y) term.write('E') term.setCursorPos(elements.level_up.x, elements.level_up.y) term.write('+') term.setCursorPos(elements.level_down.x, elements.level_down.y) term.write('-') term.setCursorPos(elements.zoom_in.x, elements.zoom_in.y) term.write('+') term.setCursorPos(elements.zoom_out.x, elements.zoom_out.y) term.write('-') term.setBackgroundColor(colors.brown) term.setCursorPos(elements.level_indicator.x, elements.level_indicator.y) term.write(string.format('LEVEL: %3d', monitor_level.y)) term.setCursorPos(elements.zoom_indicator.x, elements.zoom_indicator.y) term.write('ZOOM: ' .. monitor_zoom_level) term.setCursorPos(elements.x_indicator.x, elements.x_indicator.y) term.write('X: ' .. monitor_location.x) term.setCursorPos(elements.z_indicator.x, elements.z_indicator.y) term.write('Z: ' .. monitor_location.z) term.setCursorPos(elements.center_indicator.x, elements.center_indicator.y) term.write('-CENTER') term.setCursorPos(elements.menu_indicator.x, elements.menu_indicator.y) term.write('-MENU') term.setCursorPos(elements.all_indicator.x, elements.all_indicator.y) term.write('ALL-') term.setCursorPos(elements.mining_indicator.x, elements.mining_indicator.y) term.write('MINING-') term.redirect(monitor.restore_to) end function touch_monitor(monitor_touch) if monitor_touch.x == elements.up.x and monitor_touch.y == elements.up.y then monitor_location.z = monitor_location.z - zoom_factor elseif monitor_touch.x == elements.down.x and monitor_touch.y == elements.down.y then monitor_location.z = monitor_location.z + zoom_factor elseif monitor_touch.x == elements.left.x and monitor_touch.y == elements.left.y then monitor_location.x = monitor_location.x - zoom_factor elseif monitor_touch.x == elements.right.x and monitor_touch.y == elements.right.y then monitor_location.x = monitor_location.x + zoom_factor elseif monitor_touch.x == elements.level_up.x and monitor_touch.y == elements.level_up.y then monitor_level_index = math.min(monitor_level_index + 1, #config.mine_levels) select_mine_level() elseif monitor_touch.x == elements.level_down.x and monitor_touch.y == elements.level_down.y then monitor_level_index = math.max(monitor_level_index - 1, 1) select_mine_level() elseif monitor_touch.x == elements.zoom_in.x and monitor_touch.y == elements.zoom_in.y then monitor_zoom_level = math.max(monitor_zoom_level - 1, 0) elseif monitor_touch.x == elements.zoom_out.x and monitor_touch.y == elements.zoom_out.y then monitor_zoom_level = math.min(monitor_zoom_level + 1, config.monitor_max_zoom_level) elseif monitor_touch.x == elements.menu.x and monitor_touch.y == elements.menu.y then menu() elseif monitor_touch.x == elements.center.x and monitor_touch.y == elements.center.y then monitor_location = {x = config.default_monitor_location.x, z = config.default_monitor_location.z} elseif monitor_touch.x == elements.all_turtles.x and monitor_touch.y == elements.all_turtles.y then local turtle_ids = {} for _, turtle in pairs(state.turtles) do if turtle.data then table.insert(turtle_ids, turtle.id) end end if #turtle_ids then turtle_viewer(turtle_ids) end elseif monitor_touch.x == elements.mining_turtles.x and monitor_touch.y == elements.mining_turtles.y then local turtle_ids = {} for _, turtle in pairs(state.turtles) do if turtle.data and turtle.data.turtle_type == 'mining' then table.insert(turtle_ids, turtle.id) end end if #turtle_ids then turtle_viewer(turtle_ids) end else local str_pos = monitor_touch.x .. ',' .. monitor_touch.y if turtles[str_pos] then turtle_viewer(turtles[str_pos]) end end end function init_elements() elements = { up = {x = math.ceil(monitor_width / 2), y = 1 }, down = {x = math.ceil(monitor_width / 2), y = monitor_height }, left = {x = 1, y = math.ceil(monitor_height / 2)}, right = {x = monitor_width, y = math.ceil(monitor_height / 2)}, level_up = {x = monitor_width, y = 1}, level_down = {x = monitor_width - 11, y = 1}, level_indicator = {x = monitor_width - 10, y = 1}, zoom_in = {x = monitor_width, y = 2}, zoom_out = {x = monitor_width - 8, y = 2}, zoom_indicator = {x = monitor_width - 7, y = 2}, all_turtles = {x = monitor_width, y = monitor_height-1}, all_indicator = {x = monitor_width - 4, y = monitor_height-1}, mining_turtles = {x = monitor_width, y = monitor_height}, mining_indicator = {x = monitor_width - 7, y = monitor_height}, menu = {x = 1, y = monitor_height}, menu_indicator = {x = 2, y = monitor_height}, center = {x = 1, y = 1}, center_indicator = {x = 2, y = 1}, x_indicator = {x = 1, y = 2}, z_indicator = {x = 1, y = 3}, viewer_exit = {x = 1, y = 1}, turtle_face = {x = 5, y = 2}, turtle_id = {x = 16, y = 2}, turtle_lost = {x = 13, y = 1}, turtle_data = {x = 4, y = 8}, turtle_return = {x = 26, y = 8}, turtle_update = {x = 26, y = 9}, turtle_reboot = {x = 26, y = 10}, turtle_halt = {x = 26, y = 11}, turtle_clear = {x = 26, y = 12}, turtle_reset = {x = 26, y = 13}, turtle_find = {x = 26, y = 14}, turtle_forward = {x = 10, y = 16}, turtle_back = {x = 10, y = 18}, turtle_up = {x = 23, y = 16}, turtle_down = {x = 23, y = 18}, turtle_left = {x = 6, y = 17}, turtle_right = {x = 14, y = 17}, turtle_dig_up = {x = 31, y = 16}, turtle_dig = {x = 31, y = 17}, turtle_dig_down = {x = 31, y = 18}, menu_title = {x = 9, y = 3}, menu_toggle = {x = 10, y = 11}, menu_update = {x = 10, y = 13}, menu_return = {x = 10, y = 14}, menu_reboot = {x = 10, y = 15}, menu_halt = {x = 10, y = 16}, menu_clear = {x = 10, y = 17}, menu_reset = {x = 10, y = 18}, } end function select_mine_level() monitor_level = state.mine[config.mine_levels[monitor_level_index].level] end function step() while #state.monitor_touches > 0 do touch_monitor(table.remove(state.monitor_touches)) end draw_monitor() monitor.setVisible(true) monitor.setVisible(false) sleep(sleep_len) end function main() sleep_len = 0.3 local attached = peripheral.find('monitor') if not attached then error('No monitor connected.') end monitor_size = {attached.getSize()} monitor_width = monitor_size[1] monitor_height = monitor_size[2] if monitor_width < 29 or monitor_height < 12 then -- Must be at least that big return end monitor = window.create(attached, 1, 1, monitor_width, monitor_height) monitor.restore_to = term.current() monitor.clear() monitor.setVisible(false) monitor.setCursorPos(1, 1) monitor_location = {x = config.locations.mine_enter.x, z = config.locations.mine_enter.z} monitor_zoom_level = config.default_monitor_zoom_level init_elements() while not state.mine do sleep(0.5) end monitor_level_index = 1 select_mine_level() state.monitor_touches = {} while true do local status, caught_error = pcall(step) if not status then term.redirect(monitor.restore_to) error(caught_error) end end end main()]===], ["hub_files/report.lua"] = [===[-- CONTINUOUSLY BROADCAST STATUS REPORTS while true do turtles_parked = 0 turtle_count = 0 for _, turtle in pairs(state.turtles) do if turtle.state == 'park' then turtles_parked = turtles_parked + 1 end turtle_count = turtle_count + 1 end rednet.broadcast({ on = state.on, turtles_parked = turtles_parked, turtle_count = turtle_count, }, 'hub_report') sleep(0.5) end]===], ["hub_files/session_id"] = [===[29.0]===], ["hub_files/startup.lua"] = [===[-- SET LABEL os.setComputerLabel('Hub') -- INITIALIZE APIS if fs.exists('/apis') then fs.delete('/apis') end fs.makeDir('/apis') fs.copy('/config.lua', '/apis/config') fs.copy('/state.lua', '/apis/state') fs.copy('/basics.lua', '/apis/basics') os.loadAPI('/apis/config') os.loadAPI('/apis/state') os.loadAPI('/apis/basics') -- OPEN REDNET for _, side in pairs({'back', 'top', 'left', 'right'}) do if peripheral.getType(side) == 'modem' then rednet.open(side) break end end -- IF UPDATED PRINT "UPDATED" if fs.exists('/updated') then fs.delete('/updated') print('UPDATED') state.updated = true end -- LAUNCH PROGRAMS AS SEPARATE THREADS multishell.launch({}, '/user.lua') multishell.launch({}, '/report.lua') multishell.launch({}, '/monitor.lua') multishell.launch({}, '/events.lua') multishell.launch({}, '/whosmineisitanyway.lua') multishell.setTitle(2, 'user') multishell.setTitle(3, 'report') multishell.setTitle(4, 'monitor') multishell.setTitle(5, 'events') multishell.setTitle(6, 'whosmine')]===], ["hub_files/state.lua"] = [===[user_input = {} turtles = {} pockets = {} homes = {}]===], ["hub_files/update"] = [===[os.run({}, '/disk/hub.lua')]===], ["hub_files/updated"] = [===[]===], ["hub_files/user.lua"] = [===[-- CONTINUOUSLY AWAIT USER INPUT AND PLACE IN TABLE while true do table.insert(state.user_input, read()) end]===], ["hub_files/whosmineisitanyway.lua"] = [===[inf = basics.inf str_xyz = basics.str_xyz reverse_shift = { north = 'south', south = 'north', east = 'west', west = 'east', } function load_mine() -- LOAD MINE INTO state.mine FROM /mine// DIRECTORY state.mine_dir_path = '/mine/' .. config.locations.mine_enter.x .. ',' .. config.locations.mine_enter.z .. '/' state.mine = {} if not fs.exists(state.mine_dir_path) then fs.makeDir(state.mine_dir_path) end if fs.exists(state.mine_dir_path .. 'on') then state.on = true end for _, level_and_chance in pairs(config.mine_levels) do local level = level_and_chance.level state.mine[level] = {y = level} -- START WITH AT LEAST A MAIN SHAFT state.mine[level].main_shaft = {} 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'} 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'} -- FOR EACH STRIP IN /mine/// PUT INTO MEMORY local level_dir_path = state.mine_dir_path .. level .. '/' if not fs.exists(level_dir_path) then fs.makeDir(level_dir_path) else for _, file_name in pairs(fs.list(level_dir_path)) do if file_name:sub(1, 1) ~= '.' then local file = fs.open(level_dir_path .. file_name, 'r') if file == nil then error('Failed to open file ' .. level_dir_path .. file_name) else if file_name == 'main_shaft' then local xs = string.gmatch(file.readAll(), '[^,]+') state.mine[level].main_shaft = {} state.mine[level].main_shaft.west = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'west'} state.mine[level].main_shaft.east = {name = 'main_shaft', x = tonumber(xs()), y = level, z = config.locations.mine_enter.z, orientation = 'east'} else local zs = string.gmatch(file.readAll(), '[^,]+') local x = tonumber(file_name) state.mine[level][x] = {} state.mine[level][x].north = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'north'} state.mine[level][x].south = {name = x, x = x, y = level, z = tonumber(zs()), orientation = 'south'} end file.close() end end end end end state.turtles_dir_path = state.mine_dir_path .. 'turtles/' if not fs.exists(state.turtles_dir_path) then fs.makeDir(state.turtles_dir_path) end local turtle_pairs = {} for _, turtle_id in pairs(fs.list(state.turtles_dir_path)) do if turtle_id:sub(1, 1) ~= '.' then turtle_id = tonumber(turtle_id) local turtle = {id = turtle_id} state.turtles[turtle_id] = turtle local turtle_dir_path = state.turtles_dir_path .. turtle_id .. '/' if fs.exists(turtle_dir_path .. 'strip') then local file = fs.open(turtle_dir_path .. 'strip', 'r') if file == nil then error('Failed to open file ' .. turtle_dir_path .. 'strip') end local strip_args = string.gmatch(file.readAll(), '[^,]+') local level = tonumber(strip_args()) local name = strip_args() if name ~= 'main_shaft' then name = tonumber(name) end local orientation = strip_args() if state.mine[level] and state.mine[level][name] and state.mine[level][name][orientation] then turtle.strip = state.mine[level][name][orientation] if fs.exists(turtle_dir_path .. 'deployed') then file = fs.open(turtle_dir_path .. 'deployed', 'r') if file == nil then error('Failed to open file ' .. turtle_dir_path .. 'deployed') end turtle.steps_left = tonumber(file.readAll()) if not turtle_pairs[turtle.strip] then turtle_pairs[turtle.strip] = {} end table.insert(turtle_pairs[turtle.strip], turtle) end end end if fs.exists(turtle_dir_path .. 'halt') then turtle.state = 'halt' end end end for strip, turtles in pairs(turtle_pairs) do if #turtles == 2 then strip.turtles = turtles turtles[1].pair = turtles[2] turtles[2].pair = turtles[1] elseif #turtles == 1 and not config.use_chunky_turtles then strip.turtles = turtles end end end function write_strip(level, name) -- RECORD THE STATE OF A STRIP AT A GIVEN level AND name TO /mine/
// local file = fs.open(state.mine_dir_path .. level .. '/' .. name, 'w') if name == 'main_shaft' then file.write(state.mine[level][name].west.x .. ',' .. state.mine[level][name].east.x) else file.write(state.mine[level][name].north.z .. ',' .. state.mine[level][name].south.z) end file.close() end function write_turtle_strip(turtle, strip) local file = fs.open(state.turtles_dir_path .. turtle.id .. '/strip', 'w') file.write(strip.y .. ',' .. strip.name .. ',' .. strip.orientation) file.close() end function halt(turtle) add_task(turtle, {action = 'pass', end_state = 'halt'}) fs.open(state.turtles_dir_path .. turtle.id .. '/halt', 'w').close() end function unhalt(turtle) fs.delete(state.turtles_dir_path .. turtle.id .. '/halt', 'w') end function update_strip(turtle) -- RECORD THAT A STRIP HAS BEEN EXPLORED TO TURTLE'S POSITION local strip = turtle.strip if strip then if strip.orientation == 'north' then strip.z = math.min(strip.z, turtle.data.location.z) elseif strip.orientation == 'south' then strip.z = math.max(strip.z, turtle.data.location.z) elseif strip.orientation == 'east' then strip.x = math.max(strip.x, turtle.data.location.x) elseif strip.orientation == 'west' then strip.x = math.min(strip.x, turtle.data.location.x) end write_strip(strip.y, strip.name) end end function expand_mine(level, x) if not state.mine[level][x] then state.mine[level][x] = {} state.mine[level][x].north = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'north'} state.mine[level][x].south = {name = x, x = x, y = level, z = config.locations.mine_enter.z, orientation = 'south'} write_strip(level, x) end end function gen_next_strip() local level = get_mining_level() state.next_strip = get_closest_free_strip(level) if state.next_strip then state.min_fuel = (basics.distance(state.next_strip, config.locations.mine_enter) + config.mission_length) * 3 else state.min_fuel = nil end end function get_closest_free_strip(level) local west_x = config.locations.mine_enter.x local east_x = config.locations.mine_enter.x local offset_x = 0 local min_x = state.mine[level].main_shaft.west.x local max_x = state.mine[level].main_shaft.east.x local closest_strip local distance local z_distance local min_dist = inf while west_x >= min_x and east_x <= max_x and offset_x < min_dist do for _, x in pairs({west_x, east_x}) do for _, z_side in pairs({'north', 'south'}) do expand_mine(level, x) strip = state.mine[level][x][z_side] if not strip.turtles then z_distance = math.abs(strip.z - config.locations.mine_enter.z) distance = z_distance + math.abs(strip.x - config.locations.mine_enter.x) if distance < min_dist then min_dist = distance closest_strip = strip end end end end offset_x = offset_x + config.grid_width west_x = config.locations.mine_enter.x - offset_x east_x = config.locations.mine_enter.x + offset_x end for _, strip in pairs({state.mine[level].main_shaft.west, state.mine[level].main_shaft.east}) do if not strip.turtles then distance = math.abs(strip.x - config.locations.mine_enter.x) if distance <= min_dist then min_dist = distance closest_strip = strip end end end return closest_strip end function get_mining_level() local n = 0 local r = math.random() for _, level_and_chance in pairs(config.mine_levels) do n = n + level_and_chance.chance if n > r then return level_and_chance.level end end end function good_on_fuel(mining_turtle, chunky_turtle) local fuel_needed = math.ceil(basics.distance(mining_turtle.data.location, config.locations.mine_exit) * 1.5) 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)) end function follow(chunky_turtle) add_task(chunky_turtle, { action = 'go_to_strip', data = {chunky_turtle.strip}, end_state = 'wait', }) end function go_mine(mining_turtle) update_strip(mining_turtle) add_task(mining_turtle, { action = 'mine_vein', data = {mining_turtle.strip.orientation}, }) add_task(mining_turtle, { action = 'clear_gravity_blocks', }) if config.use_chunky_turtles then add_task(mining_turtle, { action = 'go_to_strip', data = {mining_turtle.strip}, end_state = 'wait', end_function = follow, end_function_args = {mining_turtle.pair}, }) else add_task(mining_turtle, { action = 'go_to_strip', data = {mining_turtle.strip}, end_state = 'wait', }) end mining_turtle.steps_left = mining_turtle.steps_left - 1 local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w') file.write(mining_turtle.steps_left) file.close() end function free_turtle(turtle) if turtle.pair then fs.delete(state.turtles_dir_path .. turtle.id .. '/deployed') fs.delete(state.turtles_dir_path .. turtle.pair.id .. '/deployed') turtle.pair.pair = nil turtle.pair = nil turtle.strip.turtles = nil end end function pair_turtles_finish() state.pair_hold = nil end function pair_turtles_send(chunky_turtle) add_task(chunky_turtle, { action = 'go_to_mine_enter', end_function = pair_turtles_finish }) add_task(chunky_turtle, { action = 'go_to_strip', data = {chunky_turtle.strip}, end_state = 'wait', }) end function pair_turtles_begin(turtle1, turtle2) local mining_turtle local chunky_turtle if turtle1.data.turtle_type == 'mining' then if turtle2.data.turtle_type ~= 'chunky' then error('Incompatable turtles') end mining_turtle = turtle1 chunky_turtle = turtle2 elseif turtle1.data.turtle_type == 'chunky' then if turtle2.data.turtle_type ~= 'mining' then error('Incompatable turtles') end chunky_turtle = turtle1 mining_turtle = turtle2 end local strip = state.next_strip local level = strip.level if not strip then gen_next_strip() add_task(mining_turtle, {action = 'pass', end_state = 'idle'}) add_task(chunky_turtle, {action = 'pass', end_state = 'idle'}) return end print('Pairing ' .. mining_turtle.id .. ' and ' .. chunky_turtle.id) mining_turtle.pair = chunky_turtle chunky_turtle.pair = mining_turtle state.pair_hold = {mining_turtle, chunky_turtle} mining_turtle.steps_left = config.mission_length strip.turtles = {mining_turtle, chunky_turtle} for _, turtle in pairs(strip.turtles) do turtle.strip = strip write_turtle_strip(turtle, strip) add_task(turtle, {action = 'pass', end_state = 'trip'}) end fs.open(state.turtles_dir_path .. chunky_turtle.id .. '/deployed', 'w').close() local file = fs.open(state.turtles_dir_path .. mining_turtle.id .. '/deployed', 'w') file.write(mining_turtle.steps_left) file.close() add_task(mining_turtle, { action = 'go_to_mine_enter', end_function = pair_turtles_send, end_function_args = {chunky_turtle} }) add_task(mining_turtle, { action = 'go_to_strip', data = {mining_turtle.strip}, end_state = 'wait', }) gen_next_strip() end function solo_turtle_begin(turtle) local strip = state.next_strip local level = strip.level if not strip then gen_next_strip() add_task(turtle, {action = 'pass', end_state = 'idle'}) return end print('Assigning ' .. turtle.id) turtle.steps_left = config.mission_length strip.turtles = {turtle} for _, turtle in pairs(strip.turtles) do turtle.strip = strip write_turtle_strip(turtle, strip) add_task(turtle, {action = 'pass', end_state = 'trip'}) end local file = fs.open(state.turtles_dir_path .. turtle.id .. '/deployed', 'w') file.write(turtle.steps_left) file.close() add_task(turtle, { action = 'go_to_mine_enter', }) add_task(turtle, { action = 'go_to_strip', data = {turtle.strip}, end_state = 'wait', }) gen_next_strip() end function check_pair_fuel(turtle) if state.min_fuel then if (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level <= state.min_fuel) then add_task(turtle, {action = 'prepare', data = {state.min_fuel}}) else add_task(turtle, {action = 'pass', end_state = 'pair'}) end else gen_next_strip() end end function send_turtle_up(turtle) if turtle.data.location.y < config.locations.mine_enter.y then if turtle.strip then if turtle.data.turtle_type == 'chunky' and turtle.data.location.y == turtle.strip.y then add_task(turtle, {action = 'delay', data={3}}) end add_task(turtle, {action = 'go_to_mine_exit', data = {turtle.strip}}) end end end function initialize_turtle(turtle) local data = {session_id, config} if turtle.state ~= 'halt' then turtle.state = 'lost' end turtle.task_id = 2 turtle.tasks = {} add_task(turtle, {action = 'initialize', data = data}) end function add_task(turtle, task) if not task.data then task.data = {} end table.insert(turtle.tasks, task) end function send_tasks(turtle) local task = turtle.tasks[1] if task then local turtle_data = turtle.data if turtle_data.request_id == turtle.task_id and turtle.data.session_id == session_id then if turtle_data.success then if task.end_state then if turtle.state == 'halt' and task.end_state ~= 'halt' then unhalt(turtle) end turtle.state = task.end_state end if task.end_function then if task.end_function_args then task.end_function(unpack(task.end_function_args)) else task.end_function() end end table.remove(turtle.tasks, 1) end turtle.task_id = turtle.task_id + 1 elseif (not turtle_data.busy) and ((not task.epoch) or (task.epoch > os.clock()) or (task.epoch + config.task_timeout < os.clock())) then -- ONLY SEND INSTRUCTION AFTER SECONDS HAVE PASSED task.epoch = os.clock() print(string.format('Sending %s directive to %d', task.action, turtle.id)) rednet.send(turtle.id, { action = task.action, data = task.data, request_id = turtle_data.request_id }, 'mastermine') end end end function user_input(input) -- PROCESS USER INPUT FROM USER_INPUT TABLE while #state.user_input > 0 do local input = table.remove(state.user_input, 1) local next_word = string.gmatch(input, '%S+') local command = next_word() local turtle_id_string = next_word() local turtle_id local turtles = {} if turtle_id_string and turtle_id_string ~= '*' then turtle_id = tonumber(turtle_id_string) if state.turtles[turtle_id] then turtles = {state.turtles[turtle_id]} end else turtles = state.turtles end if command == 'turtle' then -- SEND COMMAND DIRECTLY TO TURTLE local action = next_word() local data = {} for user_arg in next_word do table.insert(data, user_arg) end for _, turtle in pairs(turtles) do halt(turtle) add_task(turtle, { action = action, data = data, }) end elseif command == 'clear' then for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) end elseif command == 'shutdown' then -- REBOOT TURTLE for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) rednet.send(turtle.id, { action = 'shutdown', }, 'mastermine') end elseif command == 'reboot' then -- REBOOT TURTLE for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) rednet.send(turtle.id, { action = 'reboot', }, 'mastermine') end elseif command == 'update' then -- FEED TURTLE DINNER for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) rednet.send(turtle.id, { action = 'update', }, 'mastermine') end elseif command == 'return' then -- BRING TURTLE HOME for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) halt(turtle) send_turtle_up(turtle) add_task(turtle, {action = 'go_to_home'}) end elseif command == 'halt' then -- HALT TURTLE(S) for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) halt(turtle) end elseif command == 'reset' then -- HALT TURTLE(S) for _, turtle in pairs(turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) add_task(turtle, {action = 'pass', end_state = 'lost'}) end elseif command == 'on' or command == 'go' then -- ACTIVATE MINING NETWORK if not turtle_id_string then for _, turtle in pairs(state.turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) end state.on = true fs.open(state.mine_dir_path .. 'on', 'w').close() end elseif command == 'off' or command == 'stop' then -- STANDBY MINING NETWORK if not turtle_id_string then for _, turtle in pairs(state.turtles) do turtle.tasks = {} add_task(turtle, {action = 'pass'}) free_turtle(turtle) end state.on = nil fs.delete(state.mine_dir_path .. 'on') end elseif command == 'hubshutdown' then -- STANDBY MINING NETWORK if not turtle_id_string then os.shutdown() end elseif command == 'hubreboot' then -- STANDBY MINING NETWORK if not turtle_id_string then os.reboot() end elseif command == 'hubupdate' then -- STANDBY MINING NETWORK if not turtle_id_string then os.run({}, '/update') end elseif command == 'debug' then -- DEBUG end end end function command_turtles() local turtles_for_pair = {} for _, turtle in pairs(state.turtles) do if turtle.data then if turtle.data.session_id ~= session_id then -- BABY TURTLE NEEDS TO LEARN if (not turtle.tasks) or (not turtle.tasks[1]) or (not (turtle.tasks[1].action == 'initialize')) then initialize_turtle(turtle) end end if #turtle.tasks > 0 then -- TURTLE IS BUSY send_tasks(turtle) elseif not turtle.data.location then -- TURTLE NEEDS A MAP add_task(turtle, {action = 'calibrate'}) elseif turtle.state ~= 'halt' then if turtle.state == 'park' then -- TURTLE FOUND PARKING if state.on and (config.use_chunky_turtles or turtle.data.turtle_type == 'mining') then add_task(turtle, {action = 'pass', end_state = 'idle'}) end elseif not state.on and turtle.state ~= 'idle' then -- TURTLE HAS TO STOP add_task(turtle, {action = 'pass', end_state = 'idle'}) elseif turtle.state == 'lost' then -- TURTLE IS CONFUSED if turtle.data.location.y < config.locations.mine_enter.y and (turtle.pair or not config.use_chunky_turtles) then add_task(turtle, {action = 'pass', end_state = 'trip'}) add_task(turtle, { action = 'go_to_strip', data = {turtle.strip}, end_state = 'wait' }) else add_task(turtle, {action = 'pass', end_state = 'idle'}) end elseif turtle.state == 'idle' then -- TURTLE IS BORED free_turtle(turtle) if turtle.data.location.y < config.locations.mine_enter.y then send_turtle_up(turtle) elseif not basics.in_area(turtle.data.location, config.locations.control_room_area) then halt(turtle) elseif turtle.data.item_count > 0 or (turtle.data.fuel_level ~= "unlimited" and turtle.data.fuel_level < config.fuel_per_unit) then add_task(turtle, {action = 'prepare', data = {config.fuel_per_unit}}) elseif state.on then add_task(turtle, { action = 'go_to_waiting_room', end_function = check_pair_fuel, end_function_args = {turtle}, }) else add_task(turtle, {action = 'go_to_home', end_state = 'park'}) end elseif turtle.state == 'pair' then -- TURTLE NEEDS A FRIEND if config.use_chunky_turtles then if not state.pair_hold then if not turtle.pair then table.insert(turtles_for_pair, turtle) end else if not (state.pair_hold[1].pair and state.pair_hold[2].pair) then state.pair_hold = nil end end else solo_turtle_begin(turtle) end elseif turtle.state == 'wait' then -- TURTLE GO DO SOME WORK if turtle.pair then if turtle.data.turtle_type == 'mining' and turtle.pair.state == 'wait' then 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 add_task(turtle, {action = 'pass', end_state = 'idle'}) add_task(turtle.pair, {action = 'pass', end_state = 'idle'}) elseif turtle.data.empty_slot_count == 0 then add_task(turtle, { action = 'dump', data = {reverse_shift[turtle.strip.orientation]} }) else add_task(turtle, {action = 'pass', end_state = 'mine'}) add_task(turtle.pair, {action = 'pass', end_state = 'mine'}) go_mine(turtle) end end elseif not config.use_chunky_turtles then if turtle.steps_left <= 0 or turtle.data.empty_slot_count == 0 or not good_on_fuel(turtle) then add_task(turtle, {action = 'pass', end_state = 'idle'}) else add_task(turtle, {action = 'pass', end_state = 'mine'}) go_mine(turtle) end else add_task(turtle, {action = 'pass', end_state = 'idle'}) end elseif turtle.state == 'mine' then if config.use_chunky_turtles and not turtle.pair then add_task(turtle, {action = 'pass', end_state = 'idle'}) end end end end end if #turtles_for_pair == 2 then pair_turtles_begin(turtles_for_pair[1], turtles_for_pair[2]) end end function main() -- INCREASE SESSION ID BY ONE if fs.exists('/session_id') then session_id = tonumber(fs.open('/session_id', 'r').readAll()) + 1 else session_id = 1 end local file = fs.open('/session_id', 'w') file.write(session_id) file.close() -- LOAD MINE INTO MEMORY load_mine() -- FIND THE CLOSEST STRIP gen_next_strip() local cycle = 0 while true do print('Cycle: ' .. cycle) user_input() -- PROCESS USER INPUT command_turtles() -- COMMAND TURTLES sleep(0.1) -- DELAY 0.1 SECONDS cycle = cycle + 1 end end main()]===], } for k, v in pairs(files) do local file = fs.open(fs.combine(path, k), 'w') file.write(v) file.close() end