-- nanomachine_client.lua -- Designed by fxllenfx for fx-Studios -- Advanced Nanomachine Control System v2.0 local component = require("component") local event = require("event") local term = require("term") local gpu = component.gpu local serialization = require("serialization") -- Config local SERVER_ADDR = nil local PORT = 2000 local modem = component.modem local nano = component.nanomachines -- UI State local currentScreen = "main" local selectedProfile = nil local scrollOffset = 0 local statusData = {} local profiles = {} local discoveryActive = false local discoveryProgress = 0 -- Colors local COLOR_BG = 0x000000 local COLOR_FG = 0xFFFFFF local COLOR_HEADER = 0x00FFFF local COLOR_SUCCESS = 0x00FF00 local COLOR_WARNING = 0xFFFF00 local COLOR_ERROR = 0xFF0000 local COLOR_ACTIVE = 0x00FF00 local COLOR_INACTIVE = 0x888888 local COLOR_BUTTON = 0x0066CC local COLOR_SELECTED = 0xFF6600 local COLOR_FX = 0xFF00FF -- Boot Animation local function bootAnimation() term.clear() gpu.setBackground(COLOR_BG) gpu.setResolution(80, 25) local startY = 8 -- fx-Studios stylized text (using box drawing characters) gpu.setForeground(COLOR_FX) local logo1 = "███████╗██╗ ██╗ ███████╗████████╗██╗ ██╗██████╗ ██╗ ██████╗ ███████╗" local logo2 = "██╔════╝╚██╗██╔╝ ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██║██╔═══██╗██╔════╝" local logo3 = "█████╗ ╚███╔╝ █████╗███████╗ ██║ ██║ ██║██║ ██║██║██║ ██║███████╗" local logo4 = "██╔══╝ ██╔██╗ ╚════╝╚════██║ ██║ ██║ ██║██║ ██║██║██║ ██║╚════██║" local logo5 = "██║ ██╔╝ ██╗ ███████║ ██║ ╚██████╔╝██████╔╝██║╚██████╔╝███████║" local logo6 = "╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝" -- Animated reveal gpu.set(1, startY, logo1) os.sleep(0.12) gpu.set(1, startY + 1, logo2) os.sleep(0.12) gpu.set(1, startY + 2, logo3) os.sleep(0.12) gpu.set(1, startY + 3, logo4) os.sleep(0.12) gpu.set(1, startY + 4, logo5) os.sleep(0.12) gpu.set(1, startY + 5, logo6) os.sleep(0.5) -- Subtitle gpu.setForeground(COLOR_HEADER) local subtitle = "NANOMACHINE CONTROL SYSTEM v2.0" gpu.set(math.floor((80 - #subtitle) / 2), startY + 7, subtitle) os.sleep(0.3) -- Credits gpu.setForeground(COLOR_FX) local credit = "» Designed by fxllenfx «" gpu.set(math.floor((80 - #credit) / 2), startY + 9, credit) os.sleep(0.6) -- Loading bar gpu.setForeground(COLOR_SUCCESS) local loadText = "[ Initialisiere System ]" gpu.set(math.floor((80 - #loadText) / 2), startY + 11, loadText) local barWidth = 50 local barX = math.floor((80 - barWidth - 2) / 2) local barY = startY + 12 gpu.setForeground(COLOR_INACTIVE) gpu.set(barX, barY, "[" .. string.rep("░", barWidth) .. "]") for i = 1, barWidth do local color = COLOR_FX if i < barWidth * 0.33 then color = 0xFF0000 elseif i < barWidth * 0.66 then color = 0xFFFF00 else color = COLOR_SUCCESS end gpu.setForeground(color) gpu.set(barX + i, barY, "█") os.sleep(0.03) end os.sleep(0.4) -- System checks local checks = { {text = "Modem erkannt", status = modem ~= nil}, {text = "Wireless aktiv", status = modem and modem.isWireless()}, {text = "Nanomachines verbunden", status = nano ~= nil}, {text = "Inputs verfuegbar", status = nano and nano.getInputCount() > 0} } local checkY = startY + 14 for i, check in ipairs(checks) do os.sleep(0.25) if check.status then gpu.setForeground(COLOR_SUCCESS) gpu.set(20, checkY + i - 1, "[✓]") else gpu.setForeground(COLOR_ERROR) gpu.set(20, checkY + i - 1, "[✗]") end gpu.setForeground(COLOR_FG) gpu.set(25, checkY + i - 1, check.text) end os.sleep(0.8) -- Final message with animation local readyMsg = ">>> SYSTEM BEREIT <<<" gpu.setForeground(COLOR_SUCCESS) for i = 1, 3 do gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, readyMsg) os.sleep(0.3) gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, string.rep(" ", #readyMsg)) os.sleep(0.2) end gpu.set(math.floor((80 - #readyMsg) / 2), checkY + 5, readyMsg) os.sleep(1.2) end -- Check for nanomachines local function checkNanomachines() if not nano then term.clear() gpu.setForeground(COLOR_ERROR) print("╔════════════════════════════════════════╗") print("║ FEHLER: Keine Nanomachines gefunden! ║") print("╚════════════════════════════════════════╝") print("") gpu.setForeground(COLOR_FG) print("Stelle sicher dass:") print(" [1] Du Nanomachines konsumiert hast") print(" [2] Sie geladen sind") print(" [3] Du eine Wireless Network Card hast") print("") gpu.setForeground(COLOR_WARNING) print("Druecke eine Taste zum Beenden...") event.pull("key_down") return false end return true end -- Server Setup local function setupServer() term.clear() gpu.setForeground(COLOR_HEADER) print("╔═══════════════════════════════════════════╗") print("║ NANOMACHINE CLIENT SETUP ║") print("╚═══════════════════════════════════════════╝") print("") gpu.setForeground(COLOR_FG) print("Server-Adresse eingeben:") gpu.setForeground(COLOR_INACTIVE) print("(oder Enter druecken fuer Standalone-Modus)") print("") gpu.setForeground(COLOR_FG) io.write("> ") SERVER_ADDR = io.read() if SERVER_ADDR == "" then SERVER_ADDR = nil print("") gpu.setForeground(COLOR_WARNING) print("⚠ Standalone-Modus (kein Server)") gpu.setForeground(COLOR_INACTIVE) print("Profile werden lokal gespeichert") else print("") gpu.setForeground(COLOR_SUCCESS) print("✓ Server gesetzt: " .. SERVER_ADDR:sub(1, 8) .. "...") gpu.setForeground(COLOR_INACTIVE) print("Profile werden auf Server synchronisiert") end gpu.setForeground(COLOR_FG) print("") print("Taste druecken zum Fortfahren...") event.pull("key_down") end -- Communication local function sendCommand(command, data) if not SERVER_ADDR then return end local serialized = data and serialization.serialize(data) or nil modem.send(SERVER_ADDR, PORT, command, serialized) end local function requestData(command) if not SERVER_ADDR then return nil end sendCommand(command) local timeout = os.time() + 2 while os.time() < timeout do local _, _, sender, _, _, cmd, serializedData = event.pull(0.1, "modem_message") if cmd and cmd == command .. "_RESPONSE" then if serializedData then local success, data = pcall(serialization.unserialize, serializedData) if success then return data end end return {} end end return nil end -- Nanomachine Control local function updateNanoStatus() if not nano then return end statusData.power = nano.getPowerState() statusData.maxPower = nano.getMaxPower() statusData.totalInputs = nano.getInputCount() -- Count active inputs statusData.activeInputs = 0 for i = 1, statusData.totalInputs do if nano.getInput(i) then statusData.activeInputs = statusData.activeInputs + 1 end end -- Get active effects local effects = nano.getActiveEffects() statusData.activeEffects = effects and #effects or 0 statusData.effects = effects or {} statusData.lastUpdate = os.time() end local function setInput(inputId, active) if not nano or inputId < 1 or inputId > statusData.totalInputs then return false end nano.setInput(inputId, active) return true end local function toggleInput(inputId) if not nano then return false end local currentState = nano.getInput(inputId) return setInput(inputId, not currentState) end local function disableAllInputs() if not nano then return end for i = 1, statusData.totalInputs do setInput(i, false) end statusData.currentProfile = nil end local function getCurrentInputState() if not nano then return {} end local inputs = {} for i = 1, statusData.totalInputs do if nano.getInput(i) then table.insert(inputs, i) end end return inputs end local function applyInputState(inputs) if not nano then return end -- Disable all first disableAllInputs() -- Enable specified inputs for _, inputId in ipairs(inputs) do setInput(inputId, true) end end -- Profile Management local function loadProfiles() if SERVER_ADDR then local serverProfiles = requestData("GET_PROFILES") if serverProfiles then profiles = serverProfiles return end end -- Default profiles if no server or server unavailable if #profiles == 0 then profiles = { {name = "Safe", description = "Nur Regeneration", inputs = {}}, {name = "Combat", description = "Kampf-Modus", inputs = {}}, {name = "Mining", description = "Bergbau-Modus", inputs = {}} } end end local function saveCurrentAsProfile(name, description) local inputs = getCurrentInputState() local profile = { name = name, description = description or "Eigenes Profil", inputs = inputs, created = os.time() } table.insert(profiles, profile) -- Send to server if connected if SERVER_ADDR then sendCommand("SAVE_PROFILE", profile) end return true end local function activateProfile(profileName) local profile = nil for _, p in ipairs(profiles) do if p.name == profileName then profile = p break end end if not profile then return false end applyInputState(profile.inputs) statusData.currentProfile = profileName return true end -- Draw Functions local function drawHeader(title) gpu.setBackground(COLOR_BG) gpu.setForeground(COLOR_FX) gpu.fill(1, 1, 80, 3, " ") local fx = "fx-Studios" gpu.set(2, 1, fx) gpu.setForeground(COLOR_HEADER) local titleText = "╔═══ " .. title .. " ═══╗" gpu.set(math.floor((80 - #titleText) / 2), 2, titleText) gpu.setForeground(COLOR_INACTIVE) gpu.set(70, 1, "v2.0") end local function drawButton(x, y, text, width, active) local bg = active and COLOR_SELECTED or COLOR_BUTTON gpu.setBackground(bg) gpu.setForeground(COLOR_FG) local padding = math.floor((width - #text) / 2) local buttonText = string.rep(" ", padding) .. text .. string.rep(" ", width - padding - #text) gpu.set(x, y, buttonText) gpu.setBackground(COLOR_BG) end local function drawStatusBar(y, label, value, maxValue, color) gpu.setForeground(COLOR_FG) gpu.set(2, y, label .. ":") local barWidth = 40 local barX = 25 local percentage = maxValue > 0 and (value / maxValue) or 0 local filled = math.floor(barWidth * percentage) gpu.setForeground(color) gpu.set(barX, y, "[" .. string.rep("█", filled) .. string.rep("░", barWidth - filled) .. "]") gpu.setForeground(COLOR_FG) gpu.set(barX + barWidth + 3, y, string.format("%d/%d (%.1f%%)", value, maxValue, percentage * 100)) end -- Main Dashboard Screen local function drawMainScreen() term.clear() drawHeader("NANOMACHINE CONTROL CENTER") gpu.setForeground(COLOR_FG) -- Status Section gpu.setForeground(COLOR_HEADER) gpu.set(2, 5, "═══ SYSTEM STATUS ═══") gpu.setForeground(COLOR_FG) drawStatusBar(7, "Power", statusData.power or 0, statusData.maxPower or 100, COLOR_SUCCESS) gpu.set(2, 9, "Aktive Inputs: " .. (statusData.activeInputs or 0) .. "/" .. (statusData.totalInputs or 0)) gpu.set(2, 10, "Aktive Effekte: " .. (statusData.activeEffects or 0)) -- Display active effects if statusData.effects and #statusData.effects > 0 then gpu.setForeground(COLOR_SUCCESS) local effectText = table.concat(statusData.effects, ", ") if #effectText > 75 then effectText = effectText:sub(1, 72) .. "..." end gpu.set(2, 11, "Effekte: " .. effectText) gpu.setForeground(COLOR_FG) end -- Current Profile gpu.setForeground(COLOR_HEADER) gpu.set(2, 13, "═══ AKTUELLES PROFIL ═══") gpu.setForeground(COLOR_FG) if statusData.currentProfile then gpu.setForeground(COLOR_SUCCESS) gpu.set(2, 14, "● " .. statusData.currentProfile) else gpu.setForeground(COLOR_INACTIVE) gpu.set(2, 14, "○ Kein Profil aktiv") end -- Server Status gpu.setForeground(COLOR_HEADER) gpu.set(45, 13, "═══ SERVER ═══") gpu.setForeground(COLOR_FG) if SERVER_ADDR then gpu.setForeground(COLOR_SUCCESS) gpu.set(45, 14, "● Verbunden") gpu.setForeground(COLOR_INACTIVE) gpu.set(45, 15, SERVER_ADDR:sub(1, 16) .. "...") else gpu.setForeground(COLOR_WARNING) gpu.set(45, 14, "○ Standalone") end gpu.setForeground(COLOR_FG) -- Navigation Buttons gpu.setForeground(COLOR_HEADER) gpu.set(2, 18, "═══ SCHNELLZUGRIFF ═══") drawButton(2, 20, "1: Profile", 15, false) drawButton(19, 20, "2: Manuell", 15, false) drawButton(36, 20, "3: Discovery", 15, false) drawButton(53, 20, "4: Effekte", 15, false) drawButton(2, 22, "E: Notaus", 15, false) drawButton(19, 22, "S: Speichern", 17, false) drawButton(38, 22, "R: Refresh", 15, false) -- Footer gpu.setForeground(COLOR_INACTIVE) gpu.set(2, 24, "Q: Beenden") gpu.setForeground(COLOR_FX) gpu.set(60, 24, "by fxllenfx") end -- Profile Selection Screen local function drawProfileScreen() term.clear() drawHeader("PROFILE") gpu.setForeground(COLOR_FG) if not profiles or #profiles == 0 then gpu.setForeground(COLOR_WARNING) gpu.set(2, 5, "Keine Profile vorhanden") gpu.setForeground(COLOR_FG) gpu.set(2, 7, "Druecke S um aktuellen Status als Profil zu speichern") gpu.set(2, 8, "Druecke B um zurueck zu gehen") return end gpu.setForeground(COLOR_HEADER) gpu.set(2, 5, "═══ VERFUEGBARE PROFILE ═══") gpu.setForeground(COLOR_FG) for i, profile in ipairs(profiles) do if i > scrollOffset and i <= scrollOffset + 12 then local y = 7 + (i - scrollOffset - 1) local isActive = statusData.currentProfile == profile.name if isActive then gpu.setForeground(COLOR_SUCCESS) gpu.set(2, y, "▶ ") else gpu.setForeground(COLOR_INACTIVE) gpu.set(2, y, " ") end gpu.setForeground(COLOR_FG) gpu.set(5, y, string.format("[%d] %s", i, profile.name)) if profile.description then gpu.setForeground(COLOR_INACTIVE) gpu.set(30, y, "- " .. profile.description) end gpu.setForeground(COLOR_INACTIVE) local inputCount = profile.inputs and #profile.inputs or 0 gpu.set(65, y, "(" .. inputCount .. " inputs)") end end -- Instructions gpu.setForeground(COLOR_HEADER) gpu.set(2, 21, "═══ STEUERUNG ═══") gpu.setForeground(COLOR_FG) gpu.set(2, 22, "Zahl: Profil aktivieren") gpu.set(2, 23, "B: Zurueck") end -- Manual Control Screen local function drawManualScreen() term.clear() drawHeader("MANUELLE STEUERUNG") gpu.setForeground(COLOR_FG) if not statusData.totalInputs or statusData.totalInputs == 0 then gpu.setForeground(COLOR_WARNING) gpu.set(2, 5, "Keine Input-Daten verfuegbar") return end gpu.setForeground(COLOR_HEADER) gpu.set(2, 5, "═══ INPUT KANAELE ═══") gpu.setForeground(COLOR_FG) local cols = 4 local maxPerScreen = 40 local row = 0 for i = 1, math.min(statusData.totalInputs, maxPerScreen) do local isActive = nano.getInput(i) local col = (i - 1) % cols local x = 2 + (col * 19) local y = 7 + row if col == 0 and i > 1 then row = row + 1 end local status = isActive and " ON" or "OFF" local color = isActive and COLOR_SUCCESS or COLOR_INACTIVE gpu.setForeground(color) gpu.set(x, y, string.format("[%2d] %s", i, status)) end -- Instructions gpu.setForeground(COLOR_HEADER) gpu.set(2, 20, "═══ STEUERUNG ═══") gpu.setForeground(COLOR_FG) gpu.set(2, 21, "Zahl: Input umschalten") gpu.set(2, 22, "X: Alle deaktivieren") gpu.set(2, 23, "B: Zurueck") end -- Discovery Screen local function drawDiscoveryScreen() term.clear() drawHeader("EFFEKT DISCOVERY") gpu.setForeground(COLOR_FG) gpu.setForeground(COLOR_HEADER) gpu.set(2, 5, "═══ DISCOVERY MODUS ═══") gpu.setForeground(COLOR_FG) if discoveryActive then gpu.setForeground(COLOR_SUCCESS) gpu.set(2, 7, "● Discovery laeuft...") gpu.setForeground(COLOR_FG) gpu.set(2, 9, "Fortschritt: " .. discoveryProgress .. "%") else gpu.setForeground(COLOR_INACTIVE) gpu.set(2, 7, "○ Discovery inaktiv") end gpu.setForeground(COLOR_HEADER) gpu.set(2, 12, "═══ STEUERUNG ═══") gpu.setForeground(COLOR_FG) gpu.set(2, 13, "S: Discovery starten") gpu.set(2, 14, "P: Discovery pausieren") gpu.set(2, 15, "R: Discovery zuruecksetzen") gpu.set(2, 16, "B: Zurueck") gpu.setForeground(COLOR_WARNING) gpu.set(2, 19, "⚠ Warnung: Discovery testet systematisch alle") gpu.set(2, 20, " Input-Kombinationen. Dies kann mehrere Minuten dauern.") end -- Effects Screen local function drawEffectsScreen() term.clear() drawHeader("AKTIVE EFFEKTE") gpu.setForeground(COLOR_HEADER) gpu.set(2, 5, "═══ EFFEKT DETAILS ═══") gpu.setForeground(COLOR_FG) if statusData.effects and #statusData.effects > 0 then for i, effect in ipairs(statusData.effects) do local y = 7 + i - 1 if y < 20 then gpu.setForeground(COLOR_SUCCESS) gpu.set(2, y, "● " .. effect) end end else gpu.setForeground(COLOR_INACTIVE) gpu.set(2, 7, "Keine aktiven Effekte") end gpu.setForeground(COLOR_HEADER) gpu.set(2, 21, "═══ STEUERUNG ═══") gpu.setForeground(COLOR_FG) gpu.set(2, 22, "B: Zurueck") end -- Input Handling local function handleMainInput(char) if char == string.byte("1") then currentScreen = "profiles" elseif char == string.byte("2") then currentScreen = "manual" elseif char == string.byte("3") then currentScreen = "discovery" elseif char == string.byte("4") then currentScreen = "effects" elseif char == string.byte("e") or char == string.byte("E") then disableAllInputs() updateNanoStatus() elseif char == string.byte("s") or char == string.byte("S") then term.clear() print("Profilname eingeben:") local name = io.read() if name and name ~= "" then print("Beschreibung (optional):") local desc = io.read() saveCurrentAsProfile(name, desc ~= "" and desc or nil) loadProfiles() end elseif char == string.byte("r") or char == string.byte("R") then updateNanoStatus() if SERVER_ADDR then loadProfiles() end elseif char == string.byte("q") or char == string.byte("Q") then return false end return true end local function handleProfileInput(char) if char == string.byte("b") or char == string.byte("B") then currentScreen = "main" elseif char >= string.byte("0") and char <= string.byte("9") then local num = char - string.byte("0") if profiles[num] then activateProfile(profiles[num].name) updateNanoStatus() end end end local function handleManualInput(char) if char == string.byte("b") or char == string.byte("B") then currentScreen = "main" elseif char == string.byte("x") or char == string.byte("X") then disableAllInputs() updateNanoStatus() elseif char >= string.byte("0") and char <= string.byte("9") then local num = char - string.byte("0") if num <= statusData.totalInputs then toggleInput(num) updateNanoStatus() end end end local function handleDiscoveryInput(char) if char == string.byte("b") or char == string.byte("B") then currentScreen = "main" elseif char == string.byte("s") or char == string.byte("S") then discoveryActive = true if SERVER_ADDR then sendCommand("START_DISCOVERY") end elseif char == string.byte("p") or char == string.byte("P") then discoveryActive = false if SERVER_ADDR then sendCommand("PAUSE_DISCOVERY") end elseif char == string.byte("r") or char == string.byte("R") then discoveryProgress = 0 discoveryActive = true if SERVER_ADDR then sendCommand("RESET_DISCOVERY") end