· 4 years ago · Aug 20, 2021, 02:34 PM
1-- coding=utf-8
2--[[
3reactor++.lua By Creepercdn
4Since 2021/8/18 UTC+8:00
5A automatic reactor script for IC2 and OC.
6ONLY TESTED ON 1.12.2
7ONLY FOR 1.12.2
8
9A version from reactor2.lua(By odixus, https://www.mcmod.cn/post/693.html)
10
11Some themes can found on https://xcolors.herokuapp.com/
12--]]
13local component = require("component")
14local event = require("event")
15local filesystem = require("filesystem")
16local serialization = require("serialization")
17local term = require("term")
18local coroutine = require("coroutine")
19-- Don't use tty API. It is obsolete. You should use gpu Component API
20
21local function getCom(t) -- Get component
22 if component.list(t) then
23 return component.proxy(component.list(t)())
24 else
25 return nil
26 end
27end
28
29local function putTable(t, f) -- save table to file
30 if type(t) == "table" then
31 local file = filesystem.open(f, "w")
32 file:write(serialization.serialize(t))
33 file:close()
34 return true
35 else
36 return false
37 end
38end
39
40local function getTable(f) -- get table from file
41 if filesystem.exists(f) then
42 local file = io.open(f, "r")
43 local fileSize = filesystem.size(f)
44 local fileRead = file:read(fileSize)
45 local fileContent = fileRead
46 file:close()
47 return serialization.unserialize(fileContent)
48 else
49 return false
50 end
51end
52
53local gpu = getCom("gpu")
54
55gpu.setDepth(gpu.maxDepth())
56
57-- DO NOT USE CLEAR! USE FG AND BG BG IS BACKGROUND.
58local RED, YELLOW, GREEN, BLUE, PURPLE, CYAN, WHITE, BG, FG, BLACK, CLEAR
59
60--[[ local RED, YELLOW, GREEN, BLUE, CLEAR = "\27[31m", "\27[33m", "\27[32m",
61 "\27[36m", "\27[0m" -- ANSI escape code for colorful console (4 bit) ]]
62
63-- 0: 3bit color; 1: one dark; 2: monokai; 3: neon, 4: colorful colors
64local function initColor(type)
65 local palette
66 -- Don't worry! Add a theme is a easy work.
67 -- Use terminal.sexy! It is awesome!
68 -- https://terminal.sexy/
69 -- BG FG BLACK RED GREEN YELLOW BLUE PURPLE CYAN WHITE
70 if type == 1 then
71 palette = {
72 0x21252B, 0xABB2BF, 0x21252B, 0xE06C75, 0x98C379, 0xE5C07B,
73 0x61AFEF, 0xC678DD, 0x56B6C2, 0xABB2BF
74 }
75 elseif type == 2 then
76 palette = {
77 0x272822, 0xf8f8f2, 0x272822, 0xf92672, 0xa6e22e, 0xf4bf75,
78 0x66d9ef, 0xae81ff, 0xa1efe4, 0xf8f8f2
79 }
80 elseif type == 3 then
81 palette = {
82 0x171717, 0xf8f8f8, 0x171717, 0xd81765, 0x97d01a, 0xffa800,
83 0x16b1fb, 0xff2491, 0x0fdcb6, 0xebebeb
84 }
85 elseif type == 4 then
86 palette = {
87 0x000000, 0xffffff, 0x151515, 0xed4c7a, 0xa6e179, 0xffdf6b,
88 0x79d2ff, 0xe85b92, 0x87a8af, 0xe2f1f6
89 }
90 else
91 palette = {
92 0x000000, 0xFFFFFF, 0x000000, 0xFF0000, 0x00FF00, 0xFFFF00,
93 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF
94 }
95 end
96 gpu.setBackground(palette[1])
97 gpu.setForeground(palette[2])
98 RED = function()
99 gpu.setForeground(palette[4])
100 return ""
101 end
102 YELLOW = function()
103 gpu.setForeground(palette[6])
104 return ""
105 end
106 GREEN = function()
107 gpu.setForeground(palette[5])
108 return ""
109 end
110 BLUE = function()
111 gpu.setForeground(palette[7])
112 return ""
113 end
114 PURPLE = function()
115 gpu.setForeground(palette[8])
116 return ""
117 end
118 CYAN = function()
119 gpu.setForeground(palette[9])
120 return ""
121 end
122 WHITE = function()
123 gpu.setForeground(palette[10])
124 return ""
125 end
126 BG = function()
127 gpu.setBackground(palette[1])
128 return ""
129 end
130 FG = function()
131 gpu.setForeground(palette[2])
132 return ""
133 end
134 BLACK = function()
135 gpu.setForeground(palette[3])
136 return ""
137 end
138end
139
140-- f**king lua
141initColor(getTable("/home/reactor.cfg")["theme"] or 0)
142-- Q: What the f**k is "or 0"?
143-- A: Just try it.
144-- > =0 or 0
145-- 0
146-- > = nil or 0
147-- 0
148-- > = 255 or 0
149-- 255
150-- > = {} or 0
151-- table: 0xFFFFFFFF
152-- > = "i love python" or 0
153-- i love python
154-- >
155-- A: So, "or 0" can convert nil to 0.
156-- Q: But, why there are no Ternary Operator?
157-- A: I think you should ask Roberto Ierusalimschy, Waldemar Celes and Luiz Henrique de Figueiredo. They are authors of Lua.
158
159local function colorPrint(color, text) -- colorful print()
160 color()
161 print(text)
162 FG()
163end
164
165local function colorWrite(color, text) -- colorful term.write
166 color()
167 term.write(text)
168 FG()
169end
170
171--[[
172A list for auto detect (and replace) reactor items.
173format:
174{
175[XX]={{damage=YY,item=ZZ,metafrom=AA,metato=BB}}
176}
177
178XX: string, a item id. The script will check this item.
179AA: XX's meta value. Default to any.
180YY: float, means the item's damage value ratio. If the item's damage value lower than this value, when item is nil, the reactor will stop, otherwise it will replace it with ZZ.
181ZZ: string, a item id. The item will replaced with.
182BB: ZZ's meta value. Default to any.
183
184if there are same XX, add multi capture group.
185--]]
186
187local items = {
188 ["ic2:nuclear"] = {
189 {item = "ic2:reactorUraniumQuad", metafrom = 13},
190 {item = "ic2:reactorMOXQuad", metafrom = 16},
191 {item = "ic2:reactorUraniumDual", metafrom = 12},
192 {item = "ic2:reactorMOXDual", metafrom = 15},
193 {item = "ic2:reactorUraniumSimple", metafrom = 11},
194 {item = "ic2:reactorMOXSimple", metafrom = 14}
195 },
196 ["ic2:reactorReflector"] = {{damage = 0.01, item = "ic2:reactorReflector"}},
197 ["ic2:reactorReflectorThick"] = {{
198 damage = 0.01,
199 item = "ic2:reactorReflectorThick"
200 }},
201 ["ic2:lzh_condensator"] = {{damage = 0.01, item = "ic2:lzh_condensator"}},
202 ["ic2:rsh_condensator"] = {{damage = 0.01, item = "ic2:rsh_condensator"}},
203 ["ic2:hex_heat_storage"] = {{damage = 0.05, item = "ic2:hex_heat_storage"}},
204 ["ic2:tri_heat_storage"] = {{damage = 0.1, item = "ic2:tri_heat_storage"}},
205 ["ic2:heat_storage"] = {{damage = 0.2, item = "ic2:heat_storage"}},
206 ["ic2:heat_vent"] = {{damage = 0.5}},
207 ["ic2:reactor_heat_vent"] = {{damage = 0.5}},
208 ["ic2:overclocked_heat_vent"] = {{damage = 0.5}},
209 ["ic2:advanced_heat_vemt"] = {{damage = 0.5}},
210 ["ic2:heat_exchanger"] = {{damage = 0.5}},
211 ["ic2:reactor_heat_exchanger"] = {{damage = 0.5}},
212 ["ic2:component_heat_exchanger"] = {{damage = 0.5}},
213 ["ic2:advanced_heat_exchanger"] = {{damage = 0.5}},
214 ["fm:depleted_coaxium_rod"] = {{item = "fm:coaxium_rod"}},
215 ["fm:depleted_coaxium_rod_dual"] = {{item = "fm:coaxium_rod_dual"}},
216 ["fm:depleted_coaxium_rod_quad"] = {{item = "fm:coaxium_rod_quad"}},
217 ["fm:depleted_cesium_rod"] = {{item = "fm:cesium_rod"}},
218 ["fm:depleted_cesium_rod_dual"] = {{item = "fm:cesium_rod_dual"}},
219 ["fm:depleted_cesium_rod_quad"] = {{item = "fm:cesium_rod_quad"}}
220}
221
222-- Q: When will GregTech update to 1.16?
223-- A: When Worlds Collide.
224-- When worlds collide, you can run, but no can hide!
225-- In memory of Stephen Hillenburg.
226
227local rs = getCom("redstone")
228local reactor = getCom("reactor_chamber")
229local transfer = getCom("transposer")
230
231local function paddingMid(s, t) -- print text with padding middle
232 local tt = t
233 if not tt then tt = " " end
234 local w, h = gpu.getResolution()
235 local nLeft = math.floor((w - string.len(s)) / 2)
236 return string.rep(tt, nLeft) .. s ..
237 string.rep(tt, w - nLeft - string.len(s))
238end
239
240local function paddingLeft(s, t)
241 local tt = t
242 if not tt then tt = " " end
243 local w, h = gpu.getResolution()
244 return s .. string.rep(tt, w - string.len(s))
245end
246
247local function getKey() -- get value 0~5
248 local result = -1
249 while (not result) or (result < 0) or (result > 5) do
250 result = tonumber(io.read())
251 end
252 return result
253end
254
255local function getConfig()
256 -- Config sides. If there are config file and using the config, read the config and prompt, otherwise reconfigure.
257 local cfg = getTable("/home/reactor.cfg")
258-- gpu.setResolution(50,16)
259 term.clear()
260 if cfg then
261 colorPrint(BLUE, "Current configuration:")
262 colorPrint(BLUE, "------------------------------------------")
263 for key, s in pairs(cfg) do print(key .. ": " .. s) end
264 colorPrint(BLUE, "------------------------------------------")
265 colorPrint(BLUE, "0:down 1:top 2:north 3:south 4:west 5:east")
266 colorPrint(BLUE, "------------------------------------------")
267 colorPrint(YELLOW, "Do you want to use this config? [Y/n]")
268 if io.read() == "n" then cfg = nil end
269 end
270 if not cfg then
271 term.clear()
272 cfg = {}
273 colorPrint(BLUE, "Please config the sides:")
274 colorPrint(BLUE, "--------------------------------------------")
275 colorPrint(BLUE, "0:bottom 1:top 2:north 3:south 4:west 5:east")
276 colorPrint(BLUE, "--------------------------------------------")
277 -- redstone output side relative to the redstone adapter
278 colorWrite(GREEN, "Which side does [REDSTONE] Output? ")
279 cfg["redstone"] = getKey()
280 -- reactor side relative to the transposer
281 colorWrite(GREEN, "Which side does REACTOR towards [TRANSPOSER]? ")
282 cfg["reactor"] = getKey()
283 -- fuel box relative to the transposer
284 colorWrite(GREEN, "Which side does FuelBox towards [TRANSPOSER]? ")
285 cfg["fuelbox"] = getKey()
286 -- waste box relative to the transposer
287 colorWrite(GREEN, "Which side does WasteBox towards [TRANSPOSER]? ")
288 cfg["wastebox"] = getKey()
289 -- overheat ratio
290 colorWrite(GREEN, "What is overheat temperature ratio? (0.0001~1) ")
291 cfg["overheat"] = getKey()
292 -- theme
293 colorWrite(GREEN,
294 "What theme do you want to use? 0: 3bit color; 1: one dark; 2: monokai; 3: neno; 4: Coloful Colors")
295 cfg["theme"] = getKey()
296
297 putTable(cfg, "/home/reactor.cfg")
298 end
299 initColor(cfg["theme"])
300 colorPrint(GREEN, "Config successful!")
301
302 ---@diagnostic disable-next-line: undefined-field
303 os.sleep(1);
304-- gpu.setResolution(32,6)
305 term.clear()
306
307 return cfg
308end
309
310local function keyDown(t) -- get key. it t defined, the function will wait the key elapsed t most.
311 local result
312 if t then
313 _, _, result = event.pull(t, "key_down")
314 else
315 _, _, result = event.pull("key_down")
316 end
317 if not result then result = 0 end
318 return result
319end
320
321local function checkReactor(running)
322 local cfg = getTable("/home/reactor.cfg")
323 local item_in_reactor = transfer.getAllStacks(cfg["reactor"]).getAll()
324 while true do
325 local ready = true
326 local shortage = false;
327 for i = 0, #item_in_reactor - 4 do -- "-4" is for liquid reactor
328 if item_in_reactor[i] and items[item_in_reactor[i].name] then -- check if this slot has item and this item is recorded in items table (only id)
329 for _, captureGroup in ipairs(items[item_in_reactor[i].name]) do -- emurate the capture groups
330 local lowDamage=true -- if doesn't specify meta and no damage value, just replace it (e.g. depleted rods)
331 -- if maxDamage isn't zero, there are no metadata.
332 -- if ((it have damage) or (doesn't specify meta value) or ((there are meta value) and (meta value equals)))
333 if (item_in_reactor[i].maxDamage ~= 0) or (not captureGroup.metafrom) or (captureGroup.metafrom and (captureGroup.metafrom == item_in_reactor[i].damage)) then
334 if (not item_in_reactor[i].maxDamage == 0) then -- if it have damage
335 lowDamage = (
336 (not captureGroup.damage) -- if doesn't specify damage, just replace it! (the item is need to replace)
337 or (
338 (item_in_reactor[i].maxDamage - item_in_reactor[i].damage)
339 <= (captureGroup.damage * item_in_reactor[i].maxDamage)
340 )
341 )
342 end
343 if lowDamage then
344 ready = false
345 if running then
346 -- stop reactor before replace item
347 rs.setOutput(cfg["redstone"], 0)
348 running = false
349 end
350 if captureGroup.item then -- can replace
351 transfer.transferItem(cfg["reactor"], cfg["wastebox"], 1, i + 1) -- don't understand, daren't modify
352 -- find item can be replaced with
353 local boxLocation = 0
354 local item_in_box = transfer.getAllStacks(cfg["fuelbox"]).getAll()
355
356 -- however, for is best.
357 --[[ while (boxLocation == 0) and (k <= #item_in_box) do -- if you don't understand why use while, please see reference of boxLocation.
358 if item_in_box[k] and (item_in_box[k].name == captureGroup.item) then
359 boxLocation = k + 1
360 break
361 end
362 k = k + 1
363 end ]]
364
365 for idx=1, #item_in_box, 1 do
366 if((item_in_box[idx] == captureGroup.item) and ((not captureGroup.metato) or (not item_in_box[idx].maxDamage == 0) or (captureGroup.metato == item_in_box[idx].damage))) then
367 boxLocation = idx+1
368 break
369 end
370 --coroutine.yield()
371 end
372
373 -- replace if can replace, otherwise wait
374 if not ((boxLocation > 0) and transfer.transferItem(cfg["fuelbox"], cfg["reactor"], 1, boxLocation, i + 1)) then
375 shortage = true
376computer.beep(441,0.5)
377 end
378 end
379 end
380 end
381 end
382 -- check if low damage (no damage or damage lower than the damage in items table).
383
384 -- don't start if there are low damage item
385 end
386 end
387--term.setCursor(1,4)
388--term.write(tostring(running).." "..tostring(ready).." "..tostring(shortage))
389
390 running = coroutine.yield(running, ready, shortage)
391 end
392end
393
394---------------script starts---------------------
395local w1, h1 = gpu.getResolution() -- origin size
396local reactorThread = coroutine.create(checkReactor)
397if rs and reactor and transfer then -- if components defined
398 local cfg = getConfig()
399 -- work start
400 local w, h = gpu.getResolution()
401 local command = "s"
402 local overheated = false
403 local shortage = false;
404 while true do
405 local heat = reactor.getHeat()
406 local heatMax = reactor.getMaxHeat()
407 local running = reactor.producesEnergy()
408
409 -- f**king lua, too.
410 term.setCursor(1, 1)
411 colorWrite(BLUE, paddingLeft("DATE/TIME: " .. os.date()))
412 term.setCursor(1, 2)
413 colorWrite((overheated and RED or GREEN),
414 paddingLeft("Heat: " .. heat .. " / " .. heatMax))
415 term.setCursor(1, 3)
416 colorWrite((overheated and RED or GREEN), paddingLeft(
417 string.rep("#", math.floor(w * heat / heatMax)), "."))
418
419 term.setCursor(1, 4)
420 if shortage then
421 colorWrite(RED, paddingMid("Fuel shortage!"))
422 else
423 --term.write(paddingLeft(" "))
424 end
425
426 term.setCursor(1, 5)
427 if running then
428 colorWrite(GREEN, paddingMid("<<< RUNNING >>>"))
429 else
430 colorWrite(RED, paddingMid("<<< STOP >>>"))
431 end
432 term.setCursor(1, 6)
433 colorWrite(YELLOW, "[Run] [Stop] [eXit] [Config] " .. command)
434
435 if heat / heatMax > cfg["overheat"] then -- stop if overheat
436 if running then rs.setOutput(cfg["redstone"], 0) end
437 running = false
438 overheated = true
439 else
440 overheated = false
441 -- check the items need to replace or stop
442 -- it is f**king to rebuild shit code
443
444 local ready;
445 local success, trnig, trdy, tstag = coroutine.resume(reactorThread, running)
446 if trnig then
447 running = trnig
448 end
449 if trdy then
450 ready = trdy
451 end
452 if tstag then
453 shortage = tstag
454 end
455
456 -- start if command=r and ready
457 if (command == "r") and (not running) and ready and (not overheated) then
458 rs.setOutput(cfg["redstone"], 15)
459 end
460 end
461 local key = keyDown(0.2) -- capture key
462 if key == 115 then -- press s key means stop
463 command = "s";
464 rs.setOutput(cfg["redstone"], 0);
465 elseif key == 114 then -- press r means run
466 command = "r"
467 elseif key == 120 then -- press x means exit
468 rs.setOutput(cfg["redstone"], 0);
469 break
470 elseif key == 99 then -- press c, stop reactor before config, means config
471 rs.setOutput(cfg["redstone"], 0)
472 cfg = getConfig()
473 end
474 end
475 gpu.setResolution(w1, h1) -- restore to origin size
476else
477 colorPrint(RED, "Please check components: REDSTONE, TRANSPOSER, REACTOR")
478end