Last active
July 29, 2026 20:38
-
-
Save dyc3/6462f43f84d029809ebadb64d19b9f97 to your computer and use it in GitHub Desktop.
nuclearcraft reactor builder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- reactor_builder.lua | |
| -- | |
| -- Usage: | |
| -- reactor_builder <json file> | |
| -- get json from https://leu-235.com/ | |
| -- | |
| -- Example: | |
| -- reactor_builder reactor.json | |
| -- | |
| -- Starting position: | |
| -- - Turtle is ONE BLOCK ABOVE the bottom-left reactor coordinate. | |
| -- - The block directly below the turtle is reactor coordinate X=1,Y=1,Z=1. | |
| -- - Turtle faces toward increasing X. | |
| -- - Increasing Z is initially to the turtle's right. | |
| -- | |
| -- The turtle builds each layer underneath itself. | |
| -- When it runs out of a required block, it pauses and asks you to refill it. | |
| local args = {...} | |
| if #args ~= 1 then | |
| error("Usage: reactor_builder <json file>", 0) | |
| end | |
| local jsonPath = args[1] | |
| local file = fs.open(jsonPath, "r") | |
| if not file then | |
| error("Could not open " .. jsonPath, 0) | |
| end | |
| local reactor = textutils.unserializeJSON(file.readAll()) | |
| file.close() | |
| if not reactor then | |
| error("Invalid JSON in " .. jsonPath, 0) | |
| end | |
| local dimensions = reactor.InteriorDimensions | |
| local compressed = reactor.CompressedReactor | |
| if not dimensions or not compressed then | |
| error("JSON is missing InteriorDimensions or CompressedReactor", 0) | |
| end | |
| local WIDTH = dimensions.X | |
| local HEIGHT = dimensions.Y | |
| local DEPTH = dimensions.Z | |
| local MATERIALS = { | |
| FuelCell = { | |
| name = "nuclearcraft:cell_block", | |
| }, | |
| Water = { | |
| name = "nuclearcraft:cooler", | |
| metadata = 1, | |
| }, | |
| Redstone = { | |
| name = "nuclearcraft:cooler", | |
| metadata = 2, | |
| }, | |
| Gold = { | |
| name = "nuclearcraft:cooler", | |
| metadata = 4, | |
| }, | |
| Lapis = { | |
| name = "nuclearcraft:cooler", | |
| metadata = 6, | |
| }, | |
| Cryotheum = { | |
| name = "nuclearcraft:cooler", | |
| metadata = 10, | |
| }, | |
| } | |
| local blocks = {} | |
| local function positionKey(x, y, z) | |
| return x .. "," .. y .. "," .. z | |
| end | |
| for materialName, positions in pairs(compressed) do | |
| local material = MATERIALS[materialName] | |
| if material then | |
| for _, position in ipairs(positions) do | |
| blocks[positionKey(position.X, position.Y, position.Z)] = | |
| materialName | |
| end | |
| else | |
| print("Warning: ignoring unknown material: " .. materialName) | |
| end | |
| end | |
| local function itemMetadata(detail) | |
| return detail.damage or detail.metadata | |
| end | |
| local function itemMatches(detail, material) | |
| if not detail or detail.name ~= material.name then | |
| return false | |
| end | |
| if material.metadata == nil then | |
| return true | |
| end | |
| return itemMetadata(detail) == material.metadata | |
| end | |
| local function findMaterial(materialName) | |
| local material = MATERIALS[materialName] | |
| for slot = 1, 16 do | |
| local detail = turtle.getItemDetail(slot) | |
| if itemMatches(detail, material) then | |
| return slot | |
| end | |
| end | |
| return nil | |
| end | |
| local function selectMaterial(materialName) | |
| while true do | |
| local slot = findMaterial(materialName) | |
| if slot then | |
| turtle.select(slot) | |
| return | |
| end | |
| local material = MATERIALS[materialName] | |
| print() | |
| print("Out of " .. materialName) | |
| print("Required item: " .. material.name) | |
| if material.metadata ~= nil then | |
| print("Required metadata: " .. material.metadata) | |
| end | |
| print("Refill the turtle, then press Enter.") | |
| read() | |
| end | |
| end | |
| local function refuel() | |
| if turtle.getFuelLevel() == "unlimited" then | |
| return | |
| end | |
| while turtle.getFuelLevel() < 1 do | |
| local previousSlot = turtle.getSelectedSlot() | |
| local foundFuel = false | |
| for slot = 1, 16 do | |
| if turtle.getItemCount(slot) > 0 then | |
| turtle.select(slot) | |
| if turtle.refuel(0) then | |
| turtle.refuel(1) | |
| foundFuel = true | |
| break | |
| end | |
| end | |
| end | |
| turtle.select(previousSlot) | |
| if not foundFuel then | |
| print() | |
| print("Out of fuel.") | |
| print("Add fuel to the turtle, then press Enter.") | |
| read() | |
| end | |
| end | |
| end | |
| local function moveForward() | |
| while true do | |
| refuel() | |
| if turtle.forward() then | |
| return | |
| end | |
| if turtle.detect() then | |
| turtle.dig() | |
| else | |
| turtle.attack() | |
| end | |
| sleep(0.1) | |
| end | |
| end | |
| local function moveUp() | |
| while true do | |
| refuel() | |
| if turtle.up() then | |
| return | |
| end | |
| if turtle.detectUp() then | |
| turtle.digUp() | |
| else | |
| turtle.attackUp() | |
| end | |
| sleep(0.1) | |
| end | |
| end | |
| local direction = 0 | |
| local currentX = 1 | |
| local currentZ = 1 | |
| local function turnRight() | |
| turtle.turnRight() | |
| direction = (direction + 1) % 4 | |
| end | |
| local function turnLeft() | |
| turtle.turnLeft() | |
| direction = (direction + 3) % 4 | |
| end | |
| local function face(targetDirection) | |
| local difference = (targetDirection - direction) % 4 | |
| if difference == 1 then | |
| turnRight() | |
| elseif difference == 2 then | |
| turnRight() | |
| turnRight() | |
| elseif difference == 3 then | |
| turnLeft() | |
| end | |
| end | |
| local function stepForward() | |
| moveForward() | |
| if direction == 0 then | |
| currentX = currentX + 1 | |
| elseif direction == 1 then | |
| currentZ = currentZ + 1 | |
| elseif direction == 2 then | |
| currentX = currentX - 1 | |
| elseif direction == 3 then | |
| currentZ = currentZ - 1 | |
| end | |
| end | |
| local function moveTo(targetX, targetZ) | |
| if currentX < targetX then | |
| face(0) | |
| while currentX < targetX do | |
| stepForward() | |
| end | |
| elseif currentX > targetX then | |
| face(2) | |
| while currentX > targetX do | |
| stepForward() | |
| end | |
| end | |
| if currentZ < targetZ then | |
| face(1) | |
| while currentZ < targetZ do | |
| stepForward() | |
| end | |
| elseif currentZ > targetZ then | |
| face(3) | |
| while currentZ > targetZ do | |
| stepForward() | |
| end | |
| end | |
| end | |
| local function blockBelowMatches(materialName) | |
| local exists, block = turtle.inspectDown() | |
| if not exists then | |
| return false | |
| end | |
| local material = MATERIALS[materialName] | |
| if block.name ~= material.name then | |
| return false | |
| end | |
| if material.metadata == nil then | |
| return true | |
| end | |
| return block.metadata == material.metadata | |
| or block.damage == material.metadata | |
| end | |
| local function placeBlock(materialName, x, y, z) | |
| if blockBelowMatches(materialName) then | |
| return | |
| end | |
| if turtle.detectDown() then | |
| local _, block = turtle.inspectDown() | |
| error( | |
| string.format( | |
| "Wrong block at X=%d Y=%d Z=%d: %s", | |
| x, | |
| y, | |
| z, | |
| block and block.name or "unknown" | |
| ), | |
| 0 | |
| ) | |
| end | |
| selectMaterial(materialName) | |
| while not turtle.placeDown() do | |
| print( | |
| string.format( | |
| "Could not place %s at X=%d Y=%d Z=%d", | |
| materialName, | |
| x, | |
| y, | |
| z | |
| ) | |
| ) | |
| print("Fix the obstruction or refill the turtle, then press Enter.") | |
| read() | |
| selectMaterial(materialName) | |
| end | |
| end | |
| local function buildLayer(y) | |
| print(string.format("Building layer %d/%d", y, HEIGHT)) | |
| for z = 1, DEPTH do | |
| local startX | |
| local endX | |
| local increment | |
| if z % 2 == 1 then | |
| startX = 1 | |
| endX = WIDTH | |
| increment = 1 | |
| else | |
| startX = WIDTH | |
| endX = 1 | |
| increment = -1 | |
| end | |
| moveTo(startX, z) | |
| local x = startX | |
| while true do | |
| local materialName = blocks[positionKey(x, y, z)] | |
| if materialName then | |
| placeBlock(materialName, x, y, z) | |
| end | |
| if x == endX then | |
| break | |
| end | |
| face(increment == 1 and 0 or 2) | |
| stepForward() | |
| x = x + increment | |
| end | |
| end | |
| end | |
| print("Reactor dimensions:") | |
| print(string.format(" X: %d", WIDTH)) | |
| print(string.format(" Y: %d", HEIGHT)) | |
| print(string.format(" Z: %d", DEPTH)) | |
| print() | |
| print("Ensure the turtle is:") | |
| print(" 1. One block above reactor coordinate X=1,Y=1,Z=1") | |
| print(" 2. Facing toward increasing X") | |
| print(" 3. Positioned so increasing Z is to its right") | |
| print() | |
| print("Press Enter to begin.") | |
| read() | |
| for y = 1, HEIGHT do | |
| buildLayer(y) | |
| if y < HEIGHT then | |
| moveTo(1, 1) | |
| face(0) | |
| moveUp() | |
| end | |
| end | |
| moveTo(1, 1) | |
| face(0) | |
| print() | |
| print("Reactor interior complete.") | |
| print("Turtle is above X=1,Y=" .. HEIGHT .. ",Z=1") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment