From eb6c7ff8441c720c0e5e8f6e0da906aecedc7eaf Mon Sep 17 00:00:00 2001 From: Batrachophreno Date: Mon, 2 Mar 2026 04:41:29 -0500 Subject: [PATCH] Update TEG NanoUI to TGUI, rename TEG to Stirling engine (#21933) As title. TEGs are basically just thermopiles IRL; a Stirling engine more accurately reflects its functionality. image --------- Signed-off-by: Batrachophreno Co-authored-by: Cody Brittain <1779662+Generalcamo@users.noreply.github.com> --- .../components/binary_devices/circulator.dm | 2 + code/modules/power/generator.dm | 81 ++++---- html/changelogs/Bat-Teg.yml | 14 ++ nano/templates/jukebox.tmpl | 19 -- nano/templates/partyalarm.tmpl | 24 --- nano/templates/simple_airlock_console.tmpl | 36 ---- .../tgui/interfaces/StirlingEngine.tsx | 187 ++++++++++++++++++ 7 files changed, 243 insertions(+), 120 deletions(-) create mode 100644 html/changelogs/Bat-Teg.yml delete mode 100644 nano/templates/jukebox.tmpl delete mode 100644 nano/templates/partyalarm.tmpl delete mode 100644 nano/templates/simple_airlock_console.tmpl create mode 100644 tgui/packages/tgui/interfaces/StirlingEngine.tsx diff --git a/code/modules/atmospherics/components/binary_devices/circulator.dm b/code/modules/atmospherics/components/binary_devices/circulator.dm index 9fc6963527e..5856613843b 100644 --- a/code/modules/atmospherics/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/components/binary_devices/circulator.dm @@ -21,6 +21,8 @@ var/volume_capacity_used = 0 var/stored_energy = 0 var/temperature_overlay + /// When passing its status to the main Stirling generator, this informs labeling on the TGUI. + var/is_hot_loop = FALSE density = TRUE diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index b048726473e..0bcf1381cb9 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -1,6 +1,6 @@ /obj/machinery/power/generator - name = "thermoelectric generator" - desc = "It's a high efficiency thermoelectric generator." + name = "\improper Stirling engine" + desc = "It's a high efficiency Stirling engine. This model produces electricity from the temperature and differential between two gas loops." icon_state = "teg-unassembled" density = TRUE anchored = FALSE @@ -44,11 +44,13 @@ circ2 = null return ..() -//generators connect in dir and reverse_dir(dir) directions -//mnemonic to determine circulator/generator directions: the cirulators orbit clockwise around the generator -//so a circulator to the NORTH of the generator connects first to the EAST, then to the WEST -//and a circulator to the WEST of the generator connects first to the NORTH, then to the SOUTH -//note that the circulator's outlet dir is it's always facing dir, and it's inlet is always the reverse +/** + * Circulators connect in dir and reverse_dir(dir) directions + * A mnemonic to determine circulator/generator directions: the cirulators orbit clockwise around the generator. + * So a circulator to the NORTH of the generator connects first to the EAST, then to the WEST, + * and a circulator to the WEST of the generator connects first to the NORTH, then to the SOUTH. + * Note that the circulator's outlet dir is it's always facing dir, and it's inlet is always the reverse + */ /obj/machinery/power/generator/proc/reconnect() if(circ1) circ1.temperature_overlay = null @@ -78,11 +80,11 @@ /obj/machinery/power/generator/update_icon() icon_state = anchored ? "teg-assembled" : "teg-unassembled" ClearOverlays() - if (circ1) + if(circ1) circ1.temperature_overlay = null - if (circ2) + if(circ2) circ2.temperature_overlay = null - if (stat & (NOPOWER|BROKEN)) + if(stat & (NOPOWER|BROKEN)) return TRUE else if (lastgenlev != 0) @@ -91,10 +93,14 @@ var/extreme = (lastgenlev > 9) ? "ex" : "" if (circ1.last_temperature < circ2.last_temperature) circ1.temperature_overlay = "circ-[extreme]cold" + circ1.is_hot_loop = FALSE circ2.temperature_overlay = "circ-[extreme]hot" + circ2.is_hot_loop = TRUE else circ1.temperature_overlay = "circ-[extreme]hot" + circ1.is_hot_loop = TRUE circ2.temperature_overlay = "circ-[extreme]cold" + circ2.is_hot_loop = FALSE return TRUE /obj/machinery/power/generator/process() @@ -102,8 +108,6 @@ stored_energy = 0 return - updateDialog() - var/datum/gas_mixture/air1 = circ1.return_transfer_air() var/datum/gas_mixture/air2 = circ2.return_transfer_air() @@ -174,6 +178,7 @@ if(attacking_item.tool_behaviour == TOOL_WRENCH) attacking_item.play_tool_sound(get_turf(src), 75) anchored = !anchored + balloon_alert(user, "[anchored ? "secure" : "unsecure"]") user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ "You hear a ratchet") @@ -193,20 +198,27 @@ reconnect() ui_interact(user) -/obj/machinery/power/generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // this is the data which will be sent to the ui - var/vertical = 0 - if (dir == NORTH || dir == SOUTH) - vertical = 1 +/obj/machinery/power/generator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + // Interface ID should match your tgui route registration/name. + ui = new(user, src, "StirlingEngine", "Stirling Engine") + ui.open() - var/data[0] - data["totalOutput"] = effective_gen/1000 - data["maxTotalOutput"] = max_power/1000 - data["thermalOutput"] = last_thermal_gen/1000 - data["circConnected"] = 0 +/obj/machinery/power/generator/ui_data(mob/user) + var/list/data = list() + + var/vertical = FALSE + if(dir == NORTH || dir == SOUTH) + vertical = TRUE + + data["totalOutput"] = effective_gen / 1000 + data["maxTotalOutput"] = max_power / 1000 + data["thermalOutput"] = last_thermal_gen / 1000 + data["circConnected"] = FALSE if(circ1) - //The one on the left (or top) + // The one on the left (or top) data["primaryDir"] = vertical ? "top" : "left" data["primaryOutput"] = last_circ1_gen/1000 data["primaryFlowCapacity"] = circ1.volume_capacity_used*100 @@ -214,9 +226,10 @@ data["primaryInletTemperature"] = circ1.air1.temperature data["primaryOutletPressure"] = XGM_PRESSURE(circ1.air2) data["primaryOutletTemperature"] = circ1.air2.temperature + data["primaryIsHot"] = circ1.is_hot_loop if(circ2) - //Now for the one on the right (or bottom) + // The one on the right (or bottom) data["secondaryDir"] = vertical ? "bottom" : "right" data["secondaryOutput"] = last_circ2_gen/1000 data["secondaryFlowCapacity"] = circ2.volume_capacity_used*100 @@ -224,25 +237,11 @@ data["secondaryInletTemperature"] = circ2.air1.temperature data["secondaryOutletPressure"] = XGM_PRESSURE(circ2.air2) data["secondaryOutletTemperature"] = circ2.air2.temperature + data["secondaryIsHot"] = circ2.is_hot_loop - if(circ1 && circ2) - data["circConnected"] = 1 - else - data["circConnected"] = 0 + data["circConnected"] = (circ1 && circ2) ? TRUE : FALSE - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "generator.tmpl", "Thermoelectric Generator", 450, 500) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) + return data /obj/machinery/power/generator/power_change() ..() diff --git a/html/changelogs/Bat-Teg.yml b/html/changelogs/Bat-Teg.yml new file mode 100644 index 00000000000..27b9e0f889f --- /dev/null +++ b/html/changelogs/Bat-Teg.yml @@ -0,0 +1,14 @@ +# Your name. +author: Batrachophrenoboocosmomachia + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - spellcheck: "Renames TEGs to Stirling engines to more accurately reflect their functionality." + - refactor: "Refactor of interface for TEG/Stirling Engines to use TGUI instead of NanoUI." diff --git a/nano/templates/jukebox.tmpl b/nano/templates/jukebox.tmpl deleted file mode 100644 index f7fac89ff9f..00000000000 --- a/nano/templates/jukebox.tmpl +++ /dev/null @@ -1,19 +0,0 @@ - - -

Current track: {{:data.current_track}}

-
- {{:helper.link('Play' , 'play', {'play' : 1}, data.playing == 1 ? 'disabled' : null, null)}} - {{:helper.link('Stop' , 'stop', {'stop' : 1}, data.playing == 0 ? 'disabled' : null, null)}} -
- -

Available tracks:

-
- {{for data.tracks}} -
- {{:helper.link( value.track, 'gear', {'change_track' : 1, 'title' : value.track}, value.track == data.current_track ? 'disabled' : null, null)}} -
- {{/for}} -
\ No newline at end of file diff --git a/nano/templates/partyalarm.tmpl b/nano/templates/partyalarm.tmpl deleted file mode 100644 index e3adb80ffdd..00000000000 --- a/nano/templates/partyalarm.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -The current alert level is: -{{if data.alertLevel == 'green'}} - Green -{{/if}} -{{if data.alertLevel == 'blue'}} - Blue -{{/if}} -{{if data.alertLevel == 'red'}} - Red -{{/if}} -{{if data.alertLevel == 'delta'}} - DELTA -{{/if}} -
-

State

-{{:helper.link('No Party :(', '', {'state': 'inactive'}, data.active ? null : 'selected')}} -{{:helper.link('PARTY!!!', '', {'state': 'active'}, data.active ? 'selected' : null)}} -

Timed Lockdown

-{{:helper.link('Set', '', {'tmr' : 'set'})}} -{{if data.timing}} -{{:helper.link('Stop', '', {'tmr' : 'stop'})}} -{{/if}} -{{if !(data.timing)}}{{:helper.link('Start', '', {'tmr' : 'start'})}}{{/if}}
-{{if data.time}}Time Left: {{:data.time}} seconds.{{/if}} \ No newline at end of file diff --git a/nano/templates/simple_airlock_console.tmpl b/nano/templates/simple_airlock_console.tmpl deleted file mode 100644 index ca49b20f6ae..00000000000 --- a/nano/templates/simple_airlock_console.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -
-
-
- Chamber Pressure: -
-
- {{:helper.displayBar(data.chamber_pressure, 0, 200, (data.chamber_pressure < 80) || (data.chamber_pressure > 120) ? 'bad' : (data.chamber_pressure < 95) || (data.chamber_pressure > 110) ? 'average' : 'good')}} -
- {{:data.chamber_pressure}} kPa -
-
-
-
-
-
-
- {{:helper.link('Cycle to Exterior', 'arrowthickstop-1-w', {'command' : 'cycle_ext'}, data.processing ? 'disabled' : null)}} - {{:helper.link('Cycle to Interior', 'arrowthickstop-1-e', {'command' : 'cycle_int'}, data.processing ? 'disabled' : null)}} -
-
- {{if data.interior_status.state == "open"}} - {{:helper.link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, 'redButton')}} - {{else}} - {{:helper.link('Force exterior door', 'alert', {'command' : 'force_ext'}, null, data.processing ? 'yellowButton' : null)}} - {{/if}} - {{if data.exterior_status.state == "open"}} - {{:helper.link('Force interior door', 'alert', {'command' : 'force_int'}, null, 'redButton')}} - {{else}} - {{:helper.link('Force interior door', 'alert', {'command' : 'force_int'}, null, data.processing ? 'yellowButton' : null)}} - {{/if}} -
-
-
- {{:helper.link('Abort', 'cancel', {'command' : 'abort'}, data.processing ? null : 'disabled', data.processing ? 'redButton' : null)}} -
-
\ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/StirlingEngine.tsx b/tgui/packages/tgui/interfaces/StirlingEngine.tsx new file mode 100644 index 00000000000..3ce124065a3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/StirlingEngine.tsx @@ -0,0 +1,187 @@ +import { + Box, + LabeledList, + NoticeBox, + ProgressBar, + Section, + Stack, +} from '../components'; +import { useBackend } from '../backend'; +import { round } from 'common/math'; +import { Window } from '../layouts'; +import { BooleanLike } from '../../common/react'; + +type Data = { + totalOutput: number; + maxTotalOutput: number; + thermalOutput: number; + circConnected: BooleanLike; + isHot: BooleanLike; + + primaryDir?: string; + primaryOutput?: number; + primaryFlowCapacity?: number; + primaryInletPressure?: number; + primaryInletTemperature?: number; + primaryOutletPressure?: number; + primaryOutletTemperature?: number; + primaryIsHot?: BooleanLike; + + secondaryDir?: string; + secondaryOutput?: number; + secondaryFlowCapacity?: number; + secondaryInletPressure?: number; + secondaryInletTemperature?: number; + secondaryOutletPressure?: number; + secondaryOutletTemperature?: number; + secondaryIsHot?: BooleanLike; +}; + +// Round numbers to one decimal place +const f1 = (n?: number) => round(n ?? 0, 0.1); + +export const StirlingEngine = (props, context) => { + const { data } = useBackend(context); + const { + totalOutput, + maxTotalOutput, + thermalOutput, + circConnected, + + primaryDir, + primaryOutput, + primaryFlowCapacity, + primaryInletPressure, + primaryInletTemperature, + primaryOutletPressure, + primaryOutletTemperature, + primaryIsHot, + + secondaryDir, + secondaryOutput, + secondaryFlowCapacity, + secondaryInletPressure, + secondaryInletTemperature, + secondaryOutletPressure, + secondaryOutletTemperature, + secondaryIsHot, + } = data; + + const bothConnected = !!circConnected; + const maxValue = Math.max(maxTotalOutput || 0, 0.001); + + return ( + + +
+ + + + {f1(totalOutput)} kW + + + {f1(thermalOutput)} kWth + + +
+ + {bothConnected ? ( + + + + + + + + + ) : ( + + ERROR: Both circulators must be connected! + + )} +
+
+ ); +}; + +const CirculatorBlock = (props: { + title: string; + dir?: string; + output?: number; + flowCapacity?: number; + inletPressure?: number; + inletTemperature?: number; + outletPressure?: number; + outletTemperature?: number; + isHot?: BooleanLike; +}) => { + const { + title, + dir, + output, + flowCapacity, + inletPressure, + inletTemperature, + outletPressure, + outletTemperature, + isHot, + } = props; + + return ( +
+ + + + {f1(output)} kWth + + + {f1(flowCapacity)} % + + + {f1(inletPressure)} kPa + + + {f1(inletTemperature)} K + + + {f1(outletPressure)} kPa + + + {f1(outletTemperature)} K + + + +
+ ); +};