-- 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