Гайды

Дракониевый реактор

Перед началом

Перед запуском убедитесь, что у вас уже установлено Хранилище.


Необходимые материалы для реактора:

Дополнительно для компьютера:

Выбор правильного места

Для установки реактора и хранилища нужно выбрать один чанк. Это важно, так как если они будут находиться в разных чанках, реактор может взорваться из-за особенностей работы сервера. Чтобы отобразить границы чанков, нажмите F9.

Важно: Ничто не должно выходить за пределы выбранного чанка, даже один блок, иначе это может вызвать проблемы.


Сборка реактора
Пример

reactor_1.png

В итоге должно получиться как на картинке:

reactor_2.png

Если в стабилизаторе появляется надпись "неправильная структура", проверьте расположение блоков.


Подключение гейтов и компьютера

reactor_3.png

Примечание: У гейтов есть две точки подключения энергии: синий вход и оранжевый выход. Следите за тем, чтобы подключение было правильным.

reactor_4.png


Настройка трансиверов

1. Разместите и подключите трансиверы, как на изображении:

reactor_5.png

2. Переключите режимы трансиверов с "выход" на "вход" на оранжевых точках (на выходах гейтов и реактора). Для этого измените режимы связывателя кристаллов, нажав Shift + ПКМ по воздуху до появления нужного режима в чате:

reactor_6.png

3. Подключаем реактор к хранилищу

reactor_8.png

Важно: для каждого реактора необходимо ставить отдельный набор трансиверов
Важно: нужно переключить режим трансивера с "выход" на "вход".


Действия на компьютере
  1. Устанавливаем операционную систему:

    install
    Y [Enter]

    Ждем предложения о перезагрузке:

    Y [Enter]
  2. Добавляем пользователей:

    useradd <ник>

    Сначала прописываем себя, потом человека, которому ставим систему.

  3. Загружаем скрипт:

    pastebin get YmiZmuzQ reactor.lua (В последнее время наблюдаются проблемы с доступом)
    или
    wget https://raw.githubusercontent.com/nikita2007558/hiTechClassic/refs/heads/main/reactor.lua
  4. Запускаем программу:

    reactor.lua
  5. Настройка программы: При первом запуске мы увидим этот экран:

reactor_9.png

Здесь нужно выбрать адаптеры, к которым подключены гейты. Просто нажмите в любом порядке на адреса гейтов. 
Затем проверяем гейты. В левом (подключённом к инжектору) гейте должна быть надпись:

reactor_10.png


Если это не так, нажмите кнопку "Изменить" и прожмите адреса в другом порядке, затем снова проверяйте гейты. Если всё правильно, прожимаем кнопку "Продолжить".


Запуск реактора
  1. Откройте стабилизатор и положите в реактор 8 драконьих блоков.
  2. Откройте компьютер, нажмите кнопку "Зарядить". Теперь ждём, пока реактор зарядится — для этого в хранилище должно быть около 1B энергии.
  3. Когда реактор зарядится и нагреется до 2000 градусов, появится кнопка "Запустить реактор". Нажмите её.
  4. Ждите, пока реактор нагреется до 6000-7000 градусов, затем нажмите кнопку "Автономный режим". Температура должна стабилизироваться на уровне около 8000 градусов.

Контроллер ядра энергии

Сборка схемы
Для начала нужно собрать схему, как на изображении:

gate_1.webp

Действия на компьютере
1. Устанавливаем операционную систему:
install
Y [Enter]
Ждем предложения о перезагрузке:
Y [Enter]
2. Добавляем пользователей:
useradd <ник>

Сначала прописываем себя, потом человека, которому ставим систему.

3. Загружаем скрипт:
pastebin get i7tz8mPu gate.lua (В последнее время наблюдаются проблемы с доступом)
или
wget https://raw.githubusercontent.com/nikita2007558/hiTechClassic/refs/heads/main/gate.lua
4. Открываем файл .shrc для редактирования:
edit .shrc
Опускаемся в самый низ и создаем новую строчку. Туда прописываем:
gate.lua
Пример, как это должно выглядеть на изображении:

gate_2.webp

5. Сохраняем файл:
   - Нажимаем Ctrl + S и Ctrl + W.


6. Теперь редактируем файл 1.lua:
edit gate.lua
Ищем 4-ю строчку и меняем значение:
maxCoreEnergy = 1000 * 10^9

Можно указать любое желаемое значение. Где `1000` – это миллиарды (В) в ядре.

7. Сохраняем изменения:
   - Нажимаем Ctrl + S и Ctrl + W.
8. Перезагружаем систему:
reboot
Теперь программа готова к работе.


Ритуал по пробуждению дракониевых блоков

Альтернативно можно крафтить в Дракониевом сборщике 1 уровня.

Для ритуала нам понадобится:

Ставим 4 Заряженных дракониевых блоков вместе:

1 (78).png

Ставим динамит:

1 (79).png

Кидаем Сердце дракона рядом с динамитом:

1 (80).png

Активируем динамит:

1 (81).png

После взрыва динамита сердце активируется: увеличится в размере и начнёт парить.
Будет небольшой промежуток времени за который нужно успеть кинуть к сердцу 16 Дракониевых ядер:

Внимание! Не подходите слишком близко к сердцу, чтобы случайно его не подобрать, кидайте ядра сверху.

1 (83).png

Сердце поглотит ядра и начнет очень быстро вращаться, после взрыва появятся Пробужденные дракониевые блоки:

1 (84).png

Ритуал по призыву дракона

Призыв дракона — важная часть игры. После его убийства выпадает Сердце дракона, а также на вершине портала в Верхний мир появляется Яйцо дракона. Дополнительно вы получаете опыт и Дракниевую пыль.

Дракона можно убить и обычным оружием, однако важно помнить: во время призыва начинают бить молнии, и они могут попасть по игроку. Спасти от урона и смерти способна только Драконья броня и выше. Также учтите, что у дракона большой запас здоровья, и он регенерирует его от Кристаллов Края. Лучшее решение — уничтожить кристаллы сразу после призыва.


image.png

 

image.png

Дракон Края

Кристалл Края

Ритуал проводится только а Энде.
Список предметов для воскрешения:

Есть две версии призыва дракона:

Каноничная

Ставим все как на скриншотах и нажимаем ОДИН раз ПКМ по Камню воскрешения.
Начнут бить молнии, осторожно!
Дракон появится не сразу, нужно подождать.

2025-11-29_04.34.43.png

2025-11-29_04.34.34.png

Упрощенная

Ставим все как на скриншотах и нажимаем ОДИН раз ПКМ по Камню воскрешения.
Начнут бить молнии, осторожно!
Дракон появится не сразу, нужно подождать.

2025-11-29_04.42.30.png

2025-11-29_04.42.41.png

Мультиблочное хранилище RF энергии

Ниже этапы сборки всех уровней мультиблочного хранилища.
Для постройки потребуются блоки красного камня, дракониевые блоки, Ядро энергии и 4 Генератора частиц.
После сбора конструкции нажми Шифт+ПКМ по одному из Генераторов частиц и хранилище активируется.
Для ввода/вывода энергии используй Энергетические пилоны.

Уровень 1

Хранит RF энергии - 45,5 млн.

1.png


2.png

Уровень 2

Хранит RF энергии - 273 млн.
Дракониевый блок - 6 шт.

1.png


2.png


3.png

Уровень 3

Хранит RF энергии - 1.64 млрд.
Дракониевый блок - 26 шт.

1.png


2.png


3.png

Уровень 4

Хранит RF энергии - 9.88 млрд.
Дракониевый блок - 54 шт.
Блок красного камня - 26 шт.

1.png


2.png


3.png


4.png

Уровень 5

Хранит RF энергии - 59.3 млрд.
Дракониевый блок - 98 шт.
Блок красного камня - 80 шт.

1.png


2.png


3.png


4.png


5.png

Уровень 6

Хранит RF энергии - 356 млрд.
Дракониевый блок - 150 шт.
Блок красного камня - 133 шт.

1.png


2.png


3.png


4.png


5.png


6.png

Уровень 7

Хранит RF энергии - 2.14 трлн.
Дракониевый блок - 218 шт.
Блок красного камня - 328 шт.

1.png


2.png


3.png


4.png


5.png


6.png


7.png

5 дракониевых реакторов в одном ПК

Этот гайд поможет вам обуздать мощь сразу пяти дракониевых реакторов. Система на базе OpenComputers возьмет на себя все расчеты, предотвращая взрывы и обеспечивая стабильный приток энергии на долгие дни.

1. Список компонентов

Для стабильной работы системы управления пятью реакторами нам понадобится мощный сервер и специфическое оборудование Draconic Evolution.

Контрольный узел (Сервер)
Компонент Кол-во Примечание
Серверная стойка 1 шт Основа системы
Сервер (Уровень 3) 1 шт Максимальная мощность
Монитор (Уровень 3) 6 шт Для сетки 3х2
Клавиатура 1 шт Ставится на монитор
Видеокарта (Уровень 3) 1 шт Вставляется в сервер
Процессор (ЦП) (Уровень 3) 1 шт Вставляется в сервер
Память (Уровень 3.5) 4 шт Для работы с 5-ю реакторами
Компонентная шина (Ур. 3) 3 шт Для подключения всех адаптеров
Жесткий диск (4МВ) 1 шт Уровень 3
Дисковод для стойки 1 шт Для установки ОС
EEPROM (Lua BIOS) 1 шт Прошивка
Преобразователь энергии 1 шт Питание сервера
Кабель 6 шт Соединение компонентов
Адаптер 5 шт По одному на каждый реактор
Реакторная установка (на 5 реакторов)
  1. Ядро энергии: уже настроенное (см. предыдущий гайд).

  2. Энергетический пилон: 2 шт.

  3. Флаксовый гейт: 10 шт.

  4. Стабилизатор реактора: 20 шт.

  5. Реакторный инжектор энергии: 5 шт.

  6. Драконье ядро реактора: 5 шт.

  7. Криостабилизированная труба: 20 шт.

  8. Пробужденный дракониевый блок: 40 шт (топливо).

2. Установка реакторов

Физическая сборка

Первым делом нам нужно ядро энергии с программой стабилизации ядра - гайд тут.

Место под реакторы

2026-03-18_02.52.59.png

Ставим реакторы примерно так (не обязательно ставить все 5 сразу, программа работает одинаково и с одним реактором и с пятью)

Установка реакторов

2026-03-18_02.59.01.png

Стабилизаторы

2026-03-18_03.04.24.png

Топливо

Dmtimage.png

image.png

Имеем такую картину

Реакторы с топливом

2026-03-18_03.10.46.png

3. Настройка сервера

Сборка и подключение
Вид спереди

2026-03-18_03.14.00.png

Вид сзади

2026-03-18_03.16.40.png

image.png

image.png

image.png

image.png

Программная часть

Включите сервер и выполните следующие шаги:

install

image.png

Нажимайте Enter, подтверждая установку. В конце система предложит перезагрузку - снова нажмите Enter.

image.png

Безопасность и автозапуск

Добавьте себя в список администраторов (команда чувствительна к регистру):

useradd ник

image.png

Это мы делали для того чтобы ПК могли пользоваться только вписанные в него пользователи.

Настройте автозапуск программы при включении ПК:

edit .shrc

и дописываем в самом конце такую строку

reactor.lua

image.png

И нажимаем Ctrl + S (сохранить) и Ctrl + W (закрыть).

Создание кода

Создайте файл программы:

edit reactor.lua

Вставьте код. Код очень длинный, вставляйте его частями (по 250 строк) из-за ограничений буфера обмена.

Код
local component = require("component")
local event = require("event")
local serialization = require("serialization")
local filesystem = require("filesystem")
local computer = require("computer")
local unicode = require("unicode")
local term = require("term")
local gpu = component.gpu
 
-- === АВТОМАТИЧЕСКАЯ НАСТРОЙКА РАЗРЕШЕНИЯ ===
local maxW, maxH = gpu.maxResolution()
gpu.setResolution(maxW, maxH)
local W, H = gpu.getResolution()
 
--------------------------------------------------------------------------------
-- [КОНФИГУРАЦИЯ]
--------------------------------------------------------------------------------
local CFG = {
  filename = "/home/reactors.cfg",
  maxReactors = 5,
  updateInterval = 1.0,
  
  -- Настройки по умолчанию
  defaultTargetTemp = 8000, 
  
  targetShield = 25,    
  startShield = 50.0,   
  startSat = 50.0,      
  warmupTemp = 2000,    
  
  minFuelPct = 10.0,    
  initialFlow = 300000, 
  chargeFlow = 1000000, 
  maxDrainFlow = 2000000 
}
 
local COLORS = {
  bg = 0x111111, tileBg = 0x222222, tileHeader = 0x333333,
  text = 0xEEEEEE, label = 0x999999,
  good = 0x55FF55, warn = 0xFFAA00, bad = 0xFF5555,
  energy = 0x00AAFF, sat = 0xFFAA00, fuel = 0xFFFF55,
  btn = 0x444444, btnActive = 0x006699
}
 
--------------------------------------------------------------------------------
-- [ПЕРЕМЕННЫЕ]
--------------------------------------------------------------------------------
local reactors = {}
local buttons = {}
local running = true
local wizard = { active = false, step = 1, coreAddr = nil, inAddr = nil, outAddr = nil, candidates = {} }
local needFullRedraw = true
 
--------------------------------------------------------------------------------
-- [ЛОГИКА]
--------------------------------------------------------------------------------
local function saveConfig()
  local data = {}
  for _, r in ipairs(reactors) do
    table.insert(data, { r=r.proxy.address, i=r.gateIn.address, o=r.gateOut.address, auto=r.auto, t=r.targetTemp })
  end
  local f = io.open(CFG.filename, "w")
  f:write(serialization.serialize(data))
  f:close()
end
 
local function safeCall(func, ...)
  local ok, res = pcall(func, ...)
  return ok and res or nil
end
 
local function getFlow(proxy)
  if not proxy then return 0 end
  return tonumber(safeCall(proxy.getSignalLowFlow)) or 0
end
 
local function setFlow(proxy, val)
  if proxy then safeCall(proxy.setSignalLowFlow, math.floor(val)) end
end
 
local function tickReactors()
  for _, r in ipairs(reactors) do
    local info = safeCall(r.proxy.getReactorInfo)
    if info then
      r.isOnline = true
      r.info = info
      
      if not r.targetTemp then r.targetTemp = CFG.defaultTargetTemp end
      
      local status = tostring(info.status or "off"):lower()
      local temp = tonumber(info.temperature) or 0
      local drain = tonumber(info.fieldDrainRate) or 0
      local gen = tonumber(info.generationRate) or 0
      local field = tonumber(info.fieldStrength) or 0
      local maxField = tonumber(info.maxFieldStrength) or 1
      local fuel = tonumber(info.fuelConversion) or 0
      local maxFuel = tonumber(info.maxFuelConversion) or 1
      local sat = tonumber(info.energySaturation) or 0
      local maxSat = tonumber(info.maxEnergySaturation) or 1
      
      r.temp = temp
      r.gen = gen
      r.drain = drain
      r.fieldPct = (maxField > 0) and (field/maxField)*100 or 0
      r.fuelPct = (maxFuel > 0) and (1 - (fuel/maxFuel)) * 100 or 0
      r.satPct = (maxSat > 0) and (sat/maxSat) * 100 or 0
      r.statusStr = status:upper()
      r.maxFuel = maxFuel

      -- === ЛОГИКА ЗАПУСКА ===
      if r.isStarting then
        r.auto = false 
        setFlow(r.gateOut, 0)
        
        if maxFuel == 0 or r.fuelPct <= 0 then
             r.modeStr = "НЕТ ТОПЛИВА"
        else
             if status == "cold" or status == "cooling" or status == "offline" or status == "stopping" then
                  r.modeStr = "ИНИЦИАЛИЗАЦИЯ"
                  safeCall(r.proxy.chargeReactor)
             elseif status == "charging" then
                  if r.fieldPct < CFG.startShield then r.modeStr = "ЗАРЯДКА ПОЛЯ"
                  else r.modeStr = "ЗАРЯДКА НАСЫЩ." end
                  if r.fieldPct >= CFG.startShield and r.satPct >= CFG.startSat then
                       safeCall(r.proxy.activateReactor); r.modeStr = "АКТИВАЦИЯ..."
                  end
             elseif status == "charged" then
                  r.modeStr = "ГОТОВ К ПУСКУ"
                  safeCall(r.proxy.activateReactor)
             elseif status == "warming_up" or status == "warming up" then
                  r.modeStr = "ПРОГРЕВ > " .. CFG.warmupTemp
                  if temp >= CFG.warmupTemp then r.isStarting = false; r.auto = true end
             elseif status == "running" or status == "online" then
                  r.isStarting = false; r.auto = true
             end
        end
      end

      -- === УПРАВЛЕНИЕ ЩИТОМ ===
      if status == "charging" or status == "warming_up" or status == "warming up" then
        setFlow(r.gateIn, CFG.chargeFlow); r.shieldCost = CFG.chargeFlow
      elseif status ~= "off" and status ~= "cold" then
        if r.fieldPct < 15 then setFlow(r.gateIn, CFG.chargeFlow); r.shieldCost = CFG.chargeFlow
        else
             local targetIn = math.ceil(drain / (1 - (CFG.targetShield / 100)))
             local safeIn = math.max(10000, targetIn) 
             setFlow(r.gateIn, safeIn); r.shieldCost = safeIn
        end
      else setFlow(r.gateIn, 0); r.shieldCost = 0 end
 
      -- === УПРАВЛЕНИЕ ВЫХОДОМ ===
      if (status == "stopping" or status == "cooling") and not r.isStarting then
           r.auto = false
           r.modeStr = "ОСТЫВАНИЕ"
           if r.satPct > 10 then setFlow(r.gateOut, CFG.maxDrainFlow)
           else setFlow(r.gateOut, CFG.initialFlow) end
      
      elseif r.auto then
        if r.fuelPct <= CFG.minFuelPct and (status == "running" or status == "online") then
           safeCall(r.proxy.stopReactor); r.auto = false; r.isStarting = false
           r.modeStr = "ТОПЛИВО < 10%"; saveConfig()
        elseif status == "running" or status == "online" then
            local target = r.targetTemp
            local crit = target + 150 
            
            if temp > crit then
               r.modeStr = "!!! ПЕРЕГРЕВ !!!"
               setFlow(r.gateOut, CFG.initialFlow) 
            elseif temp > target then
               local overheat = temp - target 
               local range = crit - target 
               local penalty = (overheat / range) * 0.8 
               local multiplier = 1.0 - penalty
               if multiplier < 0.1 then multiplier = 0.1 end
               r.modeStr = string.format("УДЕРЖАНИЕ %.0f", temp)
               setFlow(r.gateOut, math.max(CFG.initialFlow, gen * multiplier))
            else
               local underheat = target - temp
               local aggressive = 1.0 + (underheat * 0.0005)
               if aggressive > 2.0 then aggressive = 2.0 end
               r.modeStr = string.format("НАГРЕВ -> %.0f", target)
               setFlow(r.gateOut, math.max(CFG.initialFlow, gen * aggressive))
            end
        else if not r.isStarting then r.modeStr = "ОЖИДАНИЕ" end end
      else 
        if not r.isStarting and status ~= "stopping" and status ~= "cooling" then
             if r.fuelPct <= CFG.minFuelPct then r.modeStr = "ТОПЛИВО < MIN"
             elseif status == "cold" or status == "offline" then r.modeStr = "СТОП"
             else r.modeStr = "РУЧНОЙ" end
             if getFlow(r.gateOut) > CFG.initialFlow then setFlow(r.gateOut, CFG.initialFlow) end
        end
      end
    else r.isOnline = false end
  end
end
 
--------------------------------------------------------------------------------
-- [ГРАФИКА]
--------------------------------------------------------------------------------
local function rect(x, y, w, h, col) gpu.setBackground(col); gpu.fill(x, y, w, h, " ") end
local function text(x, y, str, fg, bg) if bg then gpu.setBackground(bg) end; gpu.setForeground(fg); gpu.set(x, y, str) end
local function center(x, y, w, str, fg, bg) if bg then gpu.setBackground(bg) end; gpu.fill(x, y, w, 1, " "); local px = x + math.floor((w - unicode.len(str))/2); if fg then gpu.setForeground(fg) end; gpu.set(px, y, str) end
local function drawVal(x, y, w, str, fg, bg) if bg then gpu.setBackground(bg) end; gpu.fill(x, y, w, 1, " "); if fg then gpu.setForeground(fg) end; gpu.set(x, y, str) end
local function fmt(num) if num > 1000000000 then return string.format("%.2f G", num/1000000000) elseif num > 1000000 then return string.format("%.2f M", num/1000000) elseif num > 1000 then return string.format("%.0f K", num/1000) else return tostring(math.floor(num)) end end
local function progressBar(x, y, w, pct, colBar, colBg) rect(x, y, w, 1, colBg); local barW = math.floor(w * (pct/100)); if barW > 0 then rect(x, y, barW, 1, colBar) end end
local function btn(id, x, y, w, h, str, bg, fg) rect(x, y, w, h, bg); center(x, y + math.floor(h/2), w, str, fg or COLORS.text, bg); buttons[id] = {x=x, y=y, w=w, h=h} end
 
local function drawStaticInterface()
  gpu.setBackground(COLORS.bg); term.clear()
  local margin = 1; local areaH = math.floor(H * 0.66); local tileW = math.floor(W / 5) - margin; local tileH = areaH - 2
  for i = 1, 5 do
    local x = 1 + (i-1) * (tileW + margin); local y = 2
    rect(x, y, tileW, tileH, COLORS.tileBg); rect(x, y, tileW, 3, COLORS.tileHeader)
    center(x, y+1, tileW, "РЕАКТОР #" .. i, COLORS.text, COLORS.tileHeader)
    local r = reactors[i]
    if r then
      local tBg = COLORS.tileBg
      text(x+2, y+9, "ТОПЛИВО:", COLORS.label, tBg); text(x+2, y+12, "ЩИТ ПОЛЯ:", COLORS.label, tBg)
      text(x+2, y+15, "НАСЫЩЕНИЕ:", COLORS.label, tBg)
      local curY = y+18
      text(x+2, curY, "ВЫРАБОТКА:", COLORS.label, tBg); text(x+2, curY+1, "ЗАТРАТЫ:", COLORS.label, tBg)
      text(x+2, curY+2, "ИТОГ:", COLORS.label, tBg); text(x+2, curY+4, "ВЫХОД:", COLORS.label, tBg)
    else center(x, y+10, tileW, "СЛОТ СВОБОДЕН", COLORS.label, COLORS.tileBg) end
  end
  local areaY = math.floor(H * 0.66) + 2; local areaH = H - areaY + 1
  rect(1, areaY, W, areaH, 0x181818); rect(1, areaY, W, 1, 0x444444)
  local col2 = 40; local col3 = 80; local line1 = areaY + 2; local line2 = areaY + 4
  text(col2, line1, "ОБЩАЯ ГЕНЕРАЦИЯ:", COLORS.label, 0x181818); text(col2, line2, "РАСХОД НА ЩИТЫ:", COLORS.label, 0x181818); text(col3, line1, "ЧИСТАЯ ПРИБЫЛЬ:", COLORS.energy, 0x181818)
  local crY = H - 3; local crX = W - 40
  text(crX, crY, "Разработчик: DesOope", COLORS.label, 0x181818); text(crX, crY+1, "Проект: McSkill", COLORS.label, 0x181818); text(crX, crY+2, "Сервер: HitechClassic 1.7.10", COLORS.label, 0x181818)
  btn("exit", 3, H-2, 10, 1, "ВЫХОД", COLORS.bad)
end
 
local function updateLiveDisplay()
  -- ИСПРАВЛЕНИЕ: Очищаем список кнопок, чтобы удалить фантомные старые кнопки
  buttons = {} 
  -- Сразу возвращаем кнопку выхода, так как мы стерли всё
  buttons["exit"] = {x=3, y=H-2, w=10, h=1}

  local margin = 1; local areaH = math.floor(H * 0.66); local tileW = math.floor(W / 5) - margin; local tileH = areaH - 2
  local totalGen = 0; local totalCost = 0; local totalProfit = 0; local activeCount = 0
  for _, r in ipairs(reactors) do
     if r and r.isOnline and (r.statusStr == "ONLINE" or r.statusStr == "RUNNING") then
        activeCount = activeCount + 1; local p = (r.gen or 0) - (r.shieldCost or 0)
        totalGen = totalGen + (r.gen or 0); totalCost = totalCost + (r.shieldCost or 0); totalProfit = totalProfit + p
     end
  end
  for i = 1, 5 do
    local x = 1 + (i-1) * (tileW + margin); local y = 2; local r = reactors[i]
    if r then
      if r.isOnline then
        local curProfit = (r.gen or 0) - (r.shieldCost or 0)
        local stCol = (r.statusStr == "ONLINE" or r.statusStr == "RUNNING") and COLORS.good or COLORS.warn
        if r.statusStr == "OFFLINE" then stCol = COLORS.bad end
        if r.statusStr == "COOLING" or r.statusStr == "STOPPING" then stCol = COLORS.energy end
        center(x, y+4, tileW, r.statusStr, stCol, COLORS.tileBg)
        local modeCol = (r.auto and COLORS.energy or COLORS.warn)
        if r.modeStr == "!!! АВАРИЯ !!!" then modeCol = COLORS.bad end
        if r.isStarting then modeCol = COLORS.good end
        center(x, y+5, tileW, "[" .. (r.modeStr or "-") .. "]", modeCol, COLORS.tileBg)
        
        local tCol = r.temp > (r.targetTemp + 50) and COLORS.warn or COLORS.good
        if r.temp > (r.targetTemp + 150) then tCol = COLORS.bad end
        
        local tempStr = string.format("%.0f C", r.temp)
        local targetStr = string.format("(Цель: %d)", r.targetTemp or 8000)
        
        center(x, y+7, tileW, tempStr, tCol, COLORS.tileBg)
        center(x, y+8, tileW, targetStr, COLORS.label, COLORS.tileBg)
        
        local cx = x + math.floor(tileW/2)
        btn("tdn_"..i, cx - 9, y+7, 3, 1, "-", COLORS.btn, COLORS.text)
        btn("tup_"..i, cx + 6, y+7, 3, 1, "+", COLORS.btn, COLORS.text)
 
        local fuelCol = r.fuelPct <= CFG.minFuelPct and COLORS.bad or COLORS.fuel
        drawVal(x+tileW-6, y+9, 5, string.format("%d%%", r.fuelPct), fuelCol, COLORS.tileBg)
        progressBar(x+2, y+10, tileW-4, r.fuelPct, fuelCol, 0x444400)
        drawVal(x+tileW-6, y+12, 5, string.format("%d%%", r.fieldPct), COLORS.energy, COLORS.tileBg)
        progressBar(x+2, y+13, tileW-4, r.fieldPct, COLORS.energy, 0x002244)
        local satCol = r.satPct > 50 and COLORS.warn or COLORS.sat
        drawVal(x+tileW-6, y+15, 5, string.format("%d%%", r.satPct), satCol, COLORS.tileBg)
        progressBar(x+2, y+16, tileW-4, r.satPct, satCol, 0x332200)
        local curY = y+18
        drawVal(x+tileW-11, curY, 9, fmt(r.gen), COLORS.good, COLORS.tileBg)
        drawVal(x+tileW-11, curY+1, 9, fmt(r.shieldCost), COLORS.warn, COLORS.tileBg)
        drawVal(x+tileW-11, curY+2, 9, fmt(curProfit), COLORS.energy, COLORS.tileBg)
        drawVal(x+tileW-11, curY+4, 9, fmt(getFlow(r.gateOut)), COLORS.text, COLORS.tileBg)
        local by = y + tileH - 4
        local btnText, btnColor, btnAct
        if r.auto or r.isStarting or (r.statusStr == "RUNNING" or r.statusStr == "ONLINE") then
            btnText = "СТОП"; btnColor = COLORS.bad; btnAct = "stop"; if r.isStarting then btnText = "ОТМЕНА" end
        else btnText = "ПУСК"; btnColor = COLORS.good; btnAct = "start" end
        btn(btnAct.."_"..i, x+2, by, tileW-4, 3, btnText, btnColor)
        btn("del_"..i, x+tileW-3, y, 3, 1, "X", COLORS.bad, COLORS.text)
      else
        center(x, y+10, tileW, "НЕТ СВЯЗИ", COLORS.bad, COLORS.tileBg); center(x, y+11, tileW, "ПРОВЕРЬ КАБЕЛЬ", COLORS.label, COLORS.tileBg)
        btn("del_"..i, x+2, y+tileH-2, tileW-4, 1, "УДАЛИТЬ", COLORS.bad)
      end
    else if i == #reactors + 1 then btn("add", x+4, y+12, tileW-8, 3, "ДОБАВИТЬ", COLORS.good) end end
  end
  local areaY = math.floor(H * 0.66) + 2; local col1 = 3; local col2 = 40; local col3 = 80; local line1 = areaY + 2; local line2 = areaY + 4; local bgF = 0x181818
  gpu.setBackground(bgF); gpu.fill(col1, line1, 30, 1, " "); gpu.set(col1, line1, "АКТИВНО: " .. activeCount .. " / " .. CFG.maxReactors)
  drawVal(col2+18, line1, 15, fmt(totalGen).." RF/t", COLORS.good, bgF); drawVal(col2+18, line2, 15, fmt(totalCost).." RF/t", COLORS.warn, bgF); drawVal(col3, line2, 15, fmt(totalProfit).." RF/t", COLORS.energy, bgF)
end
 
local function drawWizard()
  gpu.setBackground(COLORS.bg); term.clear(); local cx = math.floor(W/2); local cy = math.floor(H/2)
  center(1, cy-7, W, "=== БЫСТРАЯ НАСТРОЙКА ===", COLORS.energy, COLORS.bg)
  if wizard.step == 1 then
    center(1, cy-4, W, "Поиск свободного ядра...", COLORS.text, COLORS.bg)
    if wizard.coreAddr then
       center(1, cy-3, W, "Ядро найдено: " .. wizard.coreAddr:sub(1,8).."...", COLORS.good, COLORS.bg)
       center(1, cy-1, W, "ПОИСК ГЕЙТОВ...", COLORS.energy, COLORS.bg)
       center(1, cy+1, W, "1. Подойдите к гейту ЩИТА (ВХОД)", COLORS.label, COLORS.bg)
       center(1, cy+2, W, "2. Установите на нем поток: 100 RF/t", COLORS.warn, COLORS.bg)
       center(1, cy+3, W, "Второй гейт определится АВТОМАТИЧЕСКИ.", COLORS.good, COLORS.bg)
       center(1, cy+5, W, "Жду сигнала 100...", COLORS.text, COLORS.bg)
    else center(1, cy-2, W, "СВОБОДНОЕ ЯДРО НЕ НАЙДЕНО!", COLORS.bad, COLORS.bg) end
  end
  btn("cancel_wiz", cx-8, cy+7, 16, 1, "ОТМЕНА", COLORS.bad)
end
 
local function tickWizard()
  if not wizard.active then return end
  if not wizard.coreAddr then
    for addr, type in component.list("draconic_reactor") do
      local used = false; for _, r in ipairs(reactors) do if r.proxy.address == addr then used = true; break end end
      if not used then wizard.coreAddr = addr; drawWizard(); break end
    end
  end
  if not wizard.coreAddr then return end
  if #wizard.candidates == 0 then
    for addr, type in component.list("flux_gate") do
      local used = false; for _, r in ipairs(reactors) do if r.gateIn.address == addr or r.gateOut.address == addr then used = true; break end end
      if not used then table.insert(wizard.candidates, component.proxy(addr)) end
    end
  end
  for _, gate in ipairs(wizard.candidates) do
    local flow = tonumber(gate.getSignalLowFlow()) or 0
    if flow == 100 then
      wizard.inAddr = gate.address
      for _, other in ipairs(wizard.candidates) do if other.address ~= wizard.inAddr then wizard.outAddr = other.address; break end end
      if wizard.outAddr then
        computer.beep(1000, 0.2)
        table.insert(reactors, { proxy = component.proxy(wizard.coreAddr), gateIn = component.proxy(wizard.inAddr), gateOut = component.proxy(wizard.outAddr), auto = false, isStarting = false, isOnline = true, temp=0, gen=0, shieldCost=0, fuelPct=0, fieldPct=0, satPct=0, targetTemp=CFG.defaultTargetTemp })
        saveConfig(); wizard.active = false; needFullRedraw = true; tickReactors(); break
      end
    end
  end
end
 
local function handleTouch(x, y)
  for id, b in pairs(buttons) do
    if x >= b.x and x < b.x + b.w and y >= b.y and y < b.y + b.h then
      if id == "exit" then running = false
      elseif id == "add" then wizard.active = true; wizard.step = 1; wizard.candidates = {}; wizard.coreAddr = nil; drawWizard()
      elseif id == "cancel_wiz" then wizard.active = false; needFullRedraw = true
      else
        local act, idx = id:match("(%a+)_(%d+)"); idx = tonumber(idx); local r = reactors[idx]
        if r then
          if act == "del" then table.remove(reactors, idx); saveConfig(); needFullRedraw = true
          elseif act == "start" then r.isStarting = true; r.auto = false; computer.beep(1000, 0.1); updateLiveDisplay()
          elseif act == "stop" then safeCall(r.proxy.stopReactor); r.auto = false; r.isStarting = false; r.modeStr = "СТОП"; saveConfig(); updateLiveDisplay()
          elseif act == "tup" then r.targetTemp = (r.targetTemp or 8000) + 50; if r.targetTemp > 20000 then r.targetTemp = 20000 end; saveConfig(); computer.beep(1200,0.05); updateLiveDisplay()
          elseif act == "tdn" then r.targetTemp = (r.targetTemp or 8000) - 50; if r.targetTemp < 2000 then r.targetTemp = 2000 end; saveConfig(); computer.beep(1000,0.05); updateLiveDisplay()
          end
        end
      end
      computer.beep(800, 0.05); break
    end
  end
end
 
gpu.setResolution(maxW, maxH); gpu.setBackground(COLORS.bg); term.clear()
if filesystem.exists(CFG.filename) then
  local f = io.open(CFG.filename, "r"); local data = serialization.unserialize(f:read("*a")); f:close()
  if data then for _, d in ipairs(data) do table.insert(reactors, { proxy=component.proxy(d.r), gateIn=component.proxy(d.i), gateOut=component.proxy(d.o), auto=d.auto, isOnline=true, isStarting=false, targetTemp=(d.t or CFG.defaultTargetTemp) }) end end
end
needFullRedraw = true
local lastTick = 0

-- === ГЛАВНЫЙ ЦИКЛ ===
while running do
  if wizard.active then tickWizard(); local _, _, x, y = event.pull(0.2, "touch"); if x then handleTouch(x, y) end
  else
    local e, _, x, y = event.pull(0.05, "touch"); if e then handleTouch(x, y) end
    if computer.uptime() - lastTick > CFG.updateInterval then tickReactors(); if needFullRedraw then drawStaticInterface(); needFullRedraw = false end; updateLiveDisplay(); lastTick = computer.uptime() end
  end
end
gpu.setResolution(maxW, maxH); gpu.setBackground(0x000000); term.clear()

Перезагрузите компьютер командой:

reboot

После перезагрузки мы видим такое окно программы

image.png

4. Подключение реакторов к сети

Гейты: Установите флаксовые гейты возле реакторов.

2026-03-18_03.37.58.png

Трубы: Проложите криостабилизированные трубы. 
Пилоны: Установите пилоны на трубы. Не забудьте активировать их стеклом снизу. Левый пилон переключите в режим приема энергии (ПКМ по синему ядру).

image.png

Трубы: Проложите криостабилизированные трубы с другой стороны.

image.png

Важно: С этой стороны трубы нужно разъединить гаечным ключом (например, Yeta из Ender IO), чтобы потоки не смешивались.
Наведитесь между труб и нажмите ПКМ держа в руках гаечный ключ.

image.png

image.png

Повторите так со всеми трубами с этой стороны

image.png

Инжекторы: Поставьте инжекторы на трубы, направив их в сторону реакторов.

image.png

Связь: Установите по очереди (смотреть ниже) по одному Адаптеру между гейтами каждого реактора и соедините их кабелями с сервером.

image.png

5. Запуск и эксплуатация

Вернитесь к монитору сервера и следуйте алгоритму:

image.png

На экране появится запрос. Подойдите к адаптеру нужного реактора и в его правом гейте вручную выставьте поток 100 RF/t.

image.png

image.png

Реактор автоматически отобразится в списке.

image.png

Повторите эту процедуру с оставшимися реакторами

image.png

После того как мы добавили все наши реакторы - жмем пуск и наблюдаем

Что произойдет дальше?

Программа автоматически зарядит щиты, поднимет насыщение, нагреет реакторы, запустит их и они будут работать на стабильных 8000 градусах. В таком режиме система может работать стабильно около 13 реальных дней.

Совет для профи: В программе можно менять рабочую температуру. Если выставить 9000 градусов на 5-10 часов, а потом вернуть на 8000 -  топливо выгорит быстрее, что позволит реактору выйти на высокие мощности в 1.5 - 2 млн RF/t. Но делайте это только на свой страх и риск.

image.png

Стабилизация Энергетического ядра Draconic Evolution через OpenComputers

Этот гайд поможет вам настроить систему автоматического контроля энергии. Программа следит за тем, чтобы в хранилище всегда оставался необходимый запас для работы реакторов, предотвращая их перегрев или остановку. Если энергии слишком много - компьютер увеличивает выход, если мало - ограничивает его.

1. Необходимые компоненты

Компьютер (OpenComputers)
Компонент Количество Примечание
Системный блок 1 шт Уровень 3
Монитор 6 шт Уровень 3 (для сборки 3х2)
Видеокарта 1 шт Уровень 3
Процессор (ЦП) 1 шт Уровень 3
Память (ОЗУ) 2 шт Уровень 3.5
Жесткий диск 1 шт Уровень 3 (4 Мб)
EEPROM 1 шт Прошивка Lua BIOS
Дискета 1 шт С установщиком OpenOS
Адаптер 1 шт Для связи с устройствами
Преобразователь энергии 1 шт Питание компьютера
Клавиатура 1 шт Для ввода команд
Ядро энергии
  1. Само ядро энергии (как его построить)
  2. Флаксовый гейт - 1шт
  3. Энергетический пилон - 1шт
  4. Криостабилизированная флаксовая труба - смотреть по ситуации

2. Подготовка и строительство

Первым делом включите отображение границ чанков (клавиша F9). Крайне важно строить всю конструкцию (ядро и компьютер) в пределах одного чанка, чтобы избежать ошибок при загрузке мира. Инструкция по сборке ядра тут. Схематика тут.

Скриншот ядра

2026-03-18_01.06.24.png

Скриншот установки пк и мониторов

2026-03-18_01.19.56.png

Нюансы сборки системного блока
  1. Lua BIOS: Обычный чистый EEPROM нужно объединить в сетке крафта с "Руководством OpenComputers".

  2. OpenOS: Обычную дискету также соедините с "Руководством OpenComputers", чтобы получить загрузочный диск с операционной системой.

image.png

3. Подключение компонентов

Скриншот подключения к ядру

2026-03-18_01.22.23.png

Настройка пилона и гейта

Важное уточнение: стекло на пилон следует ставить с той стороны пилона (верх, низ) которая смотрит в сторону центра ядра энергии. Это для тех, кто собирает схему не четко по инструкции. 

  1. Энергетический пилон: Установите его и поставьте блок стекла сверху для активации. Нажмите ПКМ по синему ядру внутри пилона, чтобы стрелки были направлены наружу. Это режим отдачи энергии.

  2. Флаксовый гейт: Ставьте строго по направлению потока. Фиолетовая сторона (вход) должна смотреть на пилон, оранжевая (выход) - в сторону ваших потребителей.

  3. Соединение: Соедините пилон и вход гейта Криостабилизированной трубой (у нее нет лимита передачи).

  4. Адаптер: Поставьте адаптер вплотную к Флаксовому гейту, чтобы компьютер мог им управлять.

Важно: Следите, чтобы ядро не переполнялось. Если реакторам некуда будет отдавать энергию, произойдет критический перегрев и взрыв. Выход гейта должен быть подключен к мощному потребителю (например, к Генераторам материи через конвертер).

image.png

4. Настройка программной части

Включите компьютер кнопкой в системном блоке.

image.png

Сначала мы видим загрузку, после чего у нас появляется вот такое окно

image.png

Установка ОС

После загрузки с дискеты введите команду:

install

image.png

Нажимайте Enter, подтверждая установку. В конце система предложит перезагрузку - снова нажмите Enter.

image.png

Безопасность и автозапуск

Добавьте себя в список администраторов (команда чувствительна к регистру):

useradd ник

image.png

Это мы делали для того чтобы ПК могли пользоваться только вписанные в него пользователи.

Настройте автозапуск программы при включении ПК:

edit .shrc

и дописываем в самом конце такую строку

gate.lua

image.png

И нажимаем Ctrl + S (сохранить) и Ctrl + W (закрыть).

Создание кода

Создайте файл программы:

edit gate.lua

Вставьте код. Код очень длинный, вставляйте его частями (по 250 строк) из-за ограничений буфера обмена.

Код (v2.0)
local component = require("component")
local event = require("event")
local computer = require("computer")
local unicode = require("unicode")
local term = require("term")
local keyboard = require("keyboard")
local gpu = component.gpu

--------------------------------------------------------------------------------
-- [КОНФИГУРАЦИЯ И ВЕРСИЯ]
--------------------------------------------------------------------------------
local VERSION = "v 2.0"

local CFG = {
  targetEnergy = 1000 * 10^9, -- Цель: 1 T RF
  aggressiveness = 0.03,      -- Плавность реакции стабилизации
  
  -- Настройки кнопок
  btnStep = 10^9,             -- Шаг (1B)
  btnStepCtrl = 10 * 10^9,    -- Шаг с Ctrl (10B)
  btnStepShift = 100 * 10^9   -- Шаг с Shift (100B)
}

local COLORS = {
  bg = 0x111111, tileBg = 0x222222, tileHeader = 0x333333,
  text = 0xEEEEEE, label = 0x999999,
  good = 0x55FF55, warn = 0xFFAA00, bad = 0xFF5555,
  energy = 0x00AAFF, sat = 0xFFAA00, fuel = 0xFFFF55,
  btn = 0x444444, btnActive = 0x006699,
  graphLine = 0x444444,
  black = 0x000000
}

-- Проверка компонентов
if not component.isAvailable("draconic_rf_storage") then 
  print("ОШИБКА: Нет ядра (draconic_rf_storage)!") 
  os.exit() 
end
if not component.isAvailable("flux_gate") then 
  print("ОШИБКА: Нет гейта (flux_gate)!") 
  os.exit() 
end

local rfStorage = component.draconic_rf_storage
local fluxGate = component.flux_gate

local W, H = gpu.maxResolution()
gpu.setResolution(W, H)

--------------------------------------------------------------------------------
-- [ПЕРЕМЕННЫЕ И РАСЧЕТ МАКЕТА]
--------------------------------------------------------------------------------
local running = true
local buttons = {}

-- === НАСТРОЙКА СИММЕТРИИ ===
local M = 2 -- Отступ (Margin) со всех сторон
local panelH = 9 -- Высота верхних панелей

-- Расчет координат
local totalW = W - (M * 3) 
local panelW = math.floor(totalW / 2)

-- Левая панель
local x1 = M
local y1 = M

-- Правая панель
local x2 = x1 + panelW + M
local y2 = M

-- График
local graphX = M
local graphY = y1 + panelH + 1
local graphW = W - (M * 2)
local graphH = H - graphY - M - 2 

local energyHistory = {} 
for i = 1, graphW do energyHistory[i] = 0 end

local maxCoreCapacity = rfStorage.getMaxEnergyStored()

-- Фильтр (Плавность)
local filVal = 0
local function expRunningAvg(newVal)
  if math.abs(newVal - filVal) > 10^15 then filVal = newVal end
  filVal = filVal + ((newVal - filVal) * 0.05) 
  return filVal
end

--------------------------------------------------------------------------------
-- [ГРАФИКА - ФУНКЦИИ]
--------------------------------------------------------------------------------
local function rect(x, y, w, h, col) 
  gpu.setBackground(col)
  gpu.fill(x, y, w, h, " ") 
end

local function text(x, y, str, fg, bg) 
  if bg then gpu.setBackground(bg) end
  gpu.setForeground(fg)
  gpu.set(x, y, str) 
end

local function center(x, y, w, str, fg, bg)
  if bg then gpu.setBackground(bg) end
  gpu.fill(x, y, w, 1, " ") 
  local px = x + math.floor((w - unicode.len(str))/2)
  if fg then gpu.setForeground(fg) end
  gpu.set(px, y, str)
end

-- ВНИМАНИЕ: Эта функция очищает всю строку под собой.
local function textRight(x, y, w, str, fg, bg)
  if bg then gpu.setBackground(bg) end
  gpu.fill(x, y, w, 1, " ") 
  local len = unicode.len(str)
  local px = x + w - len
  if fg then gpu.setForeground(fg) end
  gpu.set(px, y, str)
end

local function fmt(num)
  if math.abs(num) >= 10^12 then return string.format("%.3f T", num/10^12)
  elseif math.abs(num) >= 10^9 then return string.format("%.3f B", num/10^9)
  elseif math.abs(num) >= 10^6 then return string.format("%.3f M", num/10^6)
  elseif math.abs(num) >= 10^3 then return string.format("%.3f K", num/10^3)
  else return string.format("%d", math.floor(num)) end
end

local function progressBar(x, y, w, pct, colBar, colBg)
  rect(x, y, w, 1, colBg)
  local barW = math.floor(w * (pct/100))
  if barW > w then barW = w end
  if barW > 0 then rect(x, y, barW, 1, colBar) end
end

local function btn(id, x, y, w, h, str, bg, fg)
  rect(x, y, w, h, bg)
  center(x, y + math.floor(h/2), w, str, fg or COLORS.text, bg)
  buttons[id] = {x=x, y=y, w=w, h=h}
end

--------------------------------------------------------------------------------
-- [ИНТЕРФЕЙС]
--------------------------------------------------------------------------------

local function drawStaticInterface()
  gpu.setBackground(COLORS.bg)
  term.clear()
  
  -- Левая панель
  rect(x1, y1, panelW, panelH, COLORS.tileBg)
  rect(x1, y1, panelW, 1, COLORS.tileHeader)
  center(x1, y1, panelW, "СОСТОЯНИЕ ЯДРА", COLORS.text, COLORS.tileHeader)
  
  text(x1+2, y1+2, "НАКОПЛЕНО:", COLORS.label, COLORS.tileBg)
  text(x1+2, y1+4, "ЦЕЛЬ СТАБ.:", COLORS.label, COLORS.tileBg)

  -- Правая панель
  rect(x2, y2, panelW, panelH, COLORS.tileBg)
  rect(x2, y2, panelW, 1, COLORS.tileHeader)
  center(x2, y2, panelW, "ЭНЕРГИЯ", COLORS.text, COLORS.tileHeader)

  text(x2+2, y2+2, "ТЕКУЩИЙ ПОТОК:", COLORS.label, COLORS.tileBg)
  text(x2+2, y2+4, "ВЫХОД ГЕЙТА:", COLORS.label, COLORS.tileBg)
  
  -- График
  rect(graphX, graphY, graphW, 1, COLORS.tileHeader) 
  center(graphX, graphY, graphW, " ГРАФИК ПОТОКА ", COLORS.label, COLORS.tileHeader)
  rect(graphX, graphY+1, graphW, graphH, COLORS.bg) 
  
  -- Подвал
  local footerY = H - 1
  btn("exit", M, footerY, 10, 1, "ВЫХОД", COLORS.bad)
  
  -- === ДОБАВЛЕНО: Версия слева внизу ===
  local verX = M + 12 -- Отступ кнопки выхода + пробел
  text(verX, footerY, VERSION, COLORS.label, COLORS.bg)
  
  -- Кредиты
  local crX = W - 32
  local crY = H - 3
  text(crX, crY,   "Разработчик: DesOope", COLORS.label, COLORS.bg)
  text(crX, crY+1, "Проект: McSkill", COLORS.label, COLORS.bg)
  text(crX, crY+2, "Сервер: HitechClassic 1.7.10", COLORS.label, COLORS.bg)
end

local function drawGraph(delta)
    table.insert(energyHistory, delta)
    while #energyHistory > graphW do table.remove(energyHistory, 1) end
    
    local gTopY = graphY + 1
    local gBottomY = graphY + graphH
    local midY = gTopY + math.floor(graphH/2)
    
    if graphH < 2 then return end
    
    local maxVal = 1
    for _, v in ipairs(energyHistory) do
        if math.abs(v) > maxVal then maxVal = math.abs(v) end
    end
    
    for i = 1, graphW do
        local val = energyHistory[i] or 0
        local x = graphX + i - 1
        
        local h = 0
        if math.abs(val) > 0 then
            h = math.ceil( (math.abs(val) / maxVal) * (graphH/2) )
            if h > (graphH/2) then h = math.floor(graphH/2) end
        end

        local barTop, barBottom
        local color
        
        if val > 0 then
            barBottom = midY + 1 
            barTop = barBottom - h
            if barTop < gTopY then barTop = gTopY end
            color = COLORS.good
        elseif val < 0 then
            barTop = midY + 1
            barBottom = barTop + h
            if barBottom > gBottomY then barBottom = gBottomY end
            color = COLORS.bad
        else
            barTop = midY
            barBottom = midY + 1
            color = COLORS.bg 
        end

        if barTop > gTopY then
            gpu.setBackground(COLORS.bg)
            gpu.fill(x, gTopY, 1, barTop - gTopY, " ")
        end
        
        local barH = barBottom - barTop
        if val ~= 0 and barH > 0 then
            gpu.setBackground(color)
            gpu.fill(x, barTop, 1, barH, " ")
        elseif val == 0 then
            gpu.setBackground(COLORS.bg)
            gpu.setForeground(COLORS.graphLine)
            gpu.set(x, midY, "─")
        end
        
        if barBottom < gBottomY then
            gpu.setBackground(COLORS.bg)
            local clearH = gBottomY - barBottom + 1 
            gpu.fill(x, barBottom, 1, clearH, " ")
        end
    end
end

local function updateLiveDisplay(stored, target, delta, gateFlow)
  local valWidth = 16 
  local valX = x1 + panelW - valWidth - 2
  
  -- === ЛЕВАЯ ПАНЕЛЬ ===
  center(valX, y1+2, valWidth, fmt(stored).." RF", COLORS.energy, COLORS.tileBg)
  center(valX, y1+4, valWidth, fmt(target).." RF", COLORS.warn, COLORS.tileBg)
  
  local btnW = 3
  local btnPlusX = valX - btnW - 1
  btn("inc", btnPlusX, y1+4, btnW, 1, "+", COLORS.btn)
  local btnMinusX = btnPlusX - btnW - 1
  btn("dec", btnMinusX, y1+4, btnW, 1, "-", COLORS.btn)
  
  local maxPct = (stored / maxCoreCapacity) * 100
  if maxPct > 100 then maxPct = 100 end
  
  local barX = x1 + 2
  local barW = panelW - 4
  
  center(barX, y1+6, barW, string.format("%.2f%% (от макс. объема)", maxPct), COLORS.text, COLORS.tileBg)
  text(barX, y1+6, "0%", COLORS.label)
  
  local str100 = "100%"
  local x100 = barX + barW - unicode.len(str100)
  text(x100, y1+6, str100, COLORS.label)
  
  progressBar(barX, y1+7, barW, maxPct, COLORS.energy, 0x111111)

  -- === ПРАВАЯ ПАНЕЛЬ ===
  local valX2 = x2 + panelW - valWidth - 2
  
  local dColor = COLORS.text
  local sign = ""
  if delta > 0 then dColor = COLORS.good; sign = "+"
  elseif delta < 0 then dColor = COLORS.bad; sign = "" end 
  
  center(valX2, y2+2, valWidth, sign..fmt(delta).." RF/t", dColor, COLORS.tileBg)
  center(valX2, y2+4, valWidth, fmt(gateFlow).." RF/t", COLORS.text, COLORS.tileBg)
  
  -- === БОЛЬШОЙ СТАТУС БАР (ВНИЗУ) ===
  local statusStr = ""
  local statusBg = COLORS.btn
  local statusFg = COLORS.black
  
  -- НОВАЯ ЛОГИКА СТАТУСОВ
  if math.abs(stored - target) < 10^9 then
      -- Энергия в норме (равна цели)
      statusStr = "СТАБИЛЬНО"
      statusBg = COLORS.good
  else
      if stored < target then
          -- Нехватка энергии
          if delta > 0 then
             -- Но она растет = Хорошо
             statusStr = "ВОСПОЛНЕНИЕ"
             statusBg = COLORS.energy -- Синий
          else
             -- Она падает = Плохо
             statusStr = "ВНИМАНИЕ: РАЗРЯД"
             statusBg = COLORS.bad -- Красный
             statusFg = 0xFFFFFF 
          end
      else
          -- Избыток энергии
          if delta < 0 then
             -- Она падает (возвращается к цели) = Хорошо
             statusStr = "СБРОС ИЗЛИШКОВ"
             statusBg = COLORS.warn -- Оранжевый
          else
             -- Она растет дальше = Плохо
             statusStr = "КРИТИЧЕСКОЕ ПЕРЕПОЛНЕНИЕ"
             statusBg = COLORS.bad -- Красный
             statusFg = 0xFFFFFF
          end
      end
  end
  
  local stX = x2 + 2
  local stW = panelW - 4
  local stY = y2 + 6
  local stH = 3 
  
  gpu.setBackground(statusBg)
  gpu.fill(stX, stY, stW, stH, " ")
  center(stX, stY + 1, stW, statusStr, statusFg, statusBg)
  
  drawGraph(delta)
end

local function handleTouch(x, y)
  for id, b in pairs(buttons) do
    if x >= b.x and x < b.x + b.w and y >= b.y and y < b.y + b.h then
      if id == "exit" then 
          running = false 
      elseif id == "inc" or id == "dec" then
          local step = CFG.btnStep
          if keyboard.isControlDown() then step = CFG.btnStepCtrl
          elseif keyboard.isShiftDown() then step = CFG.btnStepShift end
          
          if id == "dec" then CFG.targetEnergy = CFG.targetEnergy - step end
          if id == "inc" then CFG.targetEnergy = CFG.targetEnergy + step end
          
          if CFG.targetEnergy < 0 then CFG.targetEnergy = 0 end
          computer.beep(1000, 0.05)
          
          rect(b.x, b.y, b.w, b.h, COLORS.good)
          os.sleep(0.05)
          rect(b.x, b.y, b.w, b.h, COLORS.btn)
      end
    end
  end
end

--------------------------------------------------------------------------------
-- [ГЛАВНЫЙ ЦИКЛ]
--------------------------------------------------------------------------------
drawStaticInterface()

-- Мы искусственно отнимаем 1 секунду от времени запуска, 
-- чтобы первый кадр отрисовался моментально.
local prevTime = computer.uptime() - 1.0 
local prevEnergy = rfStorage.getEnergyStored()

while running do
  -- Опрашиваем клики по экрану часто (каждые 0.1 сек), чтобы кнопки работали быстро
  local sig = {event.pull(0.1, "touch")}
  if sig[1] == "touch" then 
    handleTouch(sig[3], sig[4]) 
  end
  
  local nowTime = computer.uptime()
  local dt = nowTime - prevTime
  
  -- Математика, график и обновление гейта срабатывают ровно 1 раз в секунду
  if dt >= 1.0 then
      local nowEnergy = rfStorage.getEnergyStored()
      local rawDiff = (nowEnergy - prevEnergy) / (dt * 20)
      
      local smoothedDiff = expRunningAvg(rawDiff)
      
      local currentGate = fluxGate.getFlow()
      local errorVal = nowEnergy - CFG.targetEnergy
      local correction = errorVal * CFG.aggressiveness
      local newGate = currentGate + smoothedDiff + correction
      
      if newGate < 0 then newGate = 0 end
      
      -- Защита от спама в сеть
      if math.abs(newGate - currentGate) > 500 then
          fluxGate.setSignalLowFlow(math.floor(newGate))
      end
      
      updateLiveDisplay(nowEnergy, CFG.targetEnergy, smoothedDiff, newGate)
      
      prevTime = nowTime
      prevEnergy = nowEnergy
  end
end

gpu.setResolution(W, H)
gpu.setBackground(0x000000)
gpu.setForeground(0xFFFFFF)
term.clear()

И нажимаем Ctrl + S (сохранить) и Ctrl + W (закрыть).

Перезагрузите компьютер командой:

reboot

5. Управление программой

После перезагрузки откроется интерфейс стабилизации. Вы увидите текущее состояние ядра и настройки лимитов.

image.png

Регулировка целевого уровня энергии:

Логика работы: