Revector

5reactors+

Jul 13th, 2026 (edited)
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 100.26 KB | Gaming | 0 0
  1. local event = require("event")
  2. local term = require("term")
  3. local os = require("os")
  4. local computer = require("computer")
  5. local com = require("component")
  6. local unicode = require("unicode")
  7. local keyb = require("keyboard")
  8. local gpu = com.gpu
  9.  
  10. local reactorsFile = "/home/reactors.txt"
  11. local cfgName = "/home/DracReactorConfig.cfg"
  12. local MAX_REACTORS = 5
  13. local CHARGE_FLOW = 535000
  14. local CHARGE_DURATION = 12
  15. local autostateByAdr = {}
  16. local flowInitByAdr = {}
  17. local chargeRequestByAdr = {}
  18.  
  19. local DEFAULT_CONFIG = [[
  20. -- Интервалы для кнопок +/-
  21. default_interval = 1000
  22. ctrl_interval = 5000
  23. shift_interval = 10000
  24. ctrlShift_interval = 20000
  25. -- Автономный режим:
  26. -- Основные константы
  27. shield = 25 - Щиты будут автоматически поддерживаться на этом уровне ( в %)
  28. tempCriticalEdge = 8100 -- Если температура превысит это значение, программа экстренно понизит поток вывода;
  29. -- Форсированный режим
  30. forceModeTempLowEdge = 7500 -- Пока температура не превысит это значение, программа будет работать в форсированном режиме, иначе - перейдет в безопасный режим;
  31. forceModeStepCase = 5000  -- Если разница между текущей выработкой энергии и выставленным потоком превысит это значение, то к потоку добавится
  32. forceModeStep = 20000 -- это значение
  33. -- безопасный режим
  34. safeModeTempWaitEdge = 8000 -- Если температура превысит это значение, программа будет ожидать,
  35. safeModeTempToWaitEdge = 7850 -- пока температура не понизится до этого значения;
  36. safeModeStepCase = 0 -- Если разница между текущей выработкой энергии и выставленным потоком превысит это значение, то к потоку добавится
  37. safemodeStep = 8000 -- это значение
  38. autoProtectTempEdge = 8300 -- При достижении этой температуры включается автономный режим
  39. screen_w = 80 -- Ширина экрана
  40. screen_h = 25 -- Высота экрана
  41. fuelStopPct = 98 -- % топлива, при котором реактор выключается]]
  42.  
  43. local function parseNum(str)
  44.     local n = ""
  45.     for i = 1, #str do
  46.         local ch = str:byte(i, i)
  47.         if ch >= 48 and ch <= 57 or ch == 46 then
  48.             n = n .. string.char(ch)
  49.         end
  50.     end
  51.     if n ~= "" then
  52.         return tonumber(n)
  53.     else
  54.         return n
  55.     end
  56. end
  57.  
  58. local cfg = {}
  59. local confnames = {
  60.     [1]  = "default_interval",
  61.     [2]  = "ctrl_interval",
  62.     [3]  = "shift_interval",
  63.     [4]  = "ctrlShift_interval",
  64.     [5]  = "shield",
  65.     [6]  = "tempCriticalEdge",
  66.     [7]  = "forceModeTempLowEdge",
  67.     [8]  = "forceModeStepCase",
  68.     [9]  = "forceModeStep",
  69.     [10] = "safeModeTempWaitEdge",
  70.     [11] = "safeModeTempToWaitEdge",
  71.     [12] = "safeModeStepCase",
  72.     [13] = "safemodeStep",
  73.     [14] = "autoProtectTempEdge",
  74.     [15] = "screen_w",
  75.     [16] = "screen_h",
  76.     [17] = "fuelStopPct"
  77. }
  78.  
  79. local function readConfig(name)
  80.     local file = io.open(name, "r")
  81.     if not file then
  82.         return nil
  83.     end
  84.     local tab = {}
  85.     for line in file:lines() do
  86.         local key, val = line:match("^%s*([%w_]+)%s*=%s*([%d%.]+)")
  87.         if key and val then
  88.             tab[key] = tonumber(val)
  89.         end
  90.     end
  91.     file:close()
  92.     return tab
  93. end
  94.  
  95. local function hasConfigKeys(parsed)
  96.     if not parsed then
  97.         return false
  98.     end
  99.     for i = 1, #confnames do
  100.         if parsed[confnames[i]] == nil then
  101.             return false
  102.         end
  103.     end
  104.     return true
  105. end
  106.  
  107. local function readParse(name)
  108.     local file = io.open(name, "r")
  109.     if file then
  110.         local tab = {}
  111.         local i = 1
  112.         while true do
  113.             local str = file:read("*l")
  114.             if str == nil then
  115.                 break
  116.             end
  117.             if parseNum(str) ~= "" then
  118.                 tab[i] = parseNum(str)
  119.                 i = i + 1
  120.             end
  121.         end
  122.         file:close()
  123.         return tab
  124.     else
  125.         return false
  126.     end
  127. end
  128.  
  129. local function writeText(name, text)
  130.     local file = io.open(name, "w")
  131.     file:write(text)
  132.     file:close()
  133. end
  134.  
  135. local function configWrite(name)
  136.     local parsed = readConfig(name)
  137.     if not parsed or not hasConfigKeys(parsed) then
  138.         writeText(name, DEFAULT_CONFIG)
  139.     elseif cfg.default_interval ~= nil and
  140.         cfg.ctrl_interval ~= nil and
  141.         cfg.shift_interval ~= nil and
  142.         cfg.ctrlShift_interval ~= nil and
  143.         cfg.shield ~= nil and
  144.         cfg.tempCriticalEdge ~= nil and
  145.         cfg.forceModeTempLowEdge ~= nil and
  146.         cfg.forceModeStepCase ~= nil and
  147.         cfg.forceModeStep ~= nil and
  148.         cfg.safeModeTempWaitEdge ~= nil and
  149.         cfg.safeModeTempToWaitEdge ~= nil and
  150.         cfg.safeModeStepCase ~= nil and
  151.         cfg.safemodeStep ~= nil and
  152.         cfg.autoProtectTempEdge ~= nil and
  153.         cfg.screen_w ~= nil and
  154.         cfg.screen_h ~= nil and
  155.         cfg.fuelStopPct ~= nil then
  156.  
  157.         writeText(name, string.format([[
  158. -- Интервалы для кнопок +/-
  159. default_interval = %d
  160. ctrl_interval = %d
  161. shift_interval = %d
  162. ctrlShift_interval = %d
  163. -- Автономный режим:
  164. -- Основные константы
  165. shield = %0.2f - Щиты будут автоматически поддерживаться на этом уровне ( в %)
  166. tempCriticalEdge = %d -- Если температура превысит это значение, программа экстренно понизит поток вывода;
  167. -- Форсированный режим
  168. forceModeTempLowEdge = %d -- Пока температура не превысит это значение, программа будет работать в форсированном режиме, иначе - перейдет в безопасный режим;
  169. forceModeStepCase = %d -- Если разница между текущей выработкой энергии и выставленным потоком превысит это значение, то к потоку добавится
  170. forceModeStep = %d -- это значение
  171. -- Безопасный режим
  172. safeModeTempWaitEdge = %d -- Если температура превысит это значение, программа будет ожидать,
  173. safeModeTempToWaitEdge = %d -- пока температура не понизится до этого значения;
  174. safeModeStepCase = %d -- Если разница между текущей выработкой энергии и выставленным потоком превысит это значение, то к потоку добавится
  175. safemodeStep = %d -- это значение
  176. autoProtectTempEdge = %d -- При достижении этой температуры включается автономный режим
  177. screen_w = %d -- Ширина экрана
  178. screen_h = %d -- Высота экрана
  179. fuelStopPct = %d -- % топлива, при котором реактор выключается]],
  180.             cfg.default_interval,
  181.             cfg.ctrl_interval,
  182.             cfg.shift_interval,
  183.             cfg.ctrlShift_interval,
  184.             cfg.shield,
  185.             cfg.tempCriticalEdge,
  186.             cfg.forceModeTempLowEdge,
  187.             cfg.forceModeStepCase,
  188.             cfg.forceModeStep,
  189.             cfg.safeModeTempWaitEdge,
  190.             cfg.safeModeTempToWaitEdge,
  191.             cfg.safeModeStepCase,
  192.             cfg.safemodeStep,
  193.             cfg.autoProtectTempEdge,
  194.             cfg.screen_w,
  195.             cfg.screen_h,
  196.             cfg.fuelStopPct
  197.         ))
  198.     end
  199. end
  200.  
  201. local function makeConfig(tab, tab2)
  202.     local config = {}
  203.     for i = 1, #tab do
  204.         config[tostring(tab[i])] = tab2[i]
  205.     end
  206.     return config
  207. end
  208.  
  209. local function listAddresses(typeName)
  210.     local list = {}
  211.     for adr in com.list(typeName) do
  212.         list[#list + 1] = adr
  213.     end
  214.     return list
  215. end
  216.  
  217. local function readReactorsFile()
  218.     local file = io.open(reactorsFile, "r")
  219.     if not file then
  220.         return nil
  221.     end
  222.     local list = {}
  223.     for line in file:lines() do
  224.         local r, u, d = line:match("(%S+)%s+(%S+)%s+(%S+)")
  225.         if r and u and d then
  226.             list[#list + 1] = {reactorAdr = r, upAdr = u, downAdr = d}
  227.         end
  228.     end
  229.     file:close()
  230.     if #list == 0 then
  231.         return nil
  232.     end
  233.     return list
  234. end
  235.  
  236. local function writeReactorsFile(list)
  237.     local file = io.open(reactorsFile, "w")
  238.     for i = 1, #list do
  239.         file:write(string.format("%s %s %s\n", list[i].reactorAdr, list[i].upAdr, list[i].downAdr))
  240.     end
  241.     file:close()
  242. end
  243.  
  244. local function validEntry(entry)
  245.     return com.get(entry.reactorAdr) ~= nil and com.get(entry.upAdr) ~= nil and com.get(entry.downAdr) ~= nil
  246. end
  247.  
  248. local function mergeEntries(entries)
  249.     local usedReactors = {}
  250.     local usedGates = {}
  251.     for i = 1, #entries do
  252.         usedReactors[entries[i].reactorAdr] = true
  253.         usedGates[entries[i].upAdr] = true
  254.         usedGates[entries[i].downAdr] = true
  255.     end
  256.     local reactorsList = listAddresses("draconic_reactor")
  257.     local gates = listAddresses("flux_gate")
  258.     local freeReactors = {}
  259.     local freeGates = {}
  260.     for i = 1, #reactorsList do
  261.         if not usedReactors[reactorsList[i]] then
  262.             freeReactors[#freeReactors + 1] = reactorsList[i]
  263.         end
  264.     end
  265.     for i = 1, #gates do
  266.         if not usedGates[gates[i]] then
  267.             freeGates[#freeGates + 1] = gates[i]
  268.         end
  269.     end
  270.     local addCount = math.min(#freeReactors, math.floor(#freeGates / 2), MAX_REACTORS - #entries)
  271.     for i = 1, addCount do
  272.         entries[#entries + 1] = {
  273.             reactorAdr = freeReactors[i],
  274.             upAdr = freeGates[(i - 1) * 2 + 1],
  275.             downAdr = freeGates[(i - 1) * 2 + 2]
  276.         }
  277.     end
  278.     return entries
  279. end
  280.  
  281. local function buildReactors()
  282.     newEntriesAdded = false
  283.     lastAddedReactorAdr = nil
  284.     local entries = readReactorsFile()
  285.     local currentReactors = reactors or {}
  286.     if entries then
  287.         local valid = {}
  288.         for i = 1, #entries do
  289.             if validEntry(entries[i]) then
  290.                 valid[#valid + 1] = entries[i]
  291.             end
  292.         end
  293.         if #valid > 0 then
  294.             entries = valid
  295.         end
  296.     end
  297.     if entries and #entries > 0 then
  298.         local reactorsList = listAddresses("draconic_reactor")
  299.         if #reactorsList > #entries then
  300.             newEntriesAdded = true
  301.         end
  302.     end
  303.     if not entries or #entries == 0 then
  304.         return nil
  305.     end
  306.     local oldByAdr = {}
  307.     for i = 1, #currentReactors do
  308.         oldByAdr[currentReactors[i].reactorAdr] = currentReactors[i]
  309.     end
  310.     local reactors = {}
  311.     for i = 1, #entries do
  312.         local e = entries[i]
  313.         local old = oldByAdr[e.reactorAdr]
  314.         local savedAuto = autostateByAdr[e.reactorAdr]
  315.         local savedFlowInit = flowInitByAdr[e.reactorAdr]
  316.         local savedCharge = chargeRequestByAdr[e.reactorAdr]
  317.         reactors[i] = {
  318.             index = i,
  319.             reactorAdr = e.reactorAdr,
  320.             upAdr = e.upAdr,
  321.             downAdr = e.downAdr,
  322.             reactor = com.proxy(com.get(e.reactorAdr)),
  323.             upgate = com.proxy(com.get(e.upAdr)),
  324.             downgate = com.proxy(com.get(e.downAdr)),
  325.             maxGenerationRate = old and old.maxGenerationRate or 0,
  326.             autostate = old and old.autostate or (savedAuto ~= nil and savedAuto or false),
  327.             temprise = old and old.temprise or false,
  328.             flowInit = old and old.flowInit or (savedFlowInit ~= nil and savedFlowInit or false),
  329.             chargeRequested = old and old.chargeRequested or (savedCharge or 0),
  330.             lastTemp = nil,
  331.             row = {},
  332.             powerBtn = {},
  333.             tab = {}
  334.         }
  335.         if not old then
  336.             lastAddedReactorAdr = e.reactorAdr
  337.         end
  338.     end
  339.     return reactors
  340. end
  341.  
  342. configWrite(cfgName)
  343. cfg = readConfig(cfgName)
  344. if not cfg then
  345.     cfg = makeConfig(confnames, readParse(cfgName))
  346. end
  347.  
  348. local reactors = {}
  349. local activeIndex = 1
  350. local summaryProfitCord = {x = nil, y = nil}
  351. local summaryEuCord = {x = nil, y = nil}
  352. local summaryShieldCord = {x = nil, y = nil}
  353. local summaryGenCord = {x = nil, y = nil}
  354. local summaryMaxProfitCord = {x = nil, y = nil}
  355. local maxSummaryProfit = 0
  356. local newEntriesAdded = false
  357. local pendingActiveReactorAdr = nil
  358. autostateByAdr = {}
  359. flowInitByAdr = {}
  360.  
  361. local color = {}
  362. color["red"]    = 0xFF0000
  363. color["green"]  = 0x00FF00
  364. color["yellow"] = 0xFFD166
  365. color["skyblue"] = 0x4FC3F7
  366. color["black"]  = 0x0F1115
  367. color["grey"]   = 0x0F1115
  368. color["panel"]  = 0x1A1F27
  369. color["blue"]   = 0x4FC3F7
  370. color["orange"] = 0xFF9800
  371. color["white"]  = 0xE6EDF3
  372. color["dark"]   = 0x242B36
  373. color["dim"]    = 0x7A8599
  374.  
  375. local rback = gpu.getBackground()
  376. local rfore = gpu.getForeground()
  377. local chatBox = nil
  378.  
  379. local handleMain
  380. local handleOptions
  381. local handleStart
  382. local screenAssign
  383. local screenOverview
  384. local assignMode = nil
  385. local firstStartCompleted = false
  386. local assignState = {
  387.     reactorIndex = nil,
  388.     reactorAdr = nil,
  389.     shieldGateIdx = nil,
  390.     shieldGateAdr = nil,
  391.     outGateIdx = nil,
  392.     outGateAdr = nil,
  393.     step = "reactor",
  394.     message = nil
  395. }
  396. local assignUi = {
  397.     reactors = {},
  398.     gates = {},
  399.     gateAddrs = {},
  400.     reactorAddrs = {},
  401.     gateUse = {},
  402.     selectedAdr = nil,
  403.     back = {x = nil, y = nil, w = 0}
  404. }
  405.  
  406. local easyAssign = {
  407.     active = false,
  408.     step = "reactor",
  409.     index = 1,
  410.     core = nil,
  411.     shield = nil,
  412.     output = nil,
  413.     saved = false,
  414.     message = nil,
  415.     heavyBtn = {},
  416.     easyBtn = {},
  417.     continueBtn = {},
  418.     finishBtn = {},
  419.     backBtn = {},
  420.     known = {},
  421.     lastChatStep = nil,
  422.     lastChatMessage = nil
  423. }
  424. local autoStopped = {}
  425. local autoWaitTempByAdr = {}
  426. local chargeRequestByAdr = {}
  427. local autoStoppedCord = {x = nil, y = nil}
  428. local chargeHintDismissed = false
  429. local chargeHint = {x = nil, y = nil, w = 0, visible = false}
  430.  
  431. local menuItem = {}
  432. menuItem["options"] = {
  433.     ["name"] = "Настройки |",
  434.     ["x"] = 1,
  435.     ["y"] = 2
  436. }
  437. menuItem["save"] = {
  438.     ["name"] = "Сохранить |",
  439.     ["x"] = 1,
  440.     ["y"] = 2
  441. }
  442. menuItem["cancel"] = {
  443.     ["name"] = " Отменить  |",
  444.     ["x"] = 12,
  445.     ["y"] = 2
  446. }
  447. menuItem["overview"] = {
  448.     ["name"] = "Главный |",
  449.     ["x"] = 1,
  450.     ["y"] = 2
  451. }
  452. menuItem["refresh"] = {
  453.     ["name"] = "Обновить |",
  454.     ["x"] = 1,
  455.     ["y"] = 2
  456. }
  457. menuItem["gates"] = {
  458.     ["name"] = "Гейты |",
  459.     ["x"] = 1,
  460.     ["y"] = 2
  461. }
  462. menuItem["back"] = {
  463.     ["name"] = "Назад |",
  464.     ["x"] = 1,
  465.     ["y"] = 2
  466. }
  467. menuItem["reset"] = {
  468.     ["name"] = "Сброс |",
  469.     ["x"] = 1,
  470.     ["y"] = 2
  471. }
  472. menuItem["optionsBack"] = {
  473.     ["name"] = " Назад |",
  474.     ["x"] = 1,
  475.     ["y"] = 2
  476. }
  477.  
  478. local xz, yz = gpu.maxResolution()
  479. local screenW = math.min(120, xz)
  480. local screenH = math.min(40, yz)
  481. gpu.setResolution(screenW, screenH)
  482.  
  483. gpu.setBackground(color.grey)
  484. gpu.setForeground(color.white)
  485.  
  486. local prog = true
  487. local OptionDefault = {}
  488. local OptionCtrl = {}
  489. local OptionShift = {}
  490. local OptionCtrlShift = {}
  491. local OptionShield = {}
  492. local OptionFuelStop = {}
  493. OptionDefault["value"] = cfg.default_interval
  494. OptionCtrl["value"] = cfg.ctrl_interval
  495. OptionShift["value"] = cfg.shift_interval
  496. OptionCtrlShift["value"] = cfg.ctrlShift_interval
  497. OptionShield["value"] = cfg.shield
  498. OptionFuelStop["value"] = cfg.fuelStopPct or 98
  499.  
  500. local infcord = {
  501.     ["status"] = {
  502.         ["value"] = nil,
  503.         ["x"] = nil,
  504.         ["y"] = nil
  505.     },
  506.     ["temp"] = {
  507.         ["value"] = nil,
  508.         ["x"] = nil,
  509.         ["y"] = nil
  510.     },
  511.     ["generation"] = {
  512.         ["value"] = nil,
  513.         ["x"] = nil,
  514.         ["y"] = nil
  515.     },
  516.     ["maxgen"] = {
  517.         ["value"] = nil,
  518.         ["x"] = nil,
  519.         ["y"] = nil
  520.     },
  521.     ["flow"] = {
  522.         ["value"] = nil,
  523.         ["x"] = nil,
  524.         ["y"] = nil
  525.     },
  526.     ["puregen"] = {
  527.         ["value"] = nil,
  528.         ["x"] = nil,
  529.         ["y"] = nil
  530.     },
  531.     ["drain"] = {
  532.         ["value"] = nil,
  533.         ["x"] = nil,
  534.         ["y"] = nil
  535.     },
  536.     ["fstrth"] = {
  537.         ["value"] = nil,
  538.         ["x"] = nil,
  539.         ["y"] = nil
  540.     },
  541.     ["esturn"] = {
  542.         ["value"] = nil,
  543.         ["x"] = nil,
  544.         ["y"] = nil
  545.     },
  546.     ["fuel"] = {
  547.         ["value"] = nil,
  548.         ["x"] = nil,
  549.         ["y"] = nil
  550.     },
  551.     ["fuelconv"] = {
  552.         ["value"] = nil,
  553.         ["x"] = nil,
  554.         ["y"] = nil
  555.     },
  556.     ["core"] = {
  557.         ["value"] = nil,
  558.         ["x"] = nil,
  559.         ["y"] = nil
  560.     }
  561. }
  562.  
  563. local button = {
  564.     ["continue"] = {
  565.         ["name"] = "Продолжить",
  566.         ["x"] = nil,
  567.         ["y"] = nil
  568.     },
  569.     ["change"] = {
  570.         ["name"] = "Изменить",
  571.         ["x"] = nil,
  572.         ["y"] = nil
  573.     },
  574.     ["add"] = {
  575.         ["name"] = " + ",
  576.         ["x"] = nil,
  577.         ["y"] = nil
  578.     },
  579.     ["sub"] = {
  580.         ["name"] = " - ",
  581.         ["x"] = nil,
  582.         ["y"] = nil
  583.     },
  584.     ["start"] = {
  585.         ["name"] = " [PWR] ЗАПУСК ",
  586.         ["x"] = nil,
  587.         ["y"] = nil,
  588.         ["visible"] = nil
  589.     },
  590.     ["stop"] = {
  591.         ["name"] = " [PWR] СТОП ",
  592.         ["x"] = nil,
  593.         ["y"] = nil,
  594.         ["visible"] = nil
  595.     },
  596.     ["charge"] = {
  597.         ["name"] = "Зарядить реактор ",
  598.         ["x"] = nil,
  599.         ["y"] = nil,
  600.         ["visible"] = nil
  601.     },
  602.     ["autoon"] = {
  603.         ["name"] = "[ON] ",
  604.         ["x"] = nil,
  605.         ["y"] = nil,
  606.     },
  607.     ["autooff"] = {
  608.         ["name"] = "[OFF]",
  609.         ["x"] = nil,
  610.         ["y"] = nil,
  611.     }
  612. }
  613.  
  614. local ops = true
  615. local canedit = true
  616. local canclose = true
  617. local temprise
  618. local autostate = false
  619. local maxGenerationRate = 0
  620. local lastDetailUpdate = 0
  621. local lastOverviewUpdate = 0
  622.  
  623. printf = function(s, ...)
  624.     return io.write(s:format(...))
  625. end
  626.  
  627. local function textWidth(s)
  628.     local ok, len = pcall(unicode.len, s)
  629.     if ok and len then
  630.         return len
  631.     end
  632.     return #s
  633. end
  634.  
  635. local function guiWrite(x, y, cf, cb, s, ...)
  636.     term.setCursor(x, y)
  637.     if cf >= 0 and cb >= 0 then
  638.         local fg = gpu.getForeground()
  639.         local bg = gpu.getBackground()
  640.         gpu.setForeground(cf)
  641.         gpu.setBackground(cb)
  642.         printf(s, ...)
  643.         gpu.setForeground(fg)
  644.         gpu.setBackground(bg)
  645.     elseif cb < 0 and cf >= 0 then
  646.         local fg = gpu.getForeground()
  647.         gpu.setForeground(cf)
  648.         printf(s, ...)
  649.         gpu.setForeground(fg)
  650.     elseif cf < 0 and cb >= 0 then
  651.         local bg = gpu.getBackground()
  652.         printf(s, ...)
  653.         gpu.setBackground(bg)
  654.     else
  655.         local fg = gpu.getForeground()
  656.         gpu.setForeground(color.white)
  657.         printf(s, ...)
  658.         gpu.setForeground(fg)
  659.     end
  660. end
  661.  
  662. local function getCord(param)
  663.     param.x, param.y = term.getCursor()
  664.     return param.x, param.y
  665. end
  666.  
  667. local function guiClear(y)
  668.     local fg = gpu.getForeground()
  669.     local bg = gpu.getBackground()
  670.     gpu.setForeground(color.white)
  671.     gpu.setBackground(color.grey)
  672.     for i = y + 3, 3, -1 do
  673.         term.setCursor(1, i)
  674.         term.clearLine()
  675.     end
  676.     gpu.setForeground(fg)
  677.     gpu.setBackground(bg)
  678. end
  679.  
  680. local function setActive(index)
  681.     if not reactors[index] then
  682.         return
  683.     end
  684.     activeIndex = index
  685.     reactor = reactors[index].reactor
  686.     upgate = reactors[index].upgate
  687.     downgate = reactors[index].downgate
  688.     autostate = reactors[index].autostate
  689.     temprise = reactors[index].temprise
  690.     maxGenerationRate = reactors[index].maxGenerationRate
  691. end
  692.  
  693. local function syncActive()
  694.     if not reactors[activeIndex] then
  695.         return
  696.     end
  697.     reactors[activeIndex].autostate = autostate
  698.     reactors[activeIndex].temprise = temprise
  699.     reactors[activeIndex].maxGenerationRate = maxGenerationRate
  700. end
  701.  
  702. local function drawHeader(title)
  703.     local x = gpu.getResolution()
  704.     gpu.setBackground(color.grey)
  705.     gpu.setForeground(color.white)
  706.     term.clear()
  707.     gpu.setBackground(color.panel)
  708.     gpu.setForeground(color.white)
  709.     term.clearLine()
  710.     printf(" %s", title)
  711.     gpu.setForeground(color.green)
  712.     printf(" | Разработал : GintaRus")
  713.     gpu.setForeground(color.white)
  714.     printf("\n")
  715.     gpu.setBackground(color.grey)
  716.     gpu.setForeground(color.white)
  717.     term.clearLine()
  718.     gpu.setBackground(0xFF0000)
  719.     gpu.set(x - 2, 1, " X ")
  720.     gpu.setBackground(color.grey)
  721.     gpu.setForeground(color.white)
  722.     return x
  723. end
  724.  
  725. local function drawSeparator(x)
  726.     gpu.setBackground(color.grey)
  727.     gpu.setForeground(color.dim)
  728.     gpu.fill(1, 3, x, 1, "-")
  729.     gpu.setBackground(color.grey)
  730.     gpu.setForeground(color.white)
  731.     term.setCursor(1, 4)
  732. end
  733.  
  734. local function makeButton(x, y, text)
  735.     term.setCursor(x, y)
  736.     local fg = gpu.getForeground()
  737.     local bg = gpu.getBackground()
  738.     gpu.setBackground(color.blue)
  739.     gpu.setForeground(color.white)
  740.     printf(text)
  741.     gpu.setBackground(bg)
  742.     gpu.setForeground(fg)
  743. end
  744.  
  745. local function makePowerButton(x, y, text, bg, fg)
  746.     term.setCursor(x, y)
  747.     local rfg = gpu.getForeground()
  748.     local rbg = gpu.getBackground()
  749.     gpu.setBackground(bg)
  750.     gpu.setForeground(fg)
  751.     printf(text)
  752.     gpu.setBackground(rbg)
  753.     gpu.setForeground(rfg)
  754. end
  755.  
  756. local function getGateFlow(gate)
  757.     if not gate then
  758.         return 0
  759.     end
  760.     local ok, value = pcall(gate.getSignalLowFlow)
  761.     if ok and type(value) == "number" then
  762.         return value
  763.     end
  764.     local ok2, value2 = pcall(gate.getFlow)
  765.     if ok2 and type(value2) == "number" then
  766.         return value2
  767.     end
  768.     return 0
  769. end
  770.  
  771. local function isComponentAlive(addr)
  772.     if not addr then
  773.         return false
  774.     end
  775.     return com.get(addr) ~= nil
  776. end
  777.  
  778. local function ensureFlowInit(r, force)
  779.     if not r or (r.flowInit and not force) then
  780.         return
  781.     end
  782.     pcall(r.upgate.setSignalLowFlow, r.upgate, CHARGE_FLOW)
  783.     r.flowInit = true
  784.     flowInitByAdr[r.reactorAdr] = true
  785.     if reactors[activeIndex] and reactors[activeIndex].reactorAdr == r.reactorAdr then
  786.         infcord.flow.value = CHARGE_FLOW
  787.         if handleMain and infcord.flow.x then
  788.             guiWrite(infcord.flow.x, infcord.flow.y, color.red, color.grey, "%s", numformat(infcord.flow.value))
  789.         end
  790.     end
  791. end
  792.  
  793. local function canChargeStatus(status)
  794.     return status == "offline" or status == "stopping" or status == "cold" or status == "cooling" or status == "warming_up"
  795. end
  796.  
  797. local function statusText(status)
  798.     if status == "online" then
  799.         return "ВКЛ"
  800.     elseif status == "offline" then
  801.         return "ВЫК"
  802.     elseif status == "charging" then
  803.         return "ЗАР"
  804.     elseif status == "charged" then
  805.         return "ГОТ"
  806.     elseif status == "stopping" then
  807.         return "СТП"
  808.     elseif status == "cold" then
  809.         return "ХОЛ"
  810.     elseif status == "cooling" then
  811.         return "ОСТ"
  812.     elseif status == "warming_up" then
  813.         return "РАЗ"
  814.     end
  815.     return status
  816. end
  817.  
  818. local function show(fc, bc, param)
  819.     local fg = gpu.getForeground()
  820.     local bg = gpu.getBackground()
  821.     term.setCursor(param.x, param.y)
  822.     gpu.setForeground(fc)
  823.     gpu.setBackground(bc)
  824.     local st = " "
  825.     for i = 0, #tostring(param.value) do
  826.         st = st .. " "
  827.     end
  828.     gpu.set(param.x, param.y, st)
  829.     printf("%d", param.value)
  830.     gpu.setForeground(fg)
  831.     gpu.setBackground(bg)
  832. end
  833.  
  834. local function numformat(n)
  835.     local s = tostring(n)
  836.     if n >= 1000 and n < 1000000 then
  837.         local t = s:sub(1, #s - 3)
  838.         local e = s:sub(#s - 2)
  839.         s = t .. " " .. e .. "  "
  840.         return s
  841.     elseif n >= 1000000 then
  842.         local m = s:sub(1, #s - 6)
  843.         local t = s:sub(#s - 5, #s - 3)
  844.         local e = s:sub(#s - 2)
  845.         s = m .. " " .. t .. " " .. e .. "  "
  846.         return s
  847.     else
  848.         s = s .. "  "
  849.         return s
  850.     end
  851. end
  852.  
  853. local function overviewCols(x)
  854.     local cols
  855.     if x >= 120 then
  856.         cols = {status = 6, temp = 15, fuel = 27, profit = 43, gen = 60, flow = 77}
  857.     else
  858.         cols = {status = 5, temp = 11, fuel = 20, profit = 33, gen = 48, flow = 63}
  859.     end
  860.     local ctrl = cols.flow + 12
  861.     if ctrl < 65 then
  862.         ctrl = 65
  863.     end
  864.     if ctrl + 16 > x then
  865.         ctrl = math.max(65, x - 16)
  866.     end
  867.     cols.ctrl = ctrl
  868.     return cols
  869. end
  870.  
  871. local function coreformat()
  872.     local num = com.draconic_rf_storage.getEnergyStored()
  873.     if num > 10 ^ 12 then
  874.         return string.format(" Накоплено в ядре: %0.3f T   \n", num / 10 ^ 12)
  875.     elseif num > 10 ^ 9 then
  876.         return string.format(" Накоплено в ядре: %0.3f B   \n", num / 10 ^ 9)
  877.     elseif num > 10 ^ 6 then
  878.         return string.format(" Накоплено в ядре: %0.3f M   \n", num / 10 ^ 6)
  879.     elseif num > 10 ^ 3 then
  880.         return string.format(" Накоплено в ядре: %0.3f K   \n", num / 10 ^ 3)
  881.     else
  882.         return string.format(" Накоплено в ядре: %d   \n", num)
  883.     end
  884. end
  885.  
  886. local function checkButtons()
  887.     local ok, inf = pcall(reactor.getReactorInfo)
  888.     if not ok or type(inf) ~= "table" then
  889.         return
  890.     end
  891.     infcord.status.value = inf.status
  892.     button.start.visible = false
  893.     button.stop.visible = false
  894.     button.charge.visible = false
  895.     if canChargeStatus(infcord.status.value) then
  896.         guiWrite(button.charge.x, button.charge.y, color.skyblue, color.black, "%s", button.charge.name)
  897.         button.charge.visible = true
  898.     elseif infcord.status.value == "charged" then
  899.         guiWrite(button.start.x, button.start.y, color.green, color.black, "%s", button.start.name)
  900.         button.start.visible = true
  901.         guiWrite(button.stop.x, button.stop.y, color.red, color.black, "%s", button.stop.name)
  902.         button.stop.visible = true
  903.     elseif infcord.status.value == "online" or infcord.status.value == "charging" then
  904.         guiWrite(button.stop.x, button.stop.y, color.red, color.black, "%s", button.stop.name)
  905.         button.stop.visible = true
  906.     elseif infcord.status.value == "stopping" then
  907.         if (inf.energySaturation / inf.maxEnergySaturation) * 100 >= 50 and
  908.             (inf.fieldStrength / inf.maxFieldStrength) * 100 >= 50 and
  909.             inf.temperature > 2000 then
  910.             guiWrite(button.start.x, button.start.y, color.green, color.black, "%s", button.start.name)
  911.             button.start.visible = true
  912.         end
  913.     end
  914. end
  915.  
  916. local function screenStart()
  917.     handleMain = false
  918.     handleOptions = false
  919.     handleStart = true
  920.     local x = drawHeader("HiTech Classic is the best   (Preparation)")
  921.     if not firstStartCompleted then
  922.         printf("Эта страница для настройки программы при первом запуске или после сбоя")
  923.     end
  924.     drawSeparator(x)
  925.     term.setCursor(1, screenH - 2)
  926.     menuItem.gates.x, menuItem.gates.y = term.getCursor()
  927.     gpu.setForeground(color.blue)
  928.     printf(menuItem.gates.name)
  929.     menuItem.gates.w = textWidth(menuItem.gates.name)
  930.     gpu.setForeground(color.white)
  931.     local hadFile = readReactorsFile() ~= nil
  932.     while true do
  933.         if not ops then
  934.             return
  935.         end
  936.         reactors = buildReactors()
  937.         if reactors and #reactors > 0 then
  938.             setActive(1)
  939.             firstStartCompleted = true
  940.             if not hadFile then
  941.                 screenAssign()
  942.                 return
  943.             end
  944.             break
  945.         end
  946.         if not hadFile then
  947.             local reactorsList = listAddresses("draconic_reactor")
  948.             local gates = listAddresses("flux_gate")
  949.             if #reactorsList > 0 and #gates >= 2 then
  950.                 reactors = reactors or {}
  951.                 firstStartCompleted = true
  952.                 screenOverview()
  953.                 return
  954.             end
  955.         end
  956.         guiClear(2)
  957.         printf("Подключите реакторы и флюкс-гейты (2 гейта на каждый реактор)\n")
  958.         printf("Максимум: %d реакторов. Адреса можно сохранить в %s\n", MAX_REACTORS, reactorsFile)
  959.         os.sleep(1)
  960.     end
  961. end
  962.  
  963. local function screenOptions()
  964.     handleMain = false
  965.     handleStart = false
  966.     handleOptions = true
  967.     handleOverview = false
  968.     handleAssign = false
  969.     canedit = true
  970.     canclose = true
  971.     local x = drawHeader("HiTech Classic is the best   (Options)")
  972.     menuItem.save.x, menuItem.save.y = term.getCursor()
  973.     gpu.setForeground(color.blue)
  974.     printf(menuItem.save.name)
  975.     menuItem.save.w = textWidth(menuItem.save.name)
  976.     menuItem.cancel.x, menuItem.cancel.y = term.getCursor()
  977.     printf(menuItem.cancel.name)
  978.     menuItem.cancel.w = textWidth(menuItem.cancel.name)
  979.     menuItem.optionsBack.x, menuItem.optionsBack.y = term.getCursor()
  980.     printf(menuItem.optionsBack.name)
  981.     menuItem.optionsBack.w = textWidth(menuItem.optionsBack.name)
  982.     gpu.setForeground(color.white)
  983.     drawSeparator(x)
  984.     printf(" Интервал для щелчка: ")
  985.     OptionDefault["x"], OptionDefault["y"] = term.getCursor()
  986.     printf("%d\n", OptionDefault.value)
  987.     printf(" Интервал для CTRL: ")
  988.     OptionCtrl["x"], OptionCtrl["y"] = term.getCursor()
  989.     printf("%d\n", OptionCtrl.value)
  990.     printf(" Интервал для SHIFT: ")
  991.     OptionShift["x"], OptionShift["y"] = term.getCursor()
  992.     printf("%d\n", OptionShift.value)
  993.     printf(" Интервал для CTRL+SHIFT: ")
  994.     OptionCtrlShift["x"], OptionCtrlShift["y"] = term.getCursor()
  995.     printf("%d\n", OptionCtrlShift.value)
  996.     printf(" Уровень щитов: ")
  997.     OptionShield["x"], OptionShield["y"] = term.getCursor()
  998.     printf("%d\n", OptionShield.value)
  999.     printf(" Отключение по топливу (%%): ")
  1000.     OptionFuelStop["x"], OptionFuelStop["y"] = term.getCursor()
  1001.     printf("%d\n", OptionFuelStop.value)
  1002. end
  1003.  
  1004. local function clearPromptLine()
  1005.     local _, y = term.getCursor()
  1006.     if y > 1 then
  1007.         term.setCursor(1, y - 1)
  1008.         term.clearLine()
  1009.         term.setCursor(1, y - 1)
  1010.     end
  1011. end
  1012.  
  1013. local function readIndex(prompt, max, used)
  1014.     while true do
  1015.         printf("%s", prompt)
  1016.         local input = term.read(false, false) or ""
  1017.         clearPromptLine()
  1018.         local num = tonumber(input:match("%d+"))
  1019.         if num and num >= 1 and num <= max and not used[num] then
  1020.             return num
  1021.         end
  1022.         printf("Неверный выбор, попробуйте снова.\n")
  1023.     end
  1024. end
  1025.  
  1026. local function readIndexAllowZero(prompt, max)
  1027.     while true do
  1028.         printf("%s", prompt)
  1029.         local input = term.read(false, false) or ""
  1030.         clearPromptLine()
  1031.         local lower = input:lower()
  1032.         if lower:find("назад") or lower:find("back") or lower:match("^b%s*$") then
  1033.             return -1
  1034.         end
  1035.         local num = tonumber(input:match("%d+"))
  1036.         if num and num >= 0 and num <= max then
  1037.             return num
  1038.         end
  1039.         printf("Неверный выбор, попробуйте снова.\n")
  1040.     end
  1041. end
  1042.  
  1043. local saveAssignedGates
  1044.  
  1045. local function getChatBox()
  1046.     if chatBox then
  1047.         return chatBox
  1048.     end
  1049.     if com.isAvailable("chat_box") then
  1050.         chatBox = com.chat_box
  1051.     end
  1052.     return chatBox
  1053. end
  1054.  
  1055. local function chatSay(message, color_code)
  1056.     local box = getChatBox()
  1057.     if not box or not message then
  1058.         return false
  1059.     end
  1060.     if box.say and pcall(function()
  1061.         box.say(message)
  1062.     end) then
  1063.         return true
  1064.     end
  1065.     if box.sendMessage and pcall(function()
  1066.         box.sendMessage(message)
  1067.     end) then
  1068.         return true
  1069.     end
  1070.     if box.sendMessage and pcall(function()
  1071.         box.sendMessage(message, color_code or 64)
  1072.     end) then
  1073.         return true
  1074.     end
  1075.     return false
  1076. end
  1077.  
  1078. local function maybeChatSay(step, message, color_code)
  1079.     if step and easyAssign.lastChatStep ~= step then
  1080.         easyAssign.lastChatStep = step
  1081.         easyAssign.lastChatMessage = nil
  1082.     end
  1083.     if message and easyAssign.lastChatMessage ~= message then
  1084.         easyAssign.lastChatMessage = message
  1085.         chatSay(message, color_code)
  1086.     end
  1087. end
  1088.  
  1089. local function snapshotComponents(typeName)
  1090.     local list = {}
  1091.     for adr in com.list(typeName) do
  1092.         list[#list + 1] = adr
  1093.     end
  1094.     return list
  1095. end
  1096.  
  1097. local function initEasyKnown()
  1098.     easyAssign.known = {draconic_reactor = {}, flux_gate = {}}
  1099.     for _, addr in ipairs(snapshotComponents("draconic_reactor")) do
  1100.         easyAssign.known.draconic_reactor[addr] = true
  1101.     end
  1102.     for _, addr in ipairs(snapshotComponents("flux_gate")) do
  1103.         easyAssign.known.flux_gate[addr] = true
  1104.     end
  1105. end
  1106.  
  1107. local function findNewComponent(typeName)
  1108.     if not easyAssign.known or not easyAssign.known[typeName] then
  1109.         initEasyKnown()
  1110.     end
  1111.     for addr in com.list(typeName) do
  1112.         if not easyAssign.known[typeName][addr] then
  1113.             easyAssign.known[typeName][addr] = true
  1114.             return addr
  1115.         end
  1116.     end
  1117.     return nil
  1118. end
  1119.  
  1120. local function isAddressUsed(addr)
  1121.     if not addr then
  1122.         return false
  1123.     end
  1124.     local entries = readReactorsFile() or {}
  1125.     for i = 1, #entries do
  1126.         if entries[i].reactorAdr == addr or entries[i].upAdr == addr or entries[i].downAdr == addr then
  1127.             return true
  1128.         end
  1129.     end
  1130.     return false
  1131. end
  1132.  
  1133. local function clearAssignedByAdr(reactorAdr)
  1134.     if not reactorAdr then
  1135.         return false
  1136.     end
  1137.     local entries = readReactorsFile() or {}
  1138.     local newEntries = {}
  1139.     local removed = false
  1140.     for i = 1, #entries do
  1141.         if entries[i].reactorAdr ~= reactorAdr then
  1142.             newEntries[#newEntries + 1] = entries[i]
  1143.         else
  1144.             removed = true
  1145.         end
  1146.     end
  1147.     if removed then
  1148.         writeReactorsFile(newEntries)
  1149.     end
  1150.     return removed
  1151. end
  1152.  
  1153. local function startEasyAssign()
  1154.     local entries = readReactorsFile() or {}
  1155.     if #entries >= MAX_REACTORS then
  1156.         easyAssign.active = false
  1157.         easyAssign.message = "Достигнут лимит реакторов."
  1158.         return
  1159.     end
  1160.     if not getChatBox() then
  1161.         easyAssign.active = false
  1162.         easyAssign.message = "Ошибка: для работы нужен chat_box."
  1163.         return
  1164.     end
  1165.     easyAssign.active = true
  1166.     easyAssign.step = "reactor"
  1167.     easyAssign.core = nil
  1168.     easyAssign.shield = nil
  1169.     easyAssign.output = nil
  1170.     easyAssign.saved = false
  1171.     easyAssign.message = nil
  1172.     easyAssign.index = #entries + 1
  1173.     easyAssign.lastChatStep = nil
  1174.     easyAssign.lastChatMessage = nil
  1175.     initEasyKnown()
  1176.     maybeChatSay("intro", "Легкий режим: подключите адаптер к реактору #" .. tostring(easyAssign.index) .. " и смотрите монитор.")
  1177. end
  1178.  
  1179. local function screenAssignChoice()
  1180.     handleMain = false
  1181.     handleStart = false
  1182.     handleOptions = false
  1183.     handleOverview = false
  1184.     handleAssign = true
  1185.     local x = drawHeader("HiTech Classic is the best   (Гейты)")
  1186.     drawSeparator(x)
  1187.     assignUi.reactors = {}
  1188.     assignUi.gates = {}
  1189.     assignUi.gateAddrs = {}
  1190.     assignUi.reactorAddrs = {}
  1191.     assignUi.gateUse = {}
  1192.     assignUi.selectedAdr = nil
  1193.     gpu.setBackground(color.grey)
  1194.     gpu.setForeground(color.white)
  1195.     gpu.fill(1, 4, x, 21, " ")
  1196.     term.setCursor(1, 2)
  1197.     menuItem.back.x, menuItem.back.y = term.getCursor()
  1198.     assignUi.back.x, assignUi.back.y, assignUi.back.w = menuItem.back.x, menuItem.back.y, textWidth(menuItem.back.name)
  1199.     gpu.setForeground(color.blue)
  1200.     printf(menuItem.back.name)
  1201.     menuItem.back.w = textWidth(menuItem.back.name)
  1202.     gpu.setForeground(color.white)
  1203.     term.setCursor(2, 5)
  1204.     printf("Выберите способ добавления гейтов:")
  1205.     local heavyLabel = " Тяжелый способ (анализатор) "
  1206.     local easyLabel = " Легкий способ (подключение) "
  1207.     local heavyX, heavyY = 2, 7
  1208.     local easyX, easyY = 2, 9
  1209.     easyAssign.heavyBtn = {x = heavyX, y = heavyY, w = textWidth(heavyLabel)}
  1210.     easyAssign.easyBtn = {x = easyX, y = easyY, w = textWidth(easyLabel)}
  1211.     guiWrite(heavyX, heavyY, color.white, color.red, "%s", heavyLabel)
  1212.     guiWrite(easyX, easyY, color.white, color.green, "%s", easyLabel)
  1213.     if easyAssign.message then
  1214.         guiWrite(2, 12, color.yellow, -1, "%s", easyAssign.message)
  1215.     end
  1216. end
  1217.  
  1218. local function screenAssignEasy()
  1219.     handleMain = false
  1220.     handleStart = false
  1221.     handleOptions = false
  1222.     handleOverview = false
  1223.     handleAssign = true
  1224.     local x = drawHeader("HiTech Classic is the best   (Гейты - Легкий)")
  1225.     drawSeparator(x)
  1226.     gpu.setBackground(color.grey)
  1227.     gpu.setForeground(color.white)
  1228.     gpu.fill(1, 4, x, 21, " ")
  1229.     term.setCursor(1, 2)
  1230.     menuItem.back.x, menuItem.back.y = term.getCursor()
  1231.     easyAssign.backBtn = {x = menuItem.back.x, y = menuItem.back.y, w = textWidth(menuItem.back.name)}
  1232.     gpu.setForeground(color.blue)
  1233.     printf(menuItem.back.name)
  1234.     menuItem.back.w = textWidth(menuItem.back.name)
  1235.     gpu.setForeground(color.white)
  1236.     term.setCursor(2, 5)
  1237.     printf("РЕАКТОР #%d", easyAssign.index or 1)
  1238.     term.setCursor(2, 7)
  1239.     if easyAssign.step == "reactor" then
  1240.         guiWrite(2, 7, color.green, -1, "Подключите адаптер к ядру реактора.")
  1241.         maybeChatSay("reactor", "Подключите адаптер к ядру реактора.", 255)
  1242.     elseif easyAssign.step == "shield" then
  1243.         guiWrite(2, 7, color.green, -1, "Подключите адаптер к гейту ЩИТОВ.")
  1244.         maybeChatSay("shield", "Подключите адаптер к гейту ЩИТОВ.", 4096)
  1245.     elseif easyAssign.step == "output" then
  1246.         guiWrite(2, 7, color.green, -1, "Подключите адаптер к гейту ВЫВОДА.")
  1247.         maybeChatSay("output", "Подключите адаптер к гейту ВЫВОДА.", 16711680)
  1248.     elseif easyAssign.step == "done" then
  1249.         guiWrite(2, 7, color.green, -1, "Реактор добавлен.")
  1250.         maybeChatSay("done", "Реактор добавлен. Смотрите монитор.", 65280)
  1251.     end
  1252.     term.setCursor(2, 9)
  1253.     printf("Ожидаю компонент...")
  1254.     local infoY = 11
  1255.     local coreLine = "Ядро: " .. (easyAssign.core or "-")
  1256.     local shieldLine = "Щиты: " .. (easyAssign.shield or "-")
  1257.     local outputLine = "Вывод: " .. (easyAssign.output or "-")
  1258.     local coreColor = (easyAssign.step == "reactor") and color.blue or (easyAssign.core and color.green or color.white)
  1259.     local shieldColor = (easyAssign.step == "shield") and color.blue or (easyAssign.shield and color.green or color.white)
  1260.     local outputColor = (easyAssign.step == "output") and color.blue or (easyAssign.output and color.green or color.white)
  1261.     guiWrite(2, infoY, coreColor, -1, "%s", coreLine)
  1262.     guiWrite(2, infoY + 1, shieldColor, -1, "%s", shieldLine)
  1263.     guiWrite(2, infoY + 2, outputColor, -1, "%s", outputLine)
  1264.     if easyAssign.message then
  1265.         guiWrite(2, infoY + 4, color.yellow, -1, "%s", easyAssign.message)
  1266.     end
  1267.     if easyAssign.step == "done" then
  1268.         local contLabel = " [Продолжить] "
  1269.         local finLabel = " [Завершить] "
  1270.         local contX, contY = 2, infoY + 6
  1271.         local finX, finY = 2 + textWidth(contLabel) + 2, infoY + 6
  1272.         easyAssign.continueBtn = {x = contX, y = contY, w = textWidth(contLabel)}
  1273.         easyAssign.finishBtn = {x = finX, y = finY, w = textWidth(finLabel)}
  1274.         guiWrite(contX, contY, color.white, color.green, "%s", contLabel)
  1275.         guiWrite(finX, finY, color.white, color.red, "%s", finLabel)
  1276.     end
  1277. end
  1278.  
  1279. local function screenAssignManual()
  1280.     handleMain = false
  1281.     handleStart = false
  1282.     handleOptions = false
  1283.     handleOverview = false
  1284.     handleAssign = true
  1285.     local x = drawHeader("HiTech Classic is the best   (Гейты)")
  1286.     drawSeparator(x)
  1287.     assignUi.reactors = {}
  1288.     assignUi.gates = {}
  1289.     assignUi.gateAddrs = {}
  1290.     assignUi.reactorAddrs = {}
  1291.     assignUi.gateUse = {}
  1292.     assignUi.selectedAdr = nil
  1293.     gpu.setBackground(color.grey)
  1294.     gpu.setForeground(color.white)
  1295.     gpu.fill(1, 4, x, 21, " ")
  1296.     term.setCursor(1, 2)
  1297.     menuItem.back.x, menuItem.back.y = term.getCursor()
  1298.     assignUi.back.x, assignUi.back.y, assignUi.back.w = menuItem.back.x, menuItem.back.y, textWidth(menuItem.back.name)
  1299.     gpu.setForeground(color.blue)
  1300.     printf(menuItem.back.name)
  1301.     menuItem.back.w = textWidth(menuItem.back.name)
  1302.     gpu.setForeground(color.white)
  1303.     term.setCursor(2, 4)
  1304.     printf("Выберите реактор")
  1305.     local reactorsList = listAddresses("draconic_reactor")
  1306.     local gates = listAddresses("flux_gate")
  1307.     assignUi.gateAddrs = gates
  1308.     assignUi.reactorAddrs = reactorsList
  1309.     if #reactorsList == 0 or #gates < 2 then
  1310.         printf("Недостаточно реакторов или флюкс-гейтов.\n")
  1311.         os.sleep(1)
  1312.         screenOverview()
  1313.         return
  1314.     end
  1315.     local count = math.min(#reactorsList, MAX_REACTORS)
  1316.     term.setCursor(2, 5)
  1317.     printf("Реакторы:")
  1318.     local rowY = 6
  1319.     for i = 1, count do
  1320.         term.setCursor(2, rowY)
  1321.         local label = string.format("%d) %s", i, reactorsList[i])
  1322.         if assignState.reactorIndex == i then
  1323.             guiWrite(2, rowY, color.black, color.blue, "%s", label)
  1324.         else
  1325.             guiWrite(2, rowY, color.white, -1, "%s", label)
  1326.         end
  1327.         assignUi.reactors[i] = {x = 2, y = rowY, w = textWidth(label)}
  1328.         rowY = rowY + 1
  1329.     end
  1330.  
  1331.     local entries = readReactorsFile() or {}
  1332.     local gateUse = {}
  1333.     for i = 1, #entries do
  1334.         gateUse[entries[i].upAdr] = entries[i].reactorAdr
  1335.         gateUse[entries[i].downAdr] = entries[i].reactorAdr
  1336.     end
  1337.     assignUi.gateUse = gateUse
  1338.     local selectedAdr = assignState.reactorIndex and reactorsList[assignState.reactorIndex] or nil
  1339.     assignUi.selectedAdr = selectedAdr
  1340.     local selectedEntry = nil
  1341.     if selectedAdr then
  1342.         for i = 1, #entries do
  1343.             if entries[i].reactorAdr == selectedAdr then
  1344.                 selectedEntry = entries[i]
  1345.                 break
  1346.             end
  1347.         end
  1348.     end
  1349.     assignUi.selectedEntry = selectedEntry
  1350.  
  1351.     term.setCursor(40, 5)
  1352.     printf("Гейты:")
  1353.     local gateY = 6
  1354.     for i = 1, #gates do
  1355.         if gateY > 24 then
  1356.             break
  1357.         end
  1358.         term.setCursor(40, gateY)
  1359.         local label = string.format("%d) %s", i, gates[i])
  1360.         local colorGate = color.dim
  1361.         if selectedAdr then
  1362.             if selectedEntry and (gates[i] == selectedEntry.upAdr or gates[i] == selectedEntry.downAdr) then
  1363.                 colorGate = color.green
  1364.             elseif assignState.shieldGateIdx == i or assignState.outGateIdx == i then
  1365.                 colorGate = color.green
  1366.             elseif gateUse[gates[i]] and gateUse[gates[i]] ~= selectedAdr then
  1367.                 colorGate = color.red
  1368.             else
  1369.                 colorGate = color.dim
  1370.             end
  1371.         end
  1372.         guiWrite(40, gateY, colorGate, -1, "%s", label)
  1373.         assignUi.gates[i] = {x = 40, y = gateY, w = textWidth(label)}
  1374.         gateY = gateY + 1
  1375.     end
  1376. end
  1377.  
  1378. local function screenAssign()
  1379.     if assignMode == "manual" then
  1380.         screenAssignManual()
  1381.     elseif assignMode == "easy" then
  1382.         screenAssignEasy()
  1383.     else
  1384.         screenAssignChoice()
  1385.     end
  1386. end
  1387.  
  1388. local function acceptEasyComponent(address, componentType)
  1389.     if easyAssign.step == "reactor" then
  1390.         if componentType ~= "draconic_reactor" then
  1391.             easyAssign.message = "Нужен draconic_reactor."
  1392.             screenAssignEasy()
  1393.             return
  1394.         end
  1395.         if isAddressUsed(address) then
  1396.             easyAssign.message = "Адрес уже используется."
  1397.             screenAssignEasy()
  1398.             return
  1399.         end
  1400.         easyAssign.core = address
  1401.         easyAssign.step = "shield"
  1402.         easyAssign.message = "Реактор принят."
  1403.         maybeChatSay("accepted", "Реактор принят. Смотри монитор.")
  1404.         screenAssignEasy()
  1405.         return
  1406.     end
  1407.     if easyAssign.step == "shield" then
  1408.         if componentType ~= "flux_gate" then
  1409.             easyAssign.message = "Нужен flux_gate."
  1410.             screenAssignEasy()
  1411.             return
  1412.         end
  1413.         if isAddressUsed(address) then
  1414.             easyAssign.message = "Адрес уже используется."
  1415.             screenAssignEasy()
  1416.             return
  1417.         end
  1418.         easyAssign.shield = address
  1419.         easyAssign.step = "output"
  1420.         easyAssign.message = "Гейт щитов принят."
  1421.         maybeChatSay("accepted", "Гейт щитов принят. Смотри монитор.")
  1422.         screenAssignEasy()
  1423.         return
  1424.     end
  1425.     if easyAssign.step == "output" then
  1426.         if componentType ~= "flux_gate" then
  1427.             easyAssign.message = "Нужен flux_gate."
  1428.             screenAssignEasy()
  1429.             return
  1430.         end
  1431.         if isAddressUsed(address) or address == easyAssign.shield then
  1432.             easyAssign.message = "Нужны два разных гейта."
  1433.             screenAssignEasy()
  1434.             return
  1435.         end
  1436.         easyAssign.output = address
  1437.         local saved = saveAssignedGates(easyAssign.core, easyAssign.shield, easyAssign.output)
  1438.         if not saved then
  1439.             easyAssign.message = "Не удалось сохранить."
  1440.             screenAssignEasy()
  1441.             return
  1442.         end
  1443.         easyAssign.saved = true
  1444.         easyAssign.step = "done"
  1445.         easyAssign.message = "Успешно сохранено."
  1446.         maybeChatSay("accepted", "Успешно сохранено. Смотри монитор.")
  1447.         screenAssignEasy()
  1448.         return
  1449.     end
  1450. end
  1451.  
  1452. local function handleComponentAdded(_, address, componentType)
  1453.     if not handleAssign or assignMode ~= "easy" or not easyAssign.active then
  1454.         return
  1455.     end
  1456.     acceptEasyComponent(address, componentType)
  1457. end
  1458.  
  1459. local function pollEasyAssign()
  1460.     if not handleAssign or assignMode ~= "easy" or not easyAssign.active then
  1461.         return
  1462.     end
  1463.     if easyAssign.step == "reactor" then
  1464.         local addr = findNewComponent("draconic_reactor")
  1465.         if addr then
  1466.             acceptEasyComponent(addr, "draconic_reactor")
  1467.         end
  1468.     elseif easyAssign.step == "shield" or easyAssign.step == "output" then
  1469.         local addr = findNewComponent("flux_gate")
  1470.         if addr then
  1471.             acceptEasyComponent(addr, "flux_gate")
  1472.         end
  1473.     end
  1474. end
  1475.  
  1476. local function refreshReactors(openAssign)
  1477.     autostateByAdr = {}
  1478.     flowInitByAdr = {}
  1479.     autoWaitTempByAdr = {}
  1480.     chargeRequestByAdr = {}
  1481.     for i = 1, #reactors do
  1482.         local r = reactors[i]
  1483.         autostateByAdr[r.reactorAdr] = r.autostate
  1484.         flowInitByAdr[r.reactorAdr] = r.flowInit
  1485.         chargeRequestByAdr[r.reactorAdr] = r.chargeRequested
  1486.     end
  1487.     syncActive()
  1488.     local prevAdr = reactors[activeIndex] and reactors[activeIndex].reactorAdr
  1489.     reactors = buildReactors() or reactors
  1490.     local idx = 1
  1491.     if pendingActiveReactorAdr then
  1492.         for i = 1, #reactors do
  1493.             if reactors[i].reactorAdr == pendingActiveReactorAdr then
  1494.                 idx = i
  1495.                 break
  1496.             end
  1497.         end
  1498.         pendingActiveReactorAdr = nil
  1499.     elseif lastAddedReactorAdr then
  1500.         for i = 1, #reactors do
  1501.             if reactors[i].reactorAdr == lastAddedReactorAdr then
  1502.                 idx = i
  1503.                 break
  1504.             end
  1505.         end
  1506.         lastAddedReactorAdr = nil
  1507.     elseif prevAdr then
  1508.         for i = 1, #reactors do
  1509.             if reactors[i].reactorAdr == prevAdr then
  1510.                 idx = i
  1511.                 break
  1512.             end
  1513.         end
  1514.     end
  1515.     setActive(idx)
  1516.     if openAssign and newEntriesAdded then
  1517.         resetAssignState()
  1518.         screenAssign()
  1519.         return
  1520.     end
  1521.     if handleMain then
  1522.         screenMain()
  1523.     else
  1524.         screenOverview()
  1525.     end
  1526. end
  1527.  
  1528. local function resetAssignState()
  1529.     assignState.reactorIndex = nil
  1530.     assignState.reactorAdr = nil
  1531.     assignState.shieldGateIdx = nil
  1532.     assignState.shieldGateAdr = nil
  1533.     assignState.outGateIdx = nil
  1534.     assignState.outGateAdr = nil
  1535.     assignState.step = "reactor"
  1536. end
  1537.  
  1538. saveAssignedGates = function(reactorAdr, shieldAdr, outAdr)
  1539.     if not reactorAdr or not shieldAdr or not outAdr then
  1540.         return false
  1541.     end
  1542.     local entries = readReactorsFile() or {}
  1543.     local updated = false
  1544.     for i = 1, #entries do
  1545.         if entries[i].reactorAdr == reactorAdr then
  1546.             entries[i].upAdr = outAdr
  1547.             entries[i].downAdr = shieldAdr
  1548.             updated = true
  1549.             break
  1550.         end
  1551.     end
  1552.     if not updated then
  1553.         entries[#entries + 1] = {
  1554.             reactorAdr = reactorAdr,
  1555.             upAdr = outAdr,
  1556.             downAdr = shieldAdr
  1557.         }
  1558.     end
  1559.     if #entries > MAX_REACTORS then
  1560.         local trimmed = {}
  1561.         for i = 1, MAX_REACTORS do
  1562.             trimmed[i] = entries[i]
  1563.         end
  1564.         entries = trimmed
  1565.     end
  1566.     writeReactorsFile(entries)
  1567.     pendingActiveReactorAdr = reactorAdr
  1568.     return true
  1569. end
  1570.  
  1571. local function clearAssignedGates(reactorIndex)
  1572.     local reactorsList = listAddresses("draconic_reactor")
  1573.     if not reactorsList[reactorIndex] then
  1574.         return false
  1575.     end
  1576.     local entries = readReactorsFile() or {}
  1577.     local newEntries = {}
  1578.     for i = 1, #entries do
  1579.         if entries[i].reactorAdr ~= reactorsList[reactorIndex] then
  1580.             newEntries[#newEntries + 1] = entries[i]
  1581.         end
  1582.     end
  1583.     writeReactorsFile(newEntries)
  1584.     return true
  1585. end
  1586.  
  1587. screenOverview = function()
  1588.     handleMain = false
  1589.     handleStart = false
  1590.     handleOptions = false
  1591.     handleOverview = true
  1592.     handleAssign = false
  1593.     local x = drawHeader("HiTech Classic is the best")
  1594.     reactors = reactors or {}
  1595.     local cols = overviewCols(x)
  1596.     term.setCursor(1, 2)
  1597.     menuItem.options.x, menuItem.options.y = term.getCursor()
  1598.     gpu.setForeground(color.blue)
  1599.     printf(menuItem.options.name)
  1600.     menuItem.options.w = textWidth(menuItem.options.name)
  1601.     gpu.setForeground(color.white)
  1602.     menuItem.overview.x, menuItem.overview.y = term.getCursor()
  1603.     gpu.setForeground(color.blue)
  1604.     printf(menuItem.overview.name)
  1605.     menuItem.overview.w = textWidth(menuItem.overview.name)
  1606.     gpu.setForeground(color.white)
  1607.     menuItem.refresh.x, menuItem.refresh.y = term.getCursor()
  1608.     gpu.setForeground(color.blue)
  1609.     printf(menuItem.refresh.name)
  1610.     menuItem.refresh.w = textWidth(menuItem.refresh.name)
  1611.     gpu.setForeground(color.white)
  1612.     menuItem.gates.x, menuItem.gates.y = term.getCursor()
  1613.     gpu.setForeground(color.blue)
  1614.     printf(menuItem.gates.name)
  1615.     menuItem.gates.w = textWidth(menuItem.gates.name)
  1616.     gpu.setForeground(color.white)
  1617.     for i = 1, #reactors do
  1618.         local label = string.format("[R%d]", i)
  1619.         reactors[i].tab.x, reactors[i].tab.y = term.getCursor()
  1620.         reactors[i].tab.w = textWidth(label)
  1621.         if i == activeIndex then
  1622.             guiWrite(reactors[i].tab.x, reactors[i].tab.y, color.black, color.orange, "%s", label)
  1623.         else
  1624.             guiWrite(reactors[i].tab.x, reactors[i].tab.y, color.blue, color.white, "%s", label)
  1625.         end
  1626.         printf(" ")
  1627.     end
  1628.     drawSeparator(x)
  1629.     gpu.setBackground(color.grey)
  1630.     gpu.setForeground(color.white)
  1631.     gpu.fill(1, 4, x, 21, " ")
  1632.     if #reactors == 0 then
  1633.         term.setCursor(2, 5)
  1634.         printf("Нет настроенных реакторов.")
  1635.         term.setCursor(2, 6)
  1636.         printf("Откройте \"Гейты\" и назначьте гейты.")
  1637.         return
  1638.     end
  1639.  
  1640.     gpu.setBackground(color.panel)
  1641.     gpu.setForeground(color.white)
  1642.     gpu.fill(2, 4, x - 2, 6, " ")
  1643.     term.setCursor(3, 4)
  1644.     printf("Сводка")
  1645.     term.setCursor(3, 5)
  1646.     printf("Суммарная прибыль:")
  1647.     term.setCursor(28, 5)
  1648.     getCord(summaryProfitCord)
  1649.     term.setCursor(3, 6)
  1650.     printf("Суммарная EU:")
  1651.     term.setCursor(28, 6)
  1652.     getCord(summaryEuCord)
  1653.     term.setCursor(3, 7)
  1654.     printf("Тратим на щиты RF:")
  1655.     term.setCursor(28, 7)
  1656.     getCord(summaryShieldCord)
  1657.     term.setCursor(3, 8)
  1658.     printf("Суммарная выработка:")
  1659.     term.setCursor(28, 8)
  1660.     getCord(summaryGenCord)
  1661.     term.setCursor(3, 9)
  1662.     printf("Максимум прибыли RF/EU:")
  1663.     term.setCursor(28, 9)
  1664.     getCord(summaryMaxProfitCord)
  1665.     local autoX = math.max(40, x - 33)
  1666.     term.setCursor(autoX, 4)
  1667.     printf("Выключены автоматически:")
  1668.     term.setCursor(autoX, 5)
  1669.     getCord(autoStoppedCord)
  1670.  
  1671.     gpu.setBackground(color.dark)
  1672.     gpu.setForeground(color.white)
  1673.     gpu.fill(2, 11, x - 2, 1, " ")
  1674.     term.setCursor(2, 11)
  1675.     printf("#")
  1676.     term.setCursor(cols.status, 11)
  1677.     printf("СТАТУС")
  1678.     term.setCursor(cols.temp, 11)
  1679.     printf(" ТЕМП")
  1680.     term.setCursor(cols.fuel, 11)
  1681.     printf("ТОПЛИВО")
  1682.     term.setCursor(cols.profit, 11)
  1683.     printf("ПРИБЫЛЬ")
  1684.     term.setCursor(cols.gen, 11)
  1685.     printf("ГЕН")
  1686.     term.setCursor(cols.flow, 11)
  1687.     printf("ПОТОК")
  1688.     term.setCursor(cols.ctrl, 11)
  1689.     printf("УПР")
  1690.     gpu.setForeground(color.dim)
  1691.     gpu.fill(2, 12, x - 2, 1, "-")
  1692.     gpu.setForeground(color.white)
  1693.     gpu.setBackground(color.grey)
  1694.     local rowY = 13
  1695.     for i = 1, #reactors do
  1696.         local r = reactors[i]
  1697.         term.setCursor(2, rowY)
  1698.         printf("#%d", i)
  1699.         term.setCursor(cols.status, rowY)
  1700.         r.row.status = {x = cols.status, y = rowY}
  1701.         term.setCursor(cols.temp, rowY)
  1702.         r.row.temp = {x = cols.temp, y = rowY}
  1703.         term.setCursor(cols.fuel, rowY)
  1704.         r.row.fuel = {x = cols.fuel, y = rowY}
  1705.         term.setCursor(cols.profit, rowY)
  1706.         r.row.profit = {x = cols.profit, y = rowY}
  1707.         term.setCursor(cols.gen, rowY)
  1708.         r.row.gen = {x = cols.gen, y = rowY}
  1709.         term.setCursor(cols.flow, rowY)
  1710.         r.row.flow = {x = cols.flow, y = rowY}
  1711.         term.setCursor(cols.ctrl, rowY)
  1712.         r.control = r.control or {}
  1713.         r.control.dec = {x = cols.ctrl, y = rowY, w = 3}
  1714.         term.setCursor(cols.ctrl + 4, rowY)
  1715.         r.control.inc = {x = cols.ctrl + 4, y = rowY, w = 3}
  1716.         term.setCursor(cols.ctrl + 8, rowY)
  1717.         r.control.auto = {x = cols.ctrl + 8, y = rowY, w = 4}
  1718.         term.setCursor(cols.ctrl + 13, rowY)
  1719.         r.powerBtn.x, r.powerBtn.y = term.getCursor()
  1720.         r.powerBtn.w = 3
  1721.         rowY = rowY + 1
  1722.     end
  1723. end
  1724.  
  1725. local function screenMain()
  1726.     handleOptions = false
  1727.     handleStart = false
  1728.     handleMain = true
  1729.     handleOverview = false
  1730.     handleAssign = false
  1731.     if not reactor or not upgate or not downgate then
  1732.         screenOverview()
  1733.         return
  1734.     end
  1735.     if reactors[activeIndex] and (not isComponentAlive(reactors[activeIndex].reactorAdr) or
  1736.         not isComponentAlive(reactors[activeIndex].upAdr) or
  1737.         not isComponentAlive(reactors[activeIndex].downAdr)) then
  1738.         refreshReactors(false)
  1739.         screenOverview()
  1740.         return
  1741.     end
  1742.     local x = drawHeader("HiTech Classic is the best")
  1743.     local ok, inf = pcall(reactor.getReactorInfo)
  1744.     if not ok or type(inf) ~= "table" then
  1745.         refreshReactors(false)
  1746.         screenOverview()
  1747.         return
  1748.     end
  1749.     if reactors[activeIndex] and (not isComponentAlive(reactors[activeIndex].reactorAdr) or
  1750.         not isComponentAlive(reactors[activeIndex].upAdr) or
  1751.         not isComponentAlive(reactors[activeIndex].downAdr)) then
  1752.         refreshReactors(false)
  1753.         screenOverview()
  1754.         return
  1755.     end
  1756.     infcord.status.value = inf.status
  1757.     menuItem.options.x, menuItem.options.y = term.getCursor()
  1758.     gpu.setForeground(color.blue)
  1759.     printf(menuItem.options.name)
  1760.     menuItem.options.w = textWidth(menuItem.options.name)
  1761.     gpu.setForeground(color.white)
  1762.     menuItem.overview.x, menuItem.overview.y = term.getCursor()
  1763.     gpu.setForeground(color.blue)
  1764.     printf(menuItem.overview.name)
  1765.     menuItem.overview.w = textWidth(menuItem.overview.name)
  1766.     gpu.setForeground(color.white)
  1767.     menuItem.refresh.x, menuItem.refresh.y = term.getCursor()
  1768.     gpu.setForeground(color.blue)
  1769.     printf(menuItem.refresh.name)
  1770.     menuItem.refresh.w = textWidth(menuItem.refresh.name)
  1771.     gpu.setForeground(color.white)
  1772.     menuItem.gates.x, menuItem.gates.y = term.getCursor()
  1773.     gpu.setForeground(color.blue)
  1774.     printf(menuItem.gates.name)
  1775.     menuItem.gates.w = textWidth(menuItem.gates.name)
  1776.     gpu.setForeground(color.white)
  1777.     for i = 1, #reactors do
  1778.         local label = string.format("[R%d]", i)
  1779.         reactors[i].tab.x, reactors[i].tab.y = term.getCursor()
  1780.         reactors[i].tab.w = textWidth(label)
  1781.         if i == activeIndex then
  1782.             guiWrite(reactors[i].tab.x, reactors[i].tab.y, color.black, color.orange, "%s", label)
  1783.         else
  1784.             guiWrite(reactors[i].tab.x, reactors[i].tab.y, color.blue, color.white, "%s", label)
  1785.         end
  1786.         printf(" ")
  1787.     end
  1788.     guiWrite(x - 25, 1, color.yellow, color.panel, "Макс. выраб.: ")
  1789.     getCord(infcord.maxgen)
  1790.     if inf.generationRate > maxGenerationRate then
  1791.         maxGenerationRate = inf.generationRate
  1792.     end
  1793.     guiWrite(infcord.maxgen.x, infcord.maxgen.y, color.green, color.panel, "%s", numformat(maxGenerationRate))
  1794.     drawSeparator(x)
  1795.     gpu.setBackground(color.panel)
  1796.     gpu.setForeground(color.blue)
  1797.     printf(" ПАРАМЕТРЫ ")
  1798.     term.setCursor(30, 4)
  1799.     printf(" УПРАВЛЕНИЕ ")
  1800.     gpu.setBackground(color.grey)
  1801.     gpu.setForeground(color.white)
  1802.     term.setCursor(1, 6)
  1803.     gpu.setBackground(color.panel)
  1804.     printf(" Температура:     ")
  1805.     gpu.setBackground(color.grey)
  1806.     printf(" ")
  1807.     getCord(infcord.temp)
  1808.     guiWrite(infcord.temp.x, infcord.temp.y, -1, -1, "%0.2f\n", inf.temperature)
  1809.     printf(" Вырабатывает:     ")
  1810.     getCord(infcord.generation)
  1811.     guiWrite(infcord.generation.x, infcord.generation.y, -1, -1, "%s  \n", numformat(inf.generationRate))
  1812.     gpu.setBackground(color.panel)
  1813.     printf(" Поток:           ")
  1814.     gpu.setBackground(color.grey)
  1815.     printf(" ")
  1816.     getCord(infcord.flow)
  1817.     infcord.flow.value = getGateFlow(upgate)
  1818.     guiWrite(infcord.flow.x, infcord.flow.y, -1, -1, "%s  ", numformat(infcord.flow.value))
  1819.     getCord(button.add)
  1820.     makeButton(button.add.x + 4, button.add.y, button.add.name)
  1821.     getCord(button.sub)
  1822.     makeButton(button.sub.x + 2, button.sub.y, button.sub.name)
  1823.  
  1824.     printf("\n")
  1825.     printf(" Итоговая мосч:    ")
  1826.     getCord(infcord.puregen)
  1827.     guiWrite(infcord.puregen.x, infcord.puregen.y, -1, -1, "%s  \n", numformat(inf.generationRate - getGateFlow(downgate)))
  1828.     gpu.setBackground(color.panel)
  1829.     printf(" Поглощает:       ")
  1830.     gpu.setBackground(color.grey)
  1831.     printf(" ")
  1832.     getCord(infcord.drain)
  1833.     guiWrite(infcord.drain.x, infcord.drain.y, -1, -1, "%s  \n", numformat(getGateFlow(downgate)))
  1834.  
  1835.     printf(" Мощность поля:    ")
  1836.     getCord(infcord.fstrth)
  1837.     if inf.maxFieldStrength > 0 then
  1838.         guiWrite(infcord.fstrth.x, infcord.fstrth.y, -1, -1, "%d / %d (%0.2f %%) \n",
  1839.             inf.fieldStrength - inf.fieldDrainRate,
  1840.             inf.maxFieldStrength,
  1841.             ((inf.fieldStrength - inf.fieldDrainRate) / inf.maxFieldStrength) * 100)
  1842.     else
  1843.         guiWrite(infcord.fstrth.x, infcord.fstrth.y, -1, -1, "0 / 0 (0 %%) \n")
  1844.     end
  1845.     gpu.setBackground(color.panel)
  1846.     printf(" Насыщенность:    ")
  1847.     gpu.setBackground(color.grey)
  1848.     printf(" ")
  1849.     getCord(infcord.esturn)
  1850.     if inf.maxEnergySaturation > 0 then
  1851.         guiWrite(infcord.esturn.x, infcord.esturn.y, -1, -1, "%d / %d (%0.2f %%) \n",
  1852.             inf.energySaturation,
  1853.             inf.maxEnergySaturation,
  1854.             (inf.energySaturation / inf.maxEnergySaturation) * 100)
  1855.     else
  1856.         guiWrite(infcord.esturn.x, infcord.esturn.y, -1, -1, "0 / 0 (0 %%) \n")
  1857.     end
  1858.     printf(" Топливо:          ")
  1859.     getCord(infcord.fuel)
  1860.  
  1861.     if inf.fuelConversion / inf.maxFuelConversion < 0.78 then
  1862.         guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "%d / %d (%0.2f %%)                           \n",
  1863.             inf.fuelConversion,
  1864.             inf.maxFuelConversion,
  1865.             (inf.fuelConversion / inf.maxFuelConversion) * 100)
  1866.     else
  1867.         if inf.maxFuelConversion > 0 then
  1868.             if inf.status == "online" or inf.status == "charging" or inf.status == "charged" then
  1869.                 guiWrite(infcord.fuel.x, infcord.fuel.y, color.red, -1, "%d / %d (%0.2f %%) Критический уровень топлива\n",
  1870.                     inf.fuelConversion,
  1871.                     inf.maxFuelConversion,
  1872.                     (inf.fuelConversion / inf.maxFuelConversion) * 100)
  1873.             else
  1874.                 guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "%d / %d (%0.2f %%) Критический уровень топлива\n",
  1875.                     inf.fuelConversion,
  1876.                     inf.maxFuelConversion,
  1877.                     (inf.fuelConversion / inf.maxFuelConversion) * 100)
  1878.             end
  1879.         else
  1880.             guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "0 / 0 (0 %%)                           \n")
  1881.         end
  1882.     end
  1883.     gpu.setBackground(color.panel)
  1884.     printf(" Расход топлива:  ")
  1885.     gpu.setBackground(color.grey)
  1886.     printf(" ")
  1887.     getCord(infcord.fuelconv)
  1888.     guiWrite(infcord.fuelconv.x, infcord.fuelconv.y, -1, -1, "%d\n", inf.fuelConversionRate)
  1889.  
  1890.     getCord(infcord.core)
  1891.     if com.isAvailable("draconic_rf_storage") then
  1892.         guiWrite(infcord.core.x, infcord.core.y, -1, -1, coreformat())
  1893.     end
  1894.     printf("\n")
  1895.     button.stop.x, button.stop.y = term.getCursor()
  1896.     button.start.x, button.start.y = button.stop.x, button.stop.y + 1
  1897.     button.charge.x, button.charge.y = button.stop.x, button.stop.y + 2
  1898.     button.stop.w = textWidth(button.stop.name)
  1899.     button.start.w = textWidth(button.start.name)
  1900.     button.charge.w = textWidth(button.charge.name)
  1901.     local controlsY = button.stop.y + 3
  1902.     if not chargeHintDismissed then
  1903.         local hint1 = "Перед первым запуском поставь поток на 535 000."
  1904.         local hint2 = "Потом включай."
  1905.         chargeHint.x = 1
  1906.         chargeHint.y = math.max(1, screenH - 1)
  1907.         local hintY2 = math.min(screenH, chargeHint.y + 1)
  1908.         local prevFg = gpu.getForeground()
  1909.         local prevBg = gpu.getBackground()
  1910.         gpu.setForeground(color.green)
  1911.         gpu.setBackground(color.grey)
  1912.         gpu.set(chargeHint.x, chargeHint.y, hint1)
  1913.         if hintY2 ~= chargeHint.y then
  1914.             gpu.set(chargeHint.x, hintY2, hint2)
  1915.         end
  1916.         gpu.setForeground(prevFg)
  1917.         gpu.setBackground(prevBg)
  1918.         chargeHint.w = math.max(textWidth(hint1), textWidth(hint2))
  1919.         chargeHint.visible = true
  1920.     else
  1921.         chargeHint.visible = false
  1922.     end
  1923.     term.setCursor(button.stop.x, controlsY)
  1924.     printf("                   Автономный режим: ")
  1925.     getCord(button.autoon)
  1926.     getCord(button.autooff)
  1927.     if not autostate then
  1928.         guiWrite(button.autooff.x, button.autooff.y, color.red, color.black, "%s\n", button.autooff.name)
  1929.     else
  1930.         guiWrite(button.autoon.x, button.autoon.y, color.green, color.black, "%s\n", button.autoon.name)
  1931.     end
  1932.     checkButtons()
  1933. end
  1934.  
  1935. local function refreshOverview()
  1936.     if not handleOverview then
  1937.         return
  1938.     end
  1939.     if not reactors or #reactors == 0 then
  1940.         return
  1941.     end
  1942.     gpu.setForeground(color.white)
  1943.     local stopPct = cfg.fuelStopPct or 98
  1944.     local totalGen = 0
  1945.     local totalProfit = 0
  1946.     local totalShield = 0
  1947.     local maxProfit = 0
  1948.     gpu.setBackground(color.grey)
  1949.     for i = 1, #reactors do
  1950.         local r = reactors[i]
  1951.         local ok, inf = pcall(r.reactor.getReactorInfo)
  1952.         if not ok or type(inf) ~= "table" then
  1953.             guiWrite(r.row.status.x, r.row.status.y, color.red, -1, "ERR")
  1954.             guiWrite(r.row.temp.x, r.row.temp.y, color.red, -1, "----")
  1955.             guiWrite(r.row.fuel.x, r.row.fuel.y, color.red, -1, "----")
  1956.             guiWrite(r.row.profit.x, r.row.profit.y, color.red, -1, "----")
  1957.             guiWrite(r.row.gen.x, r.row.gen.y, color.red, -1, "----")
  1958.             guiWrite(r.row.flow.x, r.row.flow.y, color.red, -1, "----")
  1959.         else
  1960.             totalGen = totalGen + inf.generationRate
  1961.             guiWrite(r.row.gen.x, r.row.gen.y, -1, -1, "%s", numformat(inf.generationRate))
  1962.             local flow = getGateFlow(r.upgate)
  1963.             guiWrite(r.row.flow.x, r.row.flow.y, -1, -1, "%s", numformat(flow))
  1964.             local shieldFlow = getGateFlow(r.downgate)
  1965.             totalShield = totalShield + shieldFlow
  1966.             local profit = inf.generationRate - shieldFlow
  1967.             totalProfit = totalProfit + profit
  1968.             if profit > maxProfit then
  1969.                 maxProfit = profit
  1970.             end
  1971.             guiWrite(r.row.profit.x, r.row.profit.y, -1, -1, "%s", numformat(profit))
  1972.             local tempColor = color.white
  1973.             if inf.temperature >= cfg.tempCriticalEdge then
  1974.                 tempColor = color.red
  1975.             elseif inf.temperature >= cfg.safeModeTempWaitEdge then
  1976.                 tempColor = color.yellow
  1977.             end
  1978.             guiWrite(r.row.temp.x, r.row.temp.y, tempColor, -1, "%4.0fC", inf.temperature)
  1979.             local fuelPct = 0
  1980.             if inf.maxFuelConversion > 0 then
  1981.                 fuelPct = (inf.fuelConversion / inf.maxFuelConversion) * 100
  1982.             end
  1983.             if autoStopped[r.reactorAdr] and (inf.status == "online" or inf.status == "charging" or inf.status == "charged") then
  1984.                 autoStopped[r.reactorAdr] = nil
  1985.             end
  1986.             if fuelPct >= stopPct and (inf.status == "online" or inf.status == "charging" or inf.status == "charged") then
  1987.                 if not autoStopped[r.reactorAdr] then
  1988.                     r.reactor.stopReactor()
  1989.                     autoStopped[r.reactorAdr] = true
  1990.                 end
  1991.             end
  1992.             local fuelColor = color.white
  1993.             if fuelPct >= 80 then
  1994.                 fuelColor = color.red
  1995.             end
  1996.             guiWrite(r.row.fuel.x, r.row.fuel.y, fuelColor, -1, "%5.1f%%", fuelPct)
  1997.             local stColor = color.white
  1998.             if inf.status == "online" or inf.status == "charging" or inf.status == "charged" then
  1999.                 stColor = color.green
  2000.             elseif inf.status == "offline" or inf.status == "stopping" then
  2001.                 stColor = color.red
  2002.             end
  2003.             guiWrite(r.row.status.x, r.row.status.y, stColor, -1, "%s", statusText(inf.status))
  2004.             makePowerButton(r.control.dec.x, r.control.dec.y, "[-]", color.dim, color.white)
  2005.             makePowerButton(r.control.inc.x, r.control.inc.y, "[+]", color.dim, color.white)
  2006.             local autoColor = r.autostate and color.green or color.red
  2007.             makePowerButton(r.control.auto.x, r.control.auto.y, "AUTO", autoColor, color.white)
  2008.             if inf.status == "online" or inf.status == "charging" or inf.status == "charged" then
  2009.                 r.powerBtn.action = "stop"
  2010.                 makePowerButton(r.powerBtn.x, r.powerBtn.y, "PWR", color.red, color.white)
  2011.             else
  2012.                 r.powerBtn.action = "start"
  2013.                 makePowerButton(r.powerBtn.x, r.powerBtn.y, "PWR", color.green, color.white)
  2014.             end
  2015.         end
  2016.     end
  2017.     if totalProfit > maxSummaryProfit then
  2018.         maxSummaryProfit = totalProfit
  2019.     end
  2020.     guiWrite(summaryProfitCord.x, summaryProfitCord.y, color.white, color.panel, "%s", numformat(totalProfit))
  2021.     guiWrite(summaryEuCord.x, summaryEuCord.y, color.white, color.panel, "%s", numformat(math.floor(totalProfit / 8)))
  2022.     guiWrite(summaryShieldCord.x, summaryShieldCord.y, color.white, color.panel, "%s", numformat(totalShield))
  2023.     guiWrite(summaryGenCord.x, summaryGenCord.y, color.white, color.panel, "%s", numformat(totalGen))
  2024.     guiWrite(summaryMaxProfitCord.x, summaryMaxProfitCord.y, color.white, color.panel, "%s / %s",
  2025.         numformat(maxSummaryProfit),
  2026.         numformat(math.floor(maxSummaryProfit / 8)))
  2027.     local autoList = {}
  2028.     for i = 1, #reactors do
  2029.         if autoStopped[reactors[i].reactorAdr] then
  2030.             autoList[#autoList + 1] = "R" .. i
  2031.         end
  2032.     end
  2033.     local autoText = "нет"
  2034.     if #autoList > 0 then
  2035.         autoText = table.concat(autoList, ", ")
  2036.     end
  2037.     guiWrite(autoStoppedCord.x, autoStoppedCord.y, color.red, -1, "%s", autoText)
  2038. end
  2039.  
  2040. local screen = {}
  2041. screen["overview"] = screenOverview
  2042. screen["detail"] = screenMain
  2043. screen["options"] = screenOptions
  2044.  
  2045. local function editOption(param)
  2046.     local rd = true
  2047.     while rd do
  2048.         gpu.fill(param.x, param.y, #tostring(param.value), 1, " ")
  2049.         term.setCursor(param.x, param.y)
  2050.  
  2051.         param.value = term.read(false, false)
  2052.         local val = tonumber(param.value)
  2053.         if val then
  2054.             param.value = val
  2055.             show(color.black, color.grey, param)
  2056.             rd = false
  2057.         end
  2058.     end
  2059.     canedit = true
  2060.     canclose = true
  2061.     screen.options()
  2062. end
  2063.  
  2064. local function add(n, i)
  2065.     n.value = n.value + i
  2066.     upgate.setSignalLowFlow(n.value)
  2067.     return n.value
  2068. end
  2069.  
  2070. local function sub(n, i)
  2071.     term.setCursor(n.x, n.y)
  2072.     n.value = n.value - i
  2073.     return n.value
  2074. end
  2075.  
  2076. local function refreshInfo()
  2077.     local ok, inf = pcall(reactor.getReactorInfo)
  2078.     if not ok or type(inf) ~= "table" then
  2079.         refreshReactors(false)
  2080.         screenOverview()
  2081.         return
  2082.     end
  2083.     infcord.status.value = inf.status
  2084.     gpu.setForeground(color.white)
  2085.     local tbuf1 = inf.temperature
  2086.     os.sleep(0.1)
  2087.     local ok2, inf2 = pcall(reactor.getReactorInfo)
  2088.     local tbuf2 = ok2 and type(inf2) == "table" and inf2.temperature or tbuf1
  2089.     if handleMain then
  2090.         local stopPct = cfg.fuelStopPct or 98
  2091.         if cfg.autoProtectTempEdge and inf.temperature >= cfg.autoProtectTempEdge and not autostate then
  2092.             autostate = true
  2093.             guiWrite(button.autoon.x, button.autoon.y, color.green, color.black, "%s\n", button.autoon.name)
  2094.         end
  2095.     end
  2096.     local fuelPct = 0
  2097.     if inf.maxFuelConversion > 0 then
  2098.         fuelPct = (inf.fuelConversion / inf.maxFuelConversion) * 100
  2099.     end
  2100.     if autoStopped[reactors[activeIndex].reactorAdr] and (inf.status == "online" or inf.status == "charging" or inf.status == "charged") then
  2101.         autoStopped[reactors[activeIndex].reactorAdr] = nil
  2102.     end
  2103.     if fuelPct >= stopPct and (inf.status == "online" or inf.status == "charging" or inf.status == "charged") then
  2104.         if not autoStopped[reactors[activeIndex].reactorAdr] then
  2105.             reactor.stopReactor()
  2106.             autoStopped[reactors[activeIndex].reactorAdr] = true
  2107.         end
  2108.     end
  2109.     if tbuf2 > tbuf1 then
  2110.         guiWrite(infcord.temp.x, infcord.temp.y, color.red, -1, "%0.2f \n", inf.temperature)
  2111.         temprise = true
  2112.     elseif tbuf2 < tbuf1 then
  2113.         guiWrite(infcord.temp.x, infcord.temp.y, color.blue, -1, "%0.2f \n", inf.temperature)
  2114.         temprise = false
  2115.     else
  2116.         if inf.temperature == 2000 then
  2117.             guiWrite(infcord.temp.x, infcord.temp.y, -1, -1, "%0.2f \n", inf.temperature)
  2118.         end
  2119.     end
  2120.     guiWrite(infcord.generation.x, infcord.generation.y, -1, -1, "%s\n", numformat(inf.generationRate))
  2121.     if inf.generationRate > maxGenerationRate then
  2122.         maxGenerationRate = inf.generationRate
  2123.     end
  2124.     guiWrite(infcord.maxgen.x, infcord.maxgen.y, color.green, color.panel, "%s", numformat(maxGenerationRate))
  2125.     infcord.flow.value = getGateFlow(upgate)
  2126.     guiWrite(infcord.flow.x, infcord.flow.y, -1, -1, "%s", numformat(infcord.flow.value))
  2127.     guiWrite(infcord.puregen.x, infcord.puregen.y, -1, -1, "%s\n", numformat(inf.generationRate - getGateFlow(downgate)))
  2128.     infcord.drain.value = inf.fieldDrainRate
  2129.     guiWrite(infcord.drain.x, infcord.drain.y, -1, -1, "%s\n", numformat(getGateFlow(downgate)))
  2130.     if inf.maxFieldStrength > 0 then
  2131.         guiWrite(infcord.fstrth.x, infcord.fstrth.y, -1, -1, "%d / %d (%0.2f %%) \n",
  2132.             inf.fieldStrength - inf.fieldDrainRate,
  2133.             inf.maxFieldStrength,
  2134.             ((inf.fieldStrength - inf.fieldDrainRate) / inf.maxFieldStrength) * 100)
  2135.     else
  2136.         guiWrite(infcord.fstrth.x, infcord.fstrth.y, -1, -1, "0 / 0 (0 %%) \n")
  2137.     end
  2138.     if inf.maxEnergySaturation > 0 then
  2139.         guiWrite(infcord.esturn.x, infcord.esturn.y, -1, -1, "%d / %d (%0.2f %%) \n",
  2140.             inf.energySaturation,
  2141.             inf.maxEnergySaturation,
  2142.             (inf.energySaturation / inf.maxEnergySaturation) * 100)
  2143.     else
  2144.         guiWrite(infcord.esturn.x, infcord.esturn.y, -1, -1, "0 / 0 (0 %%) \n")
  2145.     end
  2146.     if inf.fuelConversion / inf.maxFuelConversion < 0.78 then
  2147.         guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "%d / %d (%0.2f %%) \n",
  2148.             inf.fuelConversion,
  2149.             inf.maxFuelConversion,
  2150.             (inf.fuelConversion / inf.maxFuelConversion) * 100)
  2151.     else
  2152.         if inf.maxFuelConversion > 0 then
  2153.             if inf.status == "online" or inf.status == "charging" or inf.status == "charged" then
  2154.                 guiWrite(infcord.fuel.x, infcord.fuel.y, color.red, -1, "%d / %d (%0.2f %%) Критический уровень топлива\n",
  2155.                     inf.fuelConversion,
  2156.                     inf.maxFuelConversion,
  2157.                     (inf.fuelConversion / inf.maxFuelConversion) * 100)
  2158.             else
  2159.                 guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "%d / %d (%0.2f %%) Критический уровень топлива\n",
  2160.                     inf.fuelConversion,
  2161.                     inf.maxFuelConversion,
  2162.                     (inf.fuelConversion / inf.maxFuelConversion) * 100)
  2163.             end
  2164.         else
  2165.             guiWrite(infcord.fuel.x, infcord.fuel.y, -1, -1, "0 / 0 (0 %%)\n")
  2166.         end
  2167.     end
  2168.     guiWrite(infcord.fuelconv.x, infcord.fuelconv.y, -1, -1, "%d\n", inf.fuelConversionRate)
  2169.     if com.isAvailable("draconic_rf_storage") then
  2170.         guiWrite(infcord.core.x, infcord.core.y, -1, -1, coreformat())
  2171.         if button.stop.y ~= infcord.core.y + 2 then
  2172.             screenMain()
  2173.         end
  2174.     elseif button.stop.y ~= infcord.fuelconv.y + 2 then
  2175.         screenMain()
  2176.     end
  2177.     checkButtons()
  2178.     syncActive()
  2179. end
  2180.  
  2181. local function autoForReactor(r, state)
  2182.     if not r or not r.reactor or not r.upgate or not r.downgate then
  2183.         return
  2184.     end
  2185.     local ok, inf = pcall(r.reactor.getReactorInfo)
  2186.     if not ok or type(inf) ~= "table" then
  2187.         return
  2188.     end
  2189.     local now = computer.uptime()
  2190.     if r.chargeRequested and now <= r.chargeRequested then
  2191.         local downflow = 900000
  2192.         r.downgate.setOverrideEnabled(true)
  2193.         r.downgate.setFlowOverride(downflow)
  2194.         return
  2195.     end
  2196.     if inf.status == "charging" then
  2197.         local downflow = 900000
  2198.         r.downgate.setOverrideEnabled(true)
  2199.         r.downgate.setFlowOverride(downflow)
  2200.         return
  2201.     end
  2202.     local isActive = reactors[activeIndex] and reactors[activeIndex].reactorAdr == r.reactorAdr
  2203.     if cfg.autoProtectTempEdge and inf.temperature >= cfg.autoProtectTempEdge and not r.autostate then
  2204.         r.autostate = true
  2205.         if isActive then
  2206.             autostate = true
  2207.             if handleMain and button.autoon.x then
  2208.                 guiWrite(button.autoon.x, button.autoon.y, color.green, color.black, "%s\n", button.autoon.name)
  2209.             end
  2210.         end
  2211.     end
  2212.     local shieldPct = cfg.shield or 25
  2213.     local flow = getGateFlow(r.upgate)
  2214.     if type(flow) ~= "number" then
  2215.         flow = 0
  2216.     end
  2217.     local drain = inf.fieldDrainRate
  2218.     if state then
  2219.         local forceEdge = cfg.forceModeTempLowEdge or 7600
  2220.         if forceEdge < 7600 then
  2221.             forceEdge = 7600
  2222.         end
  2223.         local waittemp = autoWaitTempByAdr[r.reactorAdr]
  2224.         local tempRise = false
  2225.         if r.lastTemp then
  2226.             if inf.temperature > r.lastTemp then
  2227.                 tempRise = true
  2228.             elseif inf.temperature < r.lastTemp then
  2229.                 tempRise = false
  2230.             else
  2231.                 tempRise = r.temprise or false
  2232.             end
  2233.         end
  2234.         r.lastTemp = inf.temperature
  2235.         r.temprise = tempRise
  2236.         local downflow = drain / (1 - (shieldPct / 100))
  2237.         r.downgate.setOverrideEnabled(true)
  2238.         r.downgate.setFlowOverride(downflow)
  2239.         if inf.temperature <= forceEdge then
  2240.             if flow - inf.generationRate <= cfg.forceModeStepCase then
  2241.                 flow = flow + cfg.forceModeStep
  2242.                 r.upgate.setSignalLowFlow(flow)
  2243.             end
  2244.         elseif flow - inf.generationRate <= cfg.safeModeStepCase and inf.temperature >= forceEdge then
  2245.             if not waittemp then
  2246.                 flow = getGateFlow(r.upgate) + cfg.safemodeStep
  2247.                 r.upgate.setSignalLowFlow(flow)
  2248.             elseif inf.temperature < cfg.safeModeTempToWaitEdge then
  2249.                 waittemp = false
  2250.             end
  2251.         elseif tempRise and inf.temperature >= cfg.safeModeTempWaitEdge then
  2252.             if inf.temperature > cfg.safeModeTempWaitEdge then
  2253.                 waittemp = true
  2254.             end
  2255.             if inf.temperature > cfg.tempCriticalEdge then
  2256.                 flow = inf.generationRate - 1000
  2257.                 r.upgate.setSignalLowFlow(flow)
  2258.             end
  2259.         end
  2260.         autoWaitTempByAdr[r.reactorAdr] = waittemp
  2261.         if isActive and handleMain and infcord.flow.x then
  2262.             infcord.flow.value = flow
  2263.             guiWrite(infcord.flow.x, infcord.flow.y, color.red, color.grey, "%s", numformat(flow))
  2264.         end
  2265.     else
  2266.         local downflow
  2267.         if inf.status == "charging" then
  2268.             downflow = 900000
  2269.         else
  2270.             downflow = drain / (1 - (shieldPct / 100))
  2271.         end
  2272.         if inf.status == "charging" then
  2273.             r.downgate.setOverrideEnabled(true)
  2274.             r.downgate.setFlowOverride(downflow)
  2275.         else
  2276.             r.downgate.setOverrideEnabled(false)
  2277.             r.downgate.setSignalHighFlow(downflow)
  2278.             r.downgate.setSignalLowFlow(downflow)
  2279.         end
  2280.     end
  2281. end
  2282.  
  2283. local function tcall(type, scrnAdr, x, y, btn, player)
  2284.     local rx, ry = gpu.getResolution()
  2285.     if x >= rx - 2 and x <= rx and y == 1 then
  2286.         event.ignore("touch", tcall)
  2287.         prog = false
  2288.         ops = false
  2289.     end
  2290.     if handleStart then
  2291.         if menuItem.gates.x and x >= menuItem.gates.x and x <= (menuItem.gates.x + (menuItem.gates.w or #menuItem.gates.name) - 1) and y == menuItem.gates.y then
  2292.             resetAssignState()
  2293.             screenAssign()
  2294.             return
  2295.         end
  2296.     end
  2297.     if handleMain then
  2298.         if x >= button.add.x + 4 and x <= button.add.x + 3 + #button.add.name and y == button.add.y then
  2299.             if keyb.isShiftDown() then
  2300.                 if keyb.isControlDown() then
  2301.                     upgate.setSignalLowFlow(add(infcord.flow, cfg.ctrlShift_interval))
  2302.                 else
  2303.                     upgate.setSignalLowFlow(add(infcord.flow, cfg.shift_interval))
  2304.                 end
  2305.             else
  2306.                 if keyb.isControlDown() then
  2307.                     upgate.setSignalLowFlow(add(infcord.flow, cfg.ctrl_interval))
  2308.                 else
  2309.                     upgate.setSignalLowFlow(add(infcord.flow, cfg.default_interval))
  2310.                 end
  2311.             end
  2312.             guiWrite(infcord.flow.x, infcord.flow.y, color.red, color.grey, "%s", numformat(infcord.flow.value))
  2313.         end
  2314.         if x >= button.sub.x + 2 and x <= button.sub.x + 1 + #button.sub.name and y == button.sub.y then
  2315.             if keyb.isShiftDown() then
  2316.                 if keyb.isControlDown() then
  2317.                     upgate.setSignalLowFlow(sub(infcord.flow, cfg.ctrlShift_interval))
  2318.                 else
  2319.                     upgate.setSignalLowFlow(sub(infcord.flow, cfg.shift_interval))
  2320.                 end
  2321.             else
  2322.                 if keyb.isControlDown() then
  2323.                     upgate.setSignalLowFlow(sub(infcord.flow, cfg.ctrl_interval))
  2324.                 else
  2325.                     upgate.setSignalLowFlow(sub(infcord.flow, cfg.default_interval))
  2326.                 end
  2327.             end
  2328.             guiWrite(infcord.flow.x, infcord.flow.y, color.blue, color.grey, "%s", numformat(infcord.flow.value))
  2329.         end
  2330.  
  2331.         if not autostate then
  2332.             if x >= button.autooff.x and x <= button.autooff.x + textWidth(button.autooff.name) - 1 and y == button.autooff.y then
  2333.                 gpu.set(button.autooff.x, button.autooff.y, "        ")
  2334.                 autostate = true
  2335.                 guiWrite(button.autoon.x, button.autoon.y, color.green, color.black, "%s\n", button.autoon.name)
  2336.             end
  2337.         else
  2338.             if x >= button.autoon.x and x <= button.autoon.x + textWidth(button.autoon.name) - 1 and y == button.autoon.y then
  2339.                 gpu.set(button.autoon.x, button.autoon.y, "       ")
  2340.                 autostate = false
  2341.                 guiWrite(button.autooff.x, button.autooff.y, color.red, color.black, "%s\n", button.autooff.name)
  2342.             end
  2343.         end
  2344.         do
  2345.             local w = button.charge.w or textWidth(button.charge.name)
  2346.             if button.charge.x and button.charge.y and x >= button.charge.x and x <= button.charge.x + w - 1 and y == button.charge.y then
  2347.                 local usedNativeCharge = false
  2348.                 if reactor and reactor.chargeReactor then
  2349.                     local ok = pcall(reactor.chargeReactor)
  2350.                     if ok then
  2351.                         usedNativeCharge = true
  2352.                     end
  2353.                 end
  2354.                 if not usedNativeCharge then
  2355.                     ensureFlowInit(reactors[activeIndex], true)
  2356.                     if upgate then
  2357.                         upgate.setSignalLowFlow(CHARGE_FLOW)
  2358.                     end
  2359.                     infcord.flow.value = CHARGE_FLOW
  2360.                     if infcord.flow.x then
  2361.                         guiWrite(infcord.flow.x, infcord.flow.y, color.red, color.grey, "%s", numformat(infcord.flow.value))
  2362.                     end
  2363.                 end
  2364.                 if chargeHint.visible and chargeHint.x and chargeHint.y then
  2365.                     guiWrite(chargeHint.x, chargeHint.y, color.white, color.grey, "%s", string.rep(" ", chargeHint.w))
  2366.                     guiWrite(chargeHint.x, chargeHint.y + 1, color.white, color.grey, "%s", string.rep(" ", chargeHint.w))
  2367.                     chargeHint.visible = false
  2368.                     chargeHintDismissed = true
  2369.                 end
  2370.                 guiWrite(button.charge.x, button.charge.y, -1, color.grey, "                 ", button.charge.name)
  2371.                 button.charge.visible = false
  2372.                 if not usedNativeCharge then
  2373.                     local currentReactor = reactors[activeIndex]
  2374.                     if currentReactor and currentReactor.reactorAdr then
  2375.                         local chargeUntil = computer.uptime() + CHARGE_DURATION
  2376.                         currentReactor.chargeRequested = chargeUntil
  2377.                         chargeRequestByAdr[currentReactor.reactorAdr] = chargeUntil
  2378.                     end
  2379.                 end
  2380.             end
  2381.         end
  2382.         if button.start.visible then
  2383.             local w = button.start.w or textWidth(button.start.name)
  2384.             if x >= button.start.x and x <= button.start.x + w - 1 and y == button.start.y then
  2385.                 ensureFlowInit(reactors[activeIndex])
  2386.                 reactor.activateReactor()
  2387.                 if reactors[activeIndex] then
  2388.                     autoStopped[reactors[activeIndex].reactorAdr] = nil
  2389.                 end
  2390.                 guiWrite(button.start.x, button.start.y, -1, color.grey, "                 ", button.start.name)
  2391.                 button.start.visible = false
  2392.             end
  2393.         end
  2394.         if button.stop.visible then
  2395.             local w = button.stop.w or textWidth(button.stop.name)
  2396.             if x >= button.stop.x and x <= button.stop.x + w - 1 and y == button.stop.y then
  2397.                 reactor.stopReactor()
  2398.                 guiWrite(button.stop.x, button.stop.y, -1, color.grey, "                 ", button.stop.name)
  2399.                 button.stop.visible = false
  2400.             end
  2401.         end
  2402.         if x >= menuItem.options.x and x <= (menuItem.options.x + (menuItem.options.w or #menuItem.options.name) - 1) and y == menuItem.options.y then
  2403.             screenOptions()
  2404.             return
  2405.         end
  2406.         if x >= menuItem.overview.x and x <= (menuItem.overview.x + (menuItem.overview.w or #menuItem.overview.name) - 1) and y == menuItem.overview.y then
  2407.             syncActive()
  2408.             screenOverview()
  2409.             return
  2410.         end
  2411.         if x >= menuItem.refresh.x and x <= (menuItem.refresh.x + (menuItem.refresh.w or #menuItem.refresh.name) - 1) and y == menuItem.refresh.y then
  2412.             refreshReactors(true)
  2413.             return
  2414.         end
  2415.         if x >= menuItem.gates.x and x <= (menuItem.gates.x + (menuItem.gates.w or #menuItem.gates.name) - 1) and y == menuItem.gates.y then
  2416.             resetAssignState()
  2417.             screenAssign()
  2418.             return
  2419.         end
  2420.         for i = 1, #reactors do
  2421.             local tab = reactors[i].tab
  2422.             if tab.x and x >= tab.x and x <= tab.x + tab.w - 1 and y == tab.y then
  2423.                 syncActive()
  2424.                 setActive(i)
  2425.                 screenMain()
  2426.                 return
  2427.             end
  2428.         end
  2429.         if x == 1 and y == 1 then
  2430.             event.ignore("touch", tcall)
  2431.             prog = false
  2432.         end
  2433.     end
  2434.     if handleOverview then
  2435.         if x >= menuItem.options.x and x <= (menuItem.options.x + (menuItem.options.w or #menuItem.options.name) - 1) and y == menuItem.options.y then
  2436.             screenOptions()
  2437.             return
  2438.         end
  2439.         if x >= menuItem.refresh.x and x <= (menuItem.refresh.x + (menuItem.refresh.w or #menuItem.refresh.name) - 1) and y == menuItem.refresh.y then
  2440.             refreshReactors(true)
  2441.             return
  2442.         end
  2443.         if x >= menuItem.gates.x and x <= (menuItem.gates.x + (menuItem.gates.w or #menuItem.gates.name) - 1) and y == menuItem.gates.y then
  2444.             resetAssignState()
  2445.             screenAssign()
  2446.             return
  2447.         end
  2448.         for i = 1, #reactors do
  2449.             local tab = reactors[i].tab
  2450.             if tab.x and x >= tab.x and x <= tab.x + tab.w - 1 and y == tab.y then
  2451.                 syncActive()
  2452.                 setActive(i)
  2453.                 screenMain()
  2454.                 return
  2455.             end
  2456.             local ctrl = reactors[i].control
  2457.             if ctrl and ctrl.dec and x >= ctrl.dec.x and x <= ctrl.dec.x + ctrl.dec.w - 1 and y == ctrl.dec.y then
  2458.                 local flow = getGateFlow(reactors[i].upgate)
  2459.                 reactors[i].upgate.setSignalLowFlow(flow - cfg.default_interval)
  2460.                 return
  2461.             end
  2462.             if ctrl and ctrl.inc and x >= ctrl.inc.x and x <= ctrl.inc.x + ctrl.inc.w - 1 and y == ctrl.inc.y then
  2463.                 local flow = getGateFlow(reactors[i].upgate)
  2464.                 reactors[i].upgate.setSignalLowFlow(flow + cfg.default_interval)
  2465.                 return
  2466.             end
  2467.             if ctrl and ctrl.auto and x >= ctrl.auto.x and x <= ctrl.auto.x + ctrl.auto.w - 1 and y == ctrl.auto.y then
  2468.                 reactors[i].autostate = not reactors[i].autostate
  2469.                 if i == activeIndex then
  2470.                     autostate = reactors[i].autostate
  2471.                 end
  2472.                 refreshOverview()
  2473.                 return
  2474.             end
  2475.             local btn = reactors[i].powerBtn
  2476.             if btn.x and x >= btn.x and x <= btn.x + btn.w - 1 and y == btn.y then
  2477.                 if btn.action == "start" then
  2478.                     ensureFlowInit(reactors[i])
  2479.                     reactors[i].reactor.activateReactor()
  2480.                     autoStopped[reactors[i].reactorAdr] = nil
  2481.                 else
  2482.                     reactors[i].reactor.stopReactor()
  2483.                 end
  2484.                 return
  2485.             end
  2486.         end
  2487.         if x == 1 and y == 1 then
  2488.             event.ignore("touch", tcall)
  2489.             prog = false
  2490.         end
  2491.     end
  2492.     if handleOptions then
  2493.         if canedit then
  2494.             if x >= OptionDefault.x and x <= OptionDefault.x + #tostring(OptionDefault.value) - 1 and y == OptionDefault.y then
  2495.                 canclose = false
  2496.                 canedit = false
  2497.                 editOption(OptionDefault)
  2498.             end
  2499.             if x >= OptionCtrl.x and x <= OptionCtrl.x + #tostring(OptionCtrl.value) - 1 and y == OptionCtrl.y then
  2500.                 canclose = false
  2501.                 canedit = false
  2502.                 editOption(OptionCtrl)
  2503.             end
  2504.             if x >= OptionShift.x and x <= OptionShift.x + #tostring(OptionShift.value) - 1 and y == OptionShift.y then
  2505.                 canclose = false
  2506.                 canedit = false
  2507.                 editOption(OptionShift)
  2508.             end
  2509.             if x >= OptionCtrlShift.x and x <= OptionCtrlShift.x + #tostring(OptionCtrlShift.value) - 1 and y == OptionCtrlShift.y then
  2510.                 canclose = false
  2511.                 canedit = false
  2512.                 editOption(OptionCtrlShift)
  2513.             end
  2514.             if x >= OptionShield.x and x <= OptionShield.x + #tostring(OptionShield.value) - 1 and y == OptionShield.y then
  2515.                 canclose = false
  2516.                 canedit = false
  2517.                 editOption(OptionShield)
  2518.             end
  2519.             if x >= OptionFuelStop.x and x <= OptionFuelStop.x + #tostring(OptionFuelStop.value) - 1 and y == OptionFuelStop.y then
  2520.                 canclose = false
  2521.                 canedit = false
  2522.                 editOption(OptionFuelStop)
  2523.             end
  2524.         end
  2525.         if canclose then
  2526.             if x >= menuItem.optionsBack.x and x <= (menuItem.optionsBack.x + (menuItem.optionsBack.w or #menuItem.optionsBack.name) - 1) and y == menuItem.optionsBack.y then
  2527.                 screenOverview()
  2528.                 return
  2529.             end
  2530.             if x >= menuItem.save.x and x <= (menuItem.save.x + (menuItem.save.w or #menuItem.save.name) - 1) and y == menuItem.save.y then
  2531.                 cfg.default_interval = OptionDefault.value
  2532.                 cfg.ctrl_interval = OptionCtrl.value
  2533.                 cfg.shift_interval = OptionShift.value
  2534.                 cfg.ctrlShift_interval = OptionCtrlShift.value
  2535.                 cfg.shield = OptionShield.value
  2536.                 cfg.fuelStopPct = OptionFuelStop.value
  2537.                 configWrite(cfgName)
  2538.                 screenOverview()
  2539.                 return
  2540.             end
  2541.             if x >= menuItem.cancel.x and x <= (menuItem.cancel.x + (menuItem.cancel.w or #menuItem.cancel.name) - 1) and y == menuItem.cancel.y then
  2542.                 OptionDefault.value = cfg.default_interval
  2543.                 OptionCtrl.value = cfg.ctrl_interval
  2544.                 OptionShift.value = cfg.shift_interval
  2545.                 OptionCtrlShift.value = cfg.ctrlShift_interval
  2546.                 OptionShield.value = cfg.shield
  2547.                 OptionFuelStop.value = cfg.fuelStopPct or 98
  2548.                 screenOverview()
  2549.                 return
  2550.             end
  2551.         end
  2552.     end
  2553.     if handleAssign then
  2554.         if assignMode ~= "manual" and assignMode ~= "easy" then
  2555.             if easyAssign.backBtn.x and x >= easyAssign.backBtn.x and x <= easyAssign.backBtn.x + easyAssign.backBtn.w - 1 and y == easyAssign.backBtn.y then
  2556.                 assignMode = nil
  2557.                 easyAssign.active = false
  2558.                 screenOverview()
  2559.                 return
  2560.             end
  2561.             if easyAssign.heavyBtn.x and x >= easyAssign.heavyBtn.x and x <= easyAssign.heavyBtn.x + easyAssign.heavyBtn.w - 1 and y == easyAssign.heavyBtn.y then
  2562.                 assignMode = "manual"
  2563.                 resetAssignState()
  2564.                 screenAssignManual()
  2565.                 return
  2566.             end
  2567.             if easyAssign.easyBtn.x and x >= easyAssign.easyBtn.x and x <= easyAssign.easyBtn.x + easyAssign.easyBtn.w - 1 and y == easyAssign.easyBtn.y then
  2568.                 assignMode = "easy"
  2569.                 startEasyAssign()
  2570.                 if not easyAssign.active then
  2571.                     assignMode = nil
  2572.                     screenAssignChoice()
  2573.                 else
  2574.                     screenAssignEasy()
  2575.                 end
  2576.                 return
  2577.             end
  2578.             return
  2579.         elseif assignMode == "easy" then
  2580.             if easyAssign.backBtn.x and x >= easyAssign.backBtn.x and x <= easyAssign.backBtn.x + easyAssign.backBtn.w - 1 and y == easyAssign.backBtn.y then
  2581.                 if easyAssign.saved and easyAssign.core then
  2582.                     clearAssignedByAdr(easyAssign.core)
  2583.                 end
  2584.                 easyAssign.active = false
  2585.                 easyAssign.step = "reactor"
  2586.                 easyAssign.core = nil
  2587.                 easyAssign.shield = nil
  2588.                 easyAssign.output = nil
  2589.                 easyAssign.saved = false
  2590.                 easyAssign.message = nil
  2591.                 assignMode = nil
  2592.                 screenAssignChoice()
  2593.                 return
  2594.             end
  2595.             if easyAssign.step == "done" then
  2596.                 if easyAssign.continueBtn.x and x >= easyAssign.continueBtn.x and x <= easyAssign.continueBtn.x + easyAssign.continueBtn.w - 1 and y == easyAssign.continueBtn.y then
  2597.                     startEasyAssign()
  2598.                     screenAssignEasy()
  2599.                     return
  2600.                 end
  2601.                 if easyAssign.finishBtn.x and x >= easyAssign.finishBtn.x and x <= easyAssign.finishBtn.x + easyAssign.finishBtn.w - 1 and y == easyAssign.finishBtn.y then
  2602.                     easyAssign.active = false
  2603.                     assignMode = nil
  2604.                     refreshReactors(false)
  2605.                     screenOverview()
  2606.                     return
  2607.                 end
  2608.             end
  2609.             return
  2610.         end
  2611.         if x >= menuItem.back.x and x <= (menuItem.back.x + (menuItem.back.w or #menuItem.back.name) - 1) and y == menuItem.back.y then
  2612.             if not assignState.reactorIndex and not assignState.shieldGateIdx and not assignState.outGateIdx then
  2613.                 resetAssignState()
  2614.                 assignMode = nil
  2615.                 screenOverview()
  2616.                 return
  2617.             end
  2618.             assignState.message = "Выбери гейты!"
  2619.             screenAssign()
  2620.             return
  2621.         end
  2622.         if assignUi.reset and x >= assignUi.reset.x and x <= assignUi.reset.x + assignUi.reset.w - 1 and y == assignUi.reset.y then
  2623.             if assignState.reactorIndex then
  2624.                 clearAssignedGates(assignState.reactorIndex)
  2625.                 reactors = buildReactors() or reactors
  2626.                 resetAssignState()
  2627.                 screenAssign()
  2628.             end
  2629.             return
  2630.         end
  2631.         for i = 1, #assignUi.reactors do
  2632.             local btn = assignUi.reactors[i]
  2633.             if btn and x >= btn.x and x <= btn.x + btn.w - 1 and y == btn.y then
  2634.                 assignState.reactorIndex = i
  2635.                 assignState.reactorAdr = assignUi.reactorAddrs[i]
  2636.                 assignState.shieldGateIdx = nil
  2637.                 assignState.shieldGateAdr = nil
  2638.                 assignState.outGateIdx = nil
  2639.                 assignState.outGateAdr = nil
  2640.                 assignState.step = "shield"
  2641.                 screenAssign()
  2642.                 return
  2643.             end
  2644.         end
  2645.         for i = 1, #assignUi.gates do
  2646.             local btn = assignUi.gates[i]
  2647.             if btn and x >= btn.x and x <= btn.x + btn.w - 1 and y == btn.y then
  2648.                 local gateAdr = assignUi.gateAddrs[i]
  2649.                 if assignState.step == "reactor" then
  2650.                     return
  2651.                 end
  2652.                 if gateAdr and assignUi.gateUse[gateAdr] and assignUi.gateUse[gateAdr] ~= assignUi.selectedAdr then
  2653.                     return
  2654.                 end
  2655.                 local selectedEntry = assignUi.selectedEntry
  2656.                 if not assignState.reactorIndex then
  2657.                     assignState.step = "reactor"
  2658.                     screenAssign()
  2659.                     return
  2660.                 end
  2661.                 if assignState.step == "shield" then
  2662.                     assignState.shieldGateIdx = i
  2663.                     assignState.shieldGateAdr = gateAdr
  2664.                     assignState.step = "output"
  2665.                     screenAssign()
  2666.                     return
  2667.                 elseif assignState.step == "output" then
  2668.                     if assignState.shieldGateAdr and assignState.shieldGateAdr == gateAdr then
  2669.                         assignState.message = "Нужны два разных гейта!"
  2670.                         screenAssign()
  2671.                         return
  2672.                     end
  2673.                     assignState.outGateIdx = i
  2674.                     assignState.outGateAdr = gateAdr
  2675.                     if not assignState.reactorAdr and assignState.reactorIndex then
  2676.                         assignState.reactorAdr = assignUi.reactorAddrs[assignState.reactorIndex]
  2677.                     end
  2678.                     if not assignState.shieldGateAdr and assignState.shieldGateIdx then
  2679.                         assignState.shieldGateAdr = assignUi.gateAddrs[assignState.shieldGateIdx]
  2680.                     end
  2681.                     local saved = saveAssignedGates(assignState.reactorAdr, assignState.shieldGateAdr, assignState.outGateAdr)
  2682.                     if not saved then
  2683.                         assignState.message = "Не удалось сохранить гейты!"
  2684.                         screenAssign()
  2685.                         return
  2686.                     end
  2687.                     local targetAdr = assignState.reactorAdr
  2688.                     resetAssignState()
  2689.                     assignMode = nil
  2690.                     handleAssign = false
  2691.                     handleMain = true
  2692.                     handleOverview = false
  2693.                     reactors = buildReactors() or reactors
  2694.                     local idx = 1
  2695.                     if targetAdr then
  2696.                         for j = 1, #reactors do
  2697.                             if reactors[j].reactorAdr == targetAdr then
  2698.                                 idx = j
  2699.                                 break
  2700.                             end
  2701.                         end
  2702.                     end
  2703.                     setActive(idx)
  2704.                     if not reactors or #reactors == 0 or not reactor or not upgate or not downgate then
  2705.                         screenOverview()
  2706.                         return
  2707.                     end
  2708.                     screenMain()
  2709.                     return
  2710.                 end
  2711.             end
  2712.         end
  2713.         if x == 1 and y == 1 then
  2714.             event.ignore("touch", tcall)
  2715.             prog = false
  2716.         end
  2717.     end
  2718. end
  2719.  
  2720. event.listen("touch", tcall)
  2721. event.listen("component_added", handleComponentAdded)
  2722. screenStart()
  2723. if upgate ~= nil and downgate ~= nil then
  2724.     screen.overview()
  2725. end
  2726. while prog do
  2727.     local now = computer.uptime()
  2728.     if handleMain and now - lastDetailUpdate >= 0.2 then
  2729.         lastDetailUpdate = now
  2730.         if not pcall(refreshInfo) then
  2731.             autostate = false
  2732.             if not pcall(screenStart) then
  2733.                 break
  2734.             end
  2735.             if not pcall(screenMain) then
  2736.                 screenStart()
  2737.                 if not pcall(screenMain) then
  2738.                     break
  2739.                 end
  2740.             end
  2741.         end
  2742.     elseif handleOverview and now - lastOverviewUpdate >= 0.5 then
  2743.         lastOverviewUpdate = now
  2744.         if not pcall(refreshOverview) then
  2745.             if not pcall(screenOverview) then
  2746.                 break
  2747.             end
  2748.         end
  2749.     end
  2750.     if reactors and #reactors > 0 then
  2751.         for i = 1, #reactors do
  2752.             autoForReactor(reactors[i], reactors[i].autostate)
  2753.         end
  2754.     end
  2755.     pollEasyAssign()
  2756.     os.sleep(0)
  2757. end
  2758. gpu.setBackground(rback)
  2759. gpu.setForeground(rfore)
  2760. gpu.setResolution(xz, yz)
  2761. term.clear()
  2762.  
Tags: DC5P
Add Comment
Please, Sign In to add comment