-- Reactor Info Client -- Receives reactor statistics via rednet and displays them on monitor -- Configuration local PROTOCOL = "reactor_stats" local TIMEOUT = 5 -- Seconds to wait for data before showing error -- Initialize peripherals local m = peripheral.wrap("monitor_1") if not m then print("Error: No monitor found!") return end -- Open rednet on any available modem local modem = nil for _, side in ipairs(peripheral.getNames()) do if peripheral.getType(side) == "modem" then modem = peripheral.wrap(side) if modem.isWireless() then rednet.open(side) print("Rednet opened on wireless modem: " .. side) break end end end if not modem then print("Error: No wireless modem found!") return end -- Rainbow colors for border animation local rainbow_colors = {colors.red, colors.orange, colors.yellow, colors.lime, colors.cyan, colors.blue, colors.purple, colors.magenta} -- Function to draw progress bar local function drawProgressBar(x, y, value, min_val, max_val, target, unit, bar_width, force_color, min_indicator) bar_width = bar_width or 20 local percentage = math.max(0, math.min(100, ((value - min_val) / (max_val - min_val)) * 100)) local filled_chars = math.floor((percentage / 100) * bar_width) -- Color coding based on proximity to target local color = colors.red if force_color then color = force_color elseif target then local target_percentage = ((target - min_val) / (max_val - min_val)) * 100 local diff = math.abs(percentage - target_percentage) if diff < 10 then color = colors.lime elseif diff < 25 then color = colors.yellow end else -- Default color grading for values without targets if percentage > 66 then color = colors.lime elseif percentage > 33 then color = colors.yellow end end m.setCursorPos(x, y) m.setTextColor(colors.gray) m.write("[") -- Calculate minimum indicator position if provided local min_indicator_pos = nil if min_indicator then min_indicator_pos = math.floor(((min_indicator - min_val) / (max_val - min_val)) * bar_width) end -- Draw filled portion for i = 1, bar_width do if min_indicator_pos and i == min_indicator_pos then m.setTextColor(colors.cyan) m.write("|") elseif i <= filled_chars then m.setTextColor(color) m.write("=") else m.setTextColor(colors.gray) m.write("-") end end m.setTextColor(colors.gray) m.write("] ") m.setTextColor(colors.white) m.write(string.format("%.2f %s", value, unit)) end -- Function to show startup animation local function showStartupAnimation() local y_padding = 2 local x_padding = 3 local width, height = m.getSize() m.setTextScale(1) m.clear() -- Phase 2: Moving rainbow border with centered text local title_lines = {"Lag 4 President", "Draconic Reactor", "Quantum Monitor v1.0", " ~ By THCoder aka David"} local corner_text = "Kennst du eigentlich Andi? :D" for frame = 1, 25 do m.clear() -- Draw moving rainbow border local color_offset = frame % #rainbow_colors -- Top and bottom borders for x = 1, width do local color_index = ((x + color_offset - 1) % #rainbow_colors) + 1 m.setTextColor(rainbow_colors[color_index]) m.setCursorPos(x, 1) m.write("=") m.setCursorPos(x, height) m.write("=") end -- Left and right borders for y = 2, height - 1 do local color_index = ((y + color_offset - 1) % #rainbow_colors) + 1 m.setTextColor(rainbow_colors[color_index]) m.setCursorPos(1, y) m.write("|") m.setCursorPos(width, y) m.write("|") end -- Draw centered title text with rainbow colors local start_y = math.floor((height - #title_lines) / 2) for i, line in ipairs(title_lines) do local x_pos = math.floor((width - #line) / 2) + 1 local line_color = rainbow_colors[((i - 1) % #rainbow_colors) + 1] if frame > i * 3 then for j = 1, math.min(#line, frame - i * 3) do m.setTextColor(line_color) m.setCursorPos(x_pos + j - 1, start_y + i) m.write(line:sub(j, j)) end end end -- Draw corner text (bottom right) if frame > 15 then m.setTextColor(colors.cyan) m.setCursorPos(width - #corner_text - 2, height - 2) m.write(corner_text) end sleep(0.1) end sleep(3) end -- Function to display reactor data local function displayReactorData(info) local y_padding = 2 local x_padding = 3 local value_align = 25 local width, height = m.getSize() -- Clear and prepare monitor m.clear() m.setTextScale(1) -- Header line m.setCursorPos(x_padding + 8, 1) m.setTextColor(colors.white) m.write("* ") m.setTextColor(colors.red) m.write("Lag4President") m.setTextColor(colors.white) m.write(" - ") m.setTextColor(colors.purple) m.write("Draconic Reactor") m.setTextColor(colors.white) m.write(" *") -- Extract data local status = info["status"] local temp = info["temperature"] local energyOut = info["generationRate"] / 1000 -- Convert to kRF/t local shieldIn = info["fieldDrainRate"] / 1000 -- Convert to kRF/t local netEnergy = energyOut - shieldIn local fieldStrength = (info["fieldStrength"] / info["maxFieldStrength"]) * 100 local saturation = (info["energySaturation"] / info["maxEnergySaturation"]) * 100 -- Check for fuel-related data local fuelConversion = info["fuelConversion"] or 0 local maxFuelConversion = info["maxFuelConversion"] or 1 local fuelConversionRate = info["fuelConversionRate"] or 0 -- Calculate fuel percentage if available local fuelPercentage = 0 if maxFuelConversion > 0 then fuelPercentage = (fuelConversion / maxFuelConversion) * 100 end local row = y_padding + 1 -- Start below header -- Status (no progress bar) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Reactor Status:") m.setCursorPos(value_align, row) if status == "running" or status == "online" then m.setTextColor(colors.lime) m.write("Running") elseif status == "cooling" then m.setTextColor(colors.yellow) m.write("Shutting Down...") elseif status == "cold" then m.setTextColor(colors.red) m.write("Offline / Powered off") elseif status == "charging" then m.setTextColor(colors.yellow) m.write("Charging") elseif status == "charged" then m.setTextColor(colors.lime) m.write("Charged - Ready for Activation") else m.setTextColor(colors.gray) m.write("Unknown State") end row = row + 2 -- Temperature progress bar (0-8000, with minimum indicator at 2000) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Temperature:") drawProgressBar(value_align, row, temp, 0, 8000, 7500, "C", 15, nil, 2000) row = row + 2 -- Energy Output progress bar with efficiency scaling local base_output = 4000 local efficiency_multiplier = 1 + (fuelPercentage / 100) * 1.5 local expected_output = base_output * efficiency_multiplier local max_output = base_output * 2.5 m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Energy Output:") drawProgressBar(value_align, row, energyOut, 0, max_output, expected_output, "kRF/t", 15) row = row + 2 -- Field Input progress bar (0-2000, target 1400) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Field Input:") drawProgressBar(value_align, row, shieldIn, 0, 2000, 1400, "kRF/t", 15) row = row + 2 -- Net Energy (just value with color grading) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Net Energy:") m.setCursorPos(value_align, row) if netEnergy < 0 then m.setTextColor(colors.red) elseif netEnergy < 1000 then m.setTextColor(colors.yellow) else m.setTextColor(colors.lime) end m.write(string.format("%.2f kRF/t", netEnergy)) row = row + 2 -- Field Strength progress bar (0-100, target 15) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Field Strength:") drawProgressBar(value_align, row, fieldStrength, 0, 100, 25, "%", 15) row = row + 2 -- Energy Saturation progress bar (0-100, target 15) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Energy Saturation:") drawProgressBar(value_align, row, saturation, 0, 100, 15, "%", 15) row = row + 2 -- Fuel Conversion progress bar (0-100) - white color m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Fuel Progress:") drawProgressBar(value_align, row, fuelPercentage, 0, 100, nil, "%", 15, colors.white) row = row + 2 -- Fuel Rate progress bar (0-5) m.setCursorPos(x_padding, row) m.setTextColor(colors.white) m.write("Fuel Rate:") drawProgressBar(value_align, row, fuelConversionRate / 1000, 0, 5, nil, "nb/t", 15) -- Bottom warning notes m.setCursorPos(x_padding + 5, height - 3) m.setTextColor(colors.red) m.write("NOTE:") m.setTextColor(colors.orange) m.write(" NEVER exceed 8000 C temperature!") m.setCursorPos(x_padding + 11, height - 2) m.write("NEVER let shield drop below 10%!") m.setCursorPos(x_padding + 11, height - 1) m.write("But making it close is quite rewarding :)") end -- Function to show connection error local function showConnectionError() m.clear() m.setTextScale(1) local width, height = m.getSize() local y_center = math.floor(height / 2) m.setCursorPos(1, y_center - 2) m.setTextColor(colors.red) m.write("CONNECTION ERROR") m.setCursorPos(1, y_center) m.setTextColor(colors.white) m.write("No data received from server") m.setCursorPos(1, y_center + 1) m.write("Waiting for reactor server...") m.setCursorPos(1, y_center + 3) m.setTextColor(colors.gray) m.write("Protocol: " .. PROTOCOL) end -- Main program print("Reactor Info Client Started") print("Listening for protocol: " .. PROTOCOL) print("Press Ctrl+T to stop") -- Show startup animation showStartupAnimation() while true do -- Listen for reactor data local sender, message, protocol = rednet.receive(PROTOCOL, TIMEOUT) if sender and message and protocol == PROTOCOL then -- Display the received reactor data displayReactorData(message) -- Debug output to terminal term.clear() term.setCursorPos(1, 1) print("=== REACTOR CLIENT STATUS ===") print("Connected to server ID: " .. sender) print("Last update: " .. os.time()) print("Reactor status: " .. tostring(message.status)) print("") print("Displaying data on monitor...") else -- No data received within timeout showConnectionError() -- Debug output to terminal term.clear() term.setCursorPos(1, 1) print("=== REACTOR CLIENT ERROR ===") print("No data received within " .. TIMEOUT .. " seconds") print("Waiting for server...") end end