Revector

dc5mini_str

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