From 34ebbcd67a2c4f8551c7af6fa3c9395b5bd7a360 Mon Sep 17 00:00:00 2001 From: Batrachophreno Date: Mon, 27 Apr 2026 14:49:22 -0400 Subject: [PATCH] NanoUI Conversions - DNA Analyzer & Engines Control (#22326) changes: - refactor: "Migrates DNA Analyzer NanoUI to TGUI." - refactor: "Migrates Engines Control NanoUI to TGUI." - qol: "DNA Analyzer reports now provide the date and time the scan was produced." - bugfix: "Ship computers no longer function after their power is cut." Before/After images: dna_analyzer engines_control --- .../detectivework/microscope/dnascanner.dm | 143 +++++++----- .../overmap/ships/computers/engine_control.dm | 163 +++++++------ code/modules/overmap/ships/computers/helm.dm | 4 +- code/modules/overmap/ships/computers/ship.dm | 4 +- code/modules/overmap/ships/engines/engine.dm | 8 +- .../overmap/ships/engines/gas_thruster.dm | 41 ++-- .../overmap/ships/engines/ion_thruster.dm | 13 +- .../ships/engines/maneuvering_thrusters.dm | 7 +- html/changelogs/Bat-NanoConversions.yml | 7 + nano/templates/dnaforensics.tmpl | 45 ---- .../packages/tgui/interfaces/DnaForensics.tsx | 90 ++++++++ .../tgui/interfaces/EnginesControl.tsx | 217 ++++++++++++++++++ 12 files changed, 543 insertions(+), 199 deletions(-) create mode 100644 html/changelogs/Bat-NanoConversions.yml delete mode 100644 nano/templates/dnaforensics.tmpl create mode 100644 tgui/packages/tgui/interfaces/DnaForensics.tsx create mode 100644 tgui/packages/tgui/interfaces/EnginesControl.tsx diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 70c1e4fc7e2..d2eaa4251ae 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -4,85 +4,111 @@ desc = "A high tech machine that is designed to read DNA samples properly." icon = 'icons/obj/forensics.dmi' icon_state = "dnaopen" - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE var/obj/item/forensics/swab/bloodsamp = null - var/closed = 0 - var/scanning = 0 + var/closed = FALSE + var/scanning = FALSE var/scanner_progress = 0 var/scanner_rate = 2.50 var/last_process_worldtime = 0 var/report_num = 0 /obj/machinery/dnaforensics/attackby(obj/item/attacking_item, mob/user) - - if(bloodsamp) - to_chat(user, SPAN_WARNING("There is already a sample in the machine.")) - return - - if(closed) - to_chat(user, SPAN_WARNING("Open the cover before inserting the sample.")) - return - var/obj/item/forensics/swab/swab = attacking_item - if(istype(swab) && swab.is_used()) - user.unEquip(attacking_item) - src.bloodsamp = swab - swab.forceMove(src) - to_chat(user, SPAN_NOTICE("You insert \the [attacking_item] into \the [src].")) - else - to_chat(user, SPAN_WARNING("\The [src] only accepts used swabs.")) - return + // General intended use (w swabs) + if(istype(swab)) + if(swab.is_used()) + // Already a sample loaded + if(bloodsamp) + to_chat(user, SPAN_WARNING("There is already a sample in the machine.")) + return FALSE + // Open the lid, dummy + if(closed) + to_chat(user, SPAN_WARNING("Open the cover before inserting the sample.")) + return FALSE + // Insert the sample + else + user.unEquip(attacking_item) + src.bloodsamp = swab + swab.forceMove(src) + to_chat(user, SPAN_NOTICE("You insert \the [attacking_item] into \the [src].")) + return TRUE + // Used swabs only + else + to_chat(user, SPAN_WARNING("\The [src] only accepts used swabs.")) + return FALSE -/obj/machinery/dnaforensics/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null) - if(stat & (NOPOWER)) return - if(user.stat || user.restrained()) return + else if(attacking_item.tool_behaviour == TOOL_WRENCH) + attacking_item.play_tool_sound(get_turf(src), 100) + 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") + anchored = !anchored + return TRUE + return ..() + +/obj/machinery/dnaforensics/ui_interact(mob/user, datum/tgui/ui) + if(stat & NOPOWER) + return + if(use_check_and_message(user)) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DnaForensics", "QuikScan DNA Analyzer") + ui.open() + ui.set_autoupdate(TRUE) + +/obj/machinery/dnaforensics/ui_data(mob/user) var/list/data = list() + data["scan_progress"] = round(scanner_progress) data["scanning"] = scanning data["bloodsamp"] = (bloodsamp ? bloodsamp.name : "") data["bloodsamp_desc"] = (bloodsamp ? (bloodsamp.desc ? bloodsamp.desc : "No information on record.") : "") - data["lidstate"] = closed + data["lid_closed"] = closed - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data) - if (!ui) - ui = new(user, src, ui_key, "dnaforensics.tmpl", "QuikScan DNA Analyzer", 540, 326) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/obj/machinery/dnaforensics/Topic(href, href_list) +/obj/machinery/dnaforensics/ui_act(action, params) + . = ..() + if(.) + return - if(..()) return 1 + switch(action) + if("scanItem") + if(scanning) + scanning = FALSE + return TRUE - if(stat & (NOPOWER)) - return 0 // don't update UIs attached to this object - - if(href_list["scanItem"]) - if(scanning) - scanning = 0 - else - if(bloodsamp) - if(closed == 1) - scanner_progress = 0 - scanning = 1 - to_chat(usr, SPAN_NOTICE("Scan initiated.")) - update_icon() - else - to_chat(usr, SPAN_NOTICE("Please close sample lid before initiating scan.")) - else + if(!bloodsamp) to_chat(usr, SPAN_WARNING("Insert an item to scan.")) + return TRUE - if(href_list["ejectItem"]) - if(bloodsamp) - bloodsamp.forceMove(src.loc) - bloodsamp = null + if(closed) + scanner_progress = 0 + scanning = TRUE + to_chat(usr, SPAN_NOTICE("Scan initiated.")) + update_icon() + return TRUE - if(href_list["toggleLid"]) - toggle_lid() + to_chat(usr, SPAN_NOTICE("Please close sample lid before initiating scan.")) + return TRUE - return 1 + if("ejectItem") + if(bloodsamp && !scanning) + bloodsamp.forceMove(src.loc) + bloodsamp = null + return TRUE + return FALSE + + if("toggleLid") + toggle_lid() + return TRUE + + return FALSE /obj/machinery/dnaforensics/process() if(scanning) @@ -108,6 +134,7 @@ P.stamped = list(/obj/item/stamp) P.overlays = list("paper_stamped") //dna data itself + var/datetime = "[worlddate2text()] [worldtime2text()]" var/data = "No scan information available." if(bloodsamp.dna != null) data = "Spectometric analysis on provided sample has determined the presence of [length(bloodsamp.dna)] string\s of DNA.

" @@ -117,7 +144,7 @@ data += "DNA: [blood]

" else data += "No DNA found.
" - info = "[src] analysis report #[report_num]
" + info = "[src] forensic report #[report_num]
Scanned [datetime]
" info += "Scanned item: [bloodsamp.name]

" + data P.set_content_unsafe(pname, info) print(P, user = usr) @@ -138,7 +165,7 @@ set name = "Toggle Lid" set src in oview(1) - if(usr.stat || !isliving(usr)) + if(use_check_and_message(usr)) return if(scanning) diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm index ee74eded8c1..b5b0f707f60 100644 --- a/code/modules/overmap/ships/computers/engine_control.dm +++ b/code/modules/overmap/ships/computers/engine_control.dm @@ -10,7 +10,7 @@ var/display_state = "status" /obj/machinery/computer/ship/engines/cockpit - density = 0 + density = FALSE icon = 'icons/obj/cockpit_console.dmi' icon_state = "right" icon_screen = "engine" @@ -28,91 +28,116 @@ can_pass_under = FALSE light_power_on = 1 -/obj/machinery/computer/ship/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/computer/ship/engines/ui_interact(mob/user, datum/tgui/ui) if(!connected) display_reconnect_dialog(user, "ship control systems") return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EnginesControl", "[connected.get_real_name()] Engines Control") + ui.open() + +/obj/machinery/computer/ship/engines/ui_data(mob/user) + var/list/data = list() + + if(!connected) + return - var/data[0] data["state"] = display_state - data["global_state"] = connected.engines_state - data["global_limit"] = round(connected.thrust_limit*100) - var/total_thrust = 0 + data["global_state"] = !!connected.engines_state + data["global_limit"] = round(connected.thrust_limit * 100) + + var/total_thrust = 0 + var/list/enginfo = list() - var/list/enginfo[0] for(var/datum/ship_engine/E in connected.engines) - var/list/rdata[0] - rdata["eng_type"] = E.name - rdata["eng_on"] = E.is_on() - rdata["eng_thrust"] = E.get_thrust() - rdata["eng_thrust_limiter"] = round(E.get_thrust_limit()*100) - rdata["eng_status"] = E.get_status() - rdata["eng_reference"] = "[REF(E)]" + var/list/edata = list() + edata["eng_type"] = E.name + edata["eng_on"] = !!E.is_on() + edata["eng_thrust"] = E.get_thrust() * 10 + edata["eng_thrust_limiter"] = round(E.get_thrust_limit() * 100) + edata["eng_status"] = E.get_status() + edata["eng_reference"] = "[REF(E)]" total_thrust += E.get_thrust() - enginfo.Add(list(rdata)) + enginfo += list(edata) data["engines_info"] = enginfo data["total_thrust"] = total_thrust - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "engines_control.tmpl", "[connected.get_real_name()] Engines Control", 390, 530) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/obj/machinery/computer/ship/engines/Topic(href, href_list) - if(..()) - return TOPIC_HANDLED +/obj/machinery/computer/ship/engines/ui_act(action, params) + . = ..() + if(.) + return - if(href_list["state"]) - display_state = href_list["state"] - return TOPIC_REFRESH + if(!connected) + return FALSE - if(href_list["global_toggle"]) - connected.engines_state = !connected.engines_state - for(var/datum/ship_engine/E in connected.engines) - if(connected.engines_state == !E.is_on()) - E.toggle() - return TOPIC_REFRESH + if(use_check_and_message(usr)) + return FALSE - if(href_list["set_global_limit"]) - var/newlim = tgui_input_number(usr, "Input the new thrust limit.", "Thrust Limit", connected.thrust_limit*100, 100, 0) - if(!CanInteract(usr, GLOB.physical_state)) - return TOPIC_NOACTION - connected.thrust_limit = clamp(newlim/100, 0, 1) - for(var/datum/ship_engine/E in connected.engines) - E.set_thrust_limit(connected.thrust_limit) - return TOPIC_REFRESH + switch(action) + if("set_state") + var/new_state = params["state"] + if(new_state in list("status", "engines")) + display_state = new_state + return TRUE + return FALSE - if(href_list["global_limit"]) - connected.thrust_limit = clamp(connected.thrust_limit + text2num(href_list["global_limit"]), 0, 1) - for(var/datum/ship_engine/E in connected.engines) - E.set_thrust_limit(connected.thrust_limit) - return TOPIC_REFRESH + if("global_toggle") + connected.engines_state = !connected.engines_state + for(var/datum/ship_engine/E in connected.engines) + if(connected.engines_state == !E.is_on()) + E.toggle() + return TRUE - if(href_list["engine"]) - if(href_list["set_limit"]) - var/datum/ship_engine/E = locate(href_list["engine"]) - var/newlim = tgui_input_number(usr, "Input the new thrust limit.", "Thrust Limit", E.get_thrust_limit(), 100, 0) - if(!CanInteract(usr, GLOB.physical_state)) - return - var/limit = clamp(newlim/100, 0, 1) - if(istype(E)) - E.set_thrust_limit(limit) - return TOPIC_REFRESH - if(href_list["limit"]) - var/datum/ship_engine/E = locate(href_list["engine"]) - var/limit = clamp(E.get_thrust_limit() + text2num(href_list["limit"]), 0, 1) - if(istype(E)) - E.set_thrust_limit(limit) - return TOPIC_REFRESH + if("set_global_limit") + var/newlim = tgui_input_number(usr, "Input the new thrust limit.", "Thrust Limit", connected.thrust_limit * 100, 100, 0) + if(isnull(newlim)) + return FALSE - if(href_list["toggle"]) - var/datum/ship_engine/E = locate(href_list["engine"]) - if(istype(E)) - E.toggle() - return TOPIC_REFRESH - return TOPIC_REFRESH - return TOPIC_NOACTION + connected.thrust_limit = clamp(newlim / 100, 0, 1) + for(var/datum/ship_engine/E in connected.engines) + E.set_thrust_limit(connected.thrust_limit) + return TRUE + + if("global_limit_delta") + var/delta = text2num(params["delta"]) + connected.thrust_limit = round(clamp(connected.thrust_limit + delta, 0, 1),0.1) + for(var/datum/ship_engine/E in connected.engines) + E.set_thrust_limit(connected.thrust_limit) + return TRUE + + if("engine_set_limit") + var/datum/ship_engine/E = locate(params["engine"]) + if(!istype(E)) + return FALSE + var/newlim = tgui_input_number(usr, "Input the new thrust limit.", "Thrust Limit", E.get_thrust_limit() * 100, 100, 0) + if(isnull(newlim)) + return FALSE + + var/limit = clamp(newlim / 100, 0, 1) + E.set_thrust_limit(limit) + return TRUE + + if("engine_limit_delta") + var/datum/ship_engine/E = locate(params["engine"]) + if(!istype(E)) + return FALSE + + var/delta = text2num(params["delta"]) + var/limit = clamp(E.get_thrust_limit() + delta, 0, 1) + E.set_thrust_limit(limit) + return TRUE + + if("engine_toggle") + var/datum/ship_engine/E = locate(params["engine"]) + if(!istype(E)) + return FALSE + + E.toggle() + return TRUE + + return FALSE diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm index a9007f68442..5bf6720203e 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -339,8 +339,10 @@ light_power_on = 1 /obj/machinery/computer/ship/navigation/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return FALSE if(use_check_and_message(user)) - return + return FALSE if(!emagged && !allowed(user)) to_chat(user, SPAN_WARNING("Access denied.")) return FALSE diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm index c14847be733..27291e47e34 100644 --- a/code/modules/overmap/ships/computers/ship.dm +++ b/code/modules/overmap/ships/computers/ship.dm @@ -65,12 +65,14 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov return ..() /obj/machinery/computer/ship/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return FALSE // Snowflake case for checking player characters for a Pilot Spacecraft Skill. // Only player characters will have the component. Which will both always be present on them, and will only enable its own return logic if it exists. // NPCs, Ghostroles, and Offship Antags that don't generate skills are unaffected by this check by intentional design so that we don't have to account for them. if (user.GetComponent(PILOT_SPACECRAFT_SKILL_COMPONENT)?.skill_level == SKILL_LEVEL_UNFAMILIAR) to_chat(user, SPAN_WARNING("There's just so many buttons... You have no idea where to even begin.")) - return + return FALSE if(use_check_and_message(user)) return diff --git a/code/modules/overmap/ships/engines/engine.dm b/code/modules/overmap/ships/engines/engine.dm index 3184ca02278..76eb67477d1 100644 --- a/code/modules/overmap/ships/engines/engine.dm +++ b/code/modules/overmap/ships/engines/engine.dm @@ -14,13 +14,13 @@ GLOBAL_LIST_INIT_TYPED(ship_engines, /datum/ship_engine, list()) /datum/ship_engine/proc/can_burn() return 0 -//Tries to fire the engine with power modifier (0..1). Returns thrust. -//Power modifier is intended to be used for maneuvers or the like, -//that should use less/more fuel/electricity than an actual burn. +/// Tries to fire the engine with power modifier (0..1). Returns thrust. +/// Power modifier is intended to be used for maneuvers or the like, +/// that should use less/more fuel/electricity than an actual burn. /datum/ship_engine/proc/burn(var/power_modifier = 1) return 0 -//Returns status string for this engine +/// Returns status string for this engine /datum/ship_engine/proc/get_status() return "All systems nominal" diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm index 1ec2c4e43dd..91a662b3cf4 100644 --- a/code/modules/overmap/ships/engines/gas_thruster.dm +++ b/code/modules/overmap/ships/engines/gas_thruster.dm @@ -158,20 +158,33 @@ /obj/machinery/atmospherics/unary/engine/proc/get_status() . = list() - .+= "Location: [get_area(src)]." - if(stat & NOPOWER) - .+= "Insufficient power to operate." - if(!check_fuel()) - .+= "Insufficient fuel for a burn." - if(stat & BROKEN) - .+= "Inoperable engine configuration." - if(blockage) - .+= "Obstruction of airflow detected." - .+= "Propellant total mass: [round(air_contents.get_mass(),0.01)] kg." - .+= "Propellant used per burn: [round(air_contents.get_mass() * volume_per_burn * thrust_limit / air_contents.volume,0.01)] kg." - .+= "Propellant pressure: [round(XGM_PRESSURE(air_contents)/1000,0.1)] MPa." - . = jointext(.,"
") + . += list(list( + "text" = "Location: [get_area(src)].", + "severity" = "info" + )) + + if(stat & NOPOWER) + . += list(list("text" = "Insufficient power to operate.", "severity" = "bad")) + if(!check_fuel()) + . += list(list("text" = "Insufficient fuel for a burn.", "severity" = "bad")) + if(stat & BROKEN) + . += list(list("text" = "Inoperable engine configuration.", "severity" = "bad")) + if(blockage) + . += list(list("text" = "Obstruction of airflow detected.", "severity" = "bad")) + + . += list(list( + "text" = "Propellant total mass: [round(air_contents.get_mass(), 0.01)] kg.", + "severity" = null + )) + . += list(list( + "text" = "Propellant used per burn: [round(air_contents.get_mass() * volume_per_burn * thrust_limit / air_contents.volume, 0.01)] kg.", + "severity" = null + )) + . += list(list( + "text" = "Propellant pressure: [round(XGM_PRESSURE(air_contents)/1000, 0.1)] MPa.", + "severity" = null + )) /obj/machinery/atmospherics/unary/engine/power_change() . = ..() @@ -246,7 +259,7 @@ new/obj/effect/engine_exhaust(T, dir, is_cmb && air_contents.temperature >= PHORON_MINIMUM_BURN_TEMPERATURE) /obj/machinery/atmospherics/unary/engine/proc/calculate_thrust(datum/gas_mixture/propellant, used_part = 1) - return round(sqrt(propellant.get_mass() * used_part * sqrt(XGM_PRESSURE(air_contents)/200)),0.1) + return round(sqrt(propellant.get_mass() * used_part * sqrt(XGM_PRESSURE(air_contents)/200)),0.01) //Exhaust effect /obj/effect/engine_exhaust diff --git a/code/modules/overmap/ships/engines/ion_thruster.dm b/code/modules/overmap/ships/engines/ion_thruster.dm index 83f8df3f1ed..b14f5c52c6d 100644 --- a/code/modules/overmap/ships/engines/ion_thruster.dm +++ b/code/modules/overmap/ships/engines/ion_thruster.dm @@ -52,7 +52,7 @@ var/datum/ship_engine/ion/controller var/thrust_limit = 1 - var/on = 1 + var/on = TRUE var/burn_cost = 36000 var/generated_thrust = 2.5 @@ -66,11 +66,14 @@ /obj/machinery/ion_engine/proc/get_status() . = list() - .+= "Location: [get_area(src)]." - if(!powered()) - .+= "Insufficient power to operate." - . = jointext(.,"
") + . += list(list( + "text" = "Location: [get_area(src)].", + "severity" = "info" + )) + + if(!powered()) + . += list(list("text" = "Insufficient power to operate.", "severity" = "bad")) /obj/machinery/ion_engine/proc/burn(var/power_modifier = 1) if(!on && !powered()) diff --git a/code/modules/overmap/ships/engines/maneuvering_thrusters.dm b/code/modules/overmap/ships/engines/maneuvering_thrusters.dm index 53f64ed8f90..79e11336af7 100644 --- a/code/modules/overmap/ships/engines/maneuvering_thrusters.dm +++ b/code/modules/overmap/ships/engines/maneuvering_thrusters.dm @@ -71,8 +71,11 @@ /obj/machinery/maneuvering_engine/proc/get_status() . = list() - .+= "Location: [get_area(src)]." - . = jointext(.,"
") + + . += list(list( + "text" = "Location: [get_area(src)].", + "severity" = "info" + )) /obj/machinery/maneuvering_engine/proc/burn(var/power_modifier = 1) . = thrust_limit * generated_thrust * power_modifier diff --git a/html/changelogs/Bat-NanoConversions.yml b/html/changelogs/Bat-NanoConversions.yml new file mode 100644 index 00000000000..c30f48056f3 --- /dev/null +++ b/html/changelogs/Bat-NanoConversions.yml @@ -0,0 +1,7 @@ +author: Batrachophrenoboocosmomachia +delete-after: True +changes: + - refactor: "Migrates DNA Analyzer NanoUI to TGUI." + - refactor: "Migrates Engines Control NanoUI to TGUI." + - qol: "DNA Analyzer reports now provide the date and time the scan was produced." + - bugfix: "Ship computers no longer function after their power is cut." diff --git a/nano/templates/dnaforensics.tmpl b/nano/templates/dnaforensics.tmpl deleted file mode 100644 index efe69e6158a..00000000000 --- a/nano/templates/dnaforensics.tmpl +++ /dev/null @@ -1,45 +0,0 @@ -

Machine Status

-
-
- {{:helper.link(data.scanning ? 'Halt Scan' : 'Begin Scan', 'signal-diag', {'scanItem' : 1}, null)}} -
-
- {{:helper.link((data.lidstate ? 'Open Lid' : 'Close Lid'), (data.lidstate ? 'unlocked' : 'locked'), {'toggleLid' : 1}, null)}} -
-
- {{:helper.link('Eject item', 'eject', {'ejectItem' : 1}, (data.bloodsamp && !data.scanning) ? null : 'disabled')}} -
-
-

Scanner

-
-
Scan progress:
-
- {{:helper.displayBar(data.scan_progress, 0, 100, 'good')}} - {{:data.scan_progress}} % -
-
- {{if data.scan_progress >= 100}} - Scan completed successfully. - {{/if}} -
-
-
-
-
Item:
-
- {{if data.bloodsamp}} - {{:data.bloodsamp}} - {{else}} - No item inserted - {{/if}} -
-
-
-
Heuristic analysis:
-
- {{if data.bloodsamp_desc}} - {{:data.bloodsamp_desc}} - {{/if}} -
-
-
\ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/DnaForensics.tsx b/tgui/packages/tgui/interfaces/DnaForensics.tsx new file mode 100644 index 00000000000..4df146a9e40 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DnaForensics.tsx @@ -0,0 +1,90 @@ +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, ProgressBar, Section } from '../components'; +import { round } from 'common/math'; +import { Window } from '../layouts'; +import { BooleanLike } from '../../common/react'; + +type Data = { + scan_progress: number; + scanning: BooleanLike; + bloodsamp: string; + bloodsamp_desc: string; + lid_closed: BooleanLike; +}; + +export const DnaForensics = (props, context) => { + const { act, data } = useBackend(context); + const hasItem = !!data.bloodsamp; + + return ( + + +
+ + + + + + + + + +
+ +
+ + + + {round(data.scan_progress, 1)}% + + {data.scan_progress >= 100 && ( + + Scan completed successfully. + + )} + +
+ +
+ + + {hasItem ? ( + {data.bloodsamp} + ) : ( + No item inserted + )} + + + {data.bloodsamp_desc ? ( + {data.bloodsamp_desc} + ) : ( + + )} + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/EnginesControl.tsx b/tgui/packages/tgui/interfaces/EnginesControl.tsx new file mode 100644 index 00000000000..96778fbd62d --- /dev/null +++ b/tgui/packages/tgui/interfaces/EnginesControl.tsx @@ -0,0 +1,217 @@ +import { useBackend } from '../backend'; +import { Window } from '../layouts'; +import { + Box, + Button, + LabeledList, + NoticeBox, + Section, + Stack, + Tabs, +} from '../components'; +import { round } from 'common/math'; +import { BooleanLike } from '../../common/react'; + +type EngineInfo = { + eng_type: string; + eng_on: BooleanLike; + eng_thrust: number; + eng_thrust_limiter: number; // percentage + eng_status: StatusLine[]; + eng_reference: string; +}; + +type StatusLine = { + text: string; + severity?: 'good' | 'average' | 'bad'; +}; + +type Data = { + state: 'status' | 'engines' | string; + global_state: BooleanLike; + global_limit: number; // percentage + total_thrust: number; + engines_info: EngineInfo[]; +}; + +export const EnginesControl = (_props, context) => { + const { act, data } = useBackend(context); + + const engines = data.engines_info || []; + const globalOn = !!data.global_state; + + const severityToColor = (sev?: StatusLine['severity']) => { + switch (sev) { + case 'bad': + return 'bad'; + case 'average': + return 'average'; + case 'good': + return 'good'; + default: + return undefined; + } + }; + + return ( + + +
+ + act('set_state', { state: 'status' })} + > + Overall info + + act('set_state', { state: 'engines' })} + > + Details + + +
+ +
+ + + + + + + +
+ + {!engines.length && No engines detected.} + + {data.state === 'status' && + engines.map((E, i) => ( +
+ + + + + + {round(E.eng_thrust * 10, 0.1) / 10} + + + {round(E.eng_thrust_limiter, 1)}% + + +
+ ))} + + {data.state === 'engines' && + engines.map((E, i) => ( +
+ + + + + + {E.eng_type} + + + + {E.eng_on ? 'Online' : 'Offline'} + + + {E.eng_status?.length ? ( + E.eng_status.map((line, idx) => ( + + {line.text} + + )) + ) : ( + - + )} + + + + + {round(E.eng_thrust * 10, 0.1) / 10} + + + + + + + + +
+ ))} +
+
+ ); +}; + +export default EnginesControl;