diff --git a/aurorastation.dme b/aurorastation.dme index 512e82059e7..0e9d5f5fd71 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -3386,6 +3386,7 @@ #include "code\modules\tgui\states\default.dm" #include "code\modules\tgui\states\hands.dm" #include "code\modules\tgui\states\human_adjacent.dm" +#include "code\modules\tgui\states\human_adjacent_loc.dm" #include "code\modules\tgui\states\inventory.dm" #include "code\modules\tgui\states\never.dm" #include "code\modules\tgui\states\new_player.dm" diff --git a/code/game/machinery/body_scanner.dm b/code/game/machinery/body_scanner.dm index 96cdb5bec98..11f8a8fef54 100644 --- a/code/game/machinery/body_scanner.dm +++ b/code/game/machinery/body_scanner.dm @@ -240,6 +240,7 @@ /obj/machinery/body_scanconsole name = "body scanner console" + var/tgui_name = "Zeng-Hu Pharmaceuticals Body Scanner" desc = "An advanced control panel that can be used to interface with a connected body scanner." icon = 'icons/obj/sleeper.dmi' icon_state = "body_scannerconsole" @@ -261,6 +262,10 @@ var/list/data = list() var/scan_data + var/has_detailed_view = TRUE + var/has_print_and_eject = TRUE + var/no_scan_message = "No diagnostics profile installed for this species." + /obj/machinery/body_scanconsole/Destroy() if (connected) connected.connected = null @@ -310,10 +315,7 @@ switch(action) // shouldn't be reachable if occupant is invalid if("print") - var/obj/item/paper/medscan/R = new /obj/item/paper/medscan - R.color = "#eeffe8" - R.set_content_unsafe("Scan ([connected.occupant])", format_occupant_data(connected.get_occupant_data())) - + var/obj/item/paper/medscan/R = new /obj/item/paper/medscan(src, format_occupant_data(connected.get_occupant_data()), "Scan ([connected.occupant])", connected.occupant) print(R, message = "\The [src] beeps, printing \the [R] after a moment.", user = usr) if("eject") @@ -328,24 +330,39 @@ return !!length(connected_displays) /obj/machinery/body_scanconsole/ui_interact(mob/user, var/datum/tgui/ui) - if(!connected) + if(!get_connected()) to_chat(usr, SPAN_WARNING("[icon2html(src, usr)]Error: No body scanner detected.")) return ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "BodyScanner", "Zeng-Hu Pharmaceuticals Body Scanner", 450, 500) + ui = new(user, src, "BodyScanner", tgui_name, 850, 500) ui.open() /obj/machinery/body_scanconsole/proc/remove_display(obj/machinery/computer/operating/display) connected_displays -= display destroyed_event.unregister(display, src, PROC_REF(remove_display)) +/obj/machinery/body_scanconsole/proc/get_connected() + if(connected) + return connected + return null + +/obj/machinery/body_scanconsole/proc/get_occupant() + if(connected) + return connected.occupant + return null + +/obj/machinery/body_scanconsole/proc/check_species() + if(connected) + return connected.check_species() + return FALSE + /obj/machinery/body_scanconsole/ui_data(mob/user) var/list/data = list( "noscan" = null, "nocons" = null, "occupied" = null, - "invalid" = null, + "invalid" = TRUE, "ipc" = null, "stationtime" = null, "stat" = null, @@ -377,17 +394,18 @@ "bodyparts" = list(), "organs" = list(), "missingparts" = list(), - "hasmissing" = null + "hasmissing" = null, + "has_detailed_view" = has_detailed_view, + "has_print_and_eject" = has_print_and_eject, + "no_scan_message" = no_scan_message ) - var/mob/living/carbon/human/occupant - if (connected) - occupant = connected.occupant - - data["noscan"] = !!connected.check_species() - data["nocons"] = !connected - data["occupied"] = !!connected.occupant - data["invalid"] = !!connected.check_species() + var/mob/living/carbon/human/occupant = get_occupant() + if(occupant) + data["noscan"] = !!check_species() + data["nocons"] = !get_connected() + data["occupied"] = !!occupant + data["invalid"] = !!check_species() data["ipc"] = occupant && isipc(occupant) if (!data["invalid"]) @@ -839,3 +857,44 @@ dat += "" return dat + +/// an embedded bodyscanner belonging to a patient monitoring console +/obj/machinery/body_scanconsole/embedded + name = "embedded bodyscanner" + tgui_name = "Zeng-Hu Pharmaceuticals Surgical Theater" + has_detailed_view = FALSE + has_print_and_eject = FALSE + no_scan_message = "No matching body scanner primer has been added to the monitoring console." + + var/obj/machinery/computer/operating/monitor_console + +/obj/machinery/body_scanconsole/embedded/Initialize(mapload, d = 0, populate_components = TRUE, is_internal = FALSE) + . = ..() + monitor_console = loc + +/obj/machinery/body_scanconsole/embedded/Destroy() + monitor_console = null + return ..() + +/obj/machinery/body_scanconsole/embedded/ui_state(mob/user) + return human_adjacent_loc_state + +/obj/machinery/body_scanconsole/embedded/get_connected() + if(monitor_console) + return monitor_console + return null + +/obj/machinery/body_scanconsole/embedded/get_occupant() + if(monitor_console?.table) + return monitor_console.table.occupant + return null + +// if our primer has a scan target, that means it was validated by a bodyscanner +/obj/machinery/body_scanconsole/embedded/check_species() + var/atom/occupant = get_occupant() + if(!occupant) + return TRUE + if(monitor_console?.primer) + var/atom/primer_scan_target = monitor_console.primer.scan_target?.resolve() + return primer_scan_target != occupant + return TRUE diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index e8fc8398bca..5c4ae53fe0f 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -9,11 +9,57 @@ icon_keyboard = "teal_key" light_color = LIGHT_COLOR_BLUE circuit = /obj/item/circuitboard/operating - var/obj/machinery/optable/table = null - var/list/bodyscans = list() - var/selected = 0 + var/obj/machinery/optable/table + var/obj/item/paper/medscan/primer + var/obj/machinery/body_scanconsole/embedded/embedded_scanner +/obj/machinery/computer/operating/Initialize() + . = ..() + embedded_scanner = new /obj/machinery/body_scanconsole/embedded(src, 0, TRUE, TRUE) + for(var/obj/machinery/optable/T in orange(1,src)) + table = T + if (table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + QDEL_NULL(embedded_scanner) + QDEL_NULL(primer) + table = null + return ..() + +/obj/machinery/computer/operating/attackby(obj/item/item, mob/user) + if(istype(item, /obj/item/paper/medscan)) + if(primer) + to_chat(user, SPAN_WARNING("\The [src] already has a primer!")) + return + user.visible_message("\The [user] slides \the [item] into \the [src].", SPAN_NOTICE("You slide \the [item] into \the [src]."), range = 3) + user.drop_from_inventory(item, src) + primer = item + +/obj/machinery/computer/operating/attack_ai(mob/user) + if(!ai_can_interact(user)) + return + return attack_hand(user) + +/obj/machinery/computer/operating/attack_hand(mob/user) + if(..()) + return + embedded_scanner.ui_interact(user) + +/obj/machinery/computer/operating/verb/eject_primer() + set src in oview(1) + set category = "Object" + set name = "Eject Primer" + + if(!primer) + to_chat(usr, SPAN_WARNING("\The [src] doesn't have a primer!")) + return + + usr.visible_message("\The [usr] takes \the [primer] out of \the [src].", SPAN_NOTICE("You take \the [primer] out of \the [src]"), range = 3) + usr.put_in_hands(primer) + primer = null /obj/machinery/computer/operating/terminal name = "patient monitoring terminal" @@ -24,100 +70,3 @@ has_off_keyboards = TRUE can_pass_under = FALSE light_power_on = 1 - -/obj/machinery/computer/operating/New() - ..() - for(var/obj/machinery/optable/T in orange(1,src)) - table = T - if (table) - table.computer = src - break - -/obj/machinery/computer/operating/attack_ai(mob/user) - if(!ai_can_interact(user)) - return - return attack_hand(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - if(..()) - return - - ui_interact(user) - -/obj/machinery/computer/operating/ui_interact(mob/user, var/datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Operating", "Patient Monitoring Console", 450, 500) - ui.open() - -/obj/machinery/computer/operating/process() - if(operable()) - updateDialog() - -/obj/machinery/computer/operating/ui_data(mob/user) - var/list/data = list( - "noscan" = null, - "nocons" = null, - "occupied" = null, - "invalid" = null, - "ipc" = null, - "stat" = null, - "name" = null, - "species" = null, - "brain_activity" = null, - "pulse" = null, - "blood_pressure" = null, - "blood_pressure_level" = null, - "blood_volume" = null, - "blood_o2" = null, - "blood_type" = null - ) - - var/mob/living/carbon/human/occupant - if(table) - occupant = table.occupant - - data["noscan"] = !!table.check_species() - data["nocons"] = !table - data["occupied"] = !!table.occupant - data["invalid"] = !!table.check_species() - data["ipc"] = occupant && isipc(occupant) - - if(!data["invalid"]) - var/brain_result = occupant.get_brain_result() - var/pulse_result - if(occupant.should_have_organ(BP_HEART)) - var/obj/item/organ/internal/heart/heart = occupant.internal_organs_by_name[BP_HEART] - if(!heart) - pulse_result = 0 - else if(BP_IS_ROBOTIC(heart)) - pulse_result = -2 - else if(occupant.status_flags & FAKEDEATH) - pulse_result = 0 - else - pulse_result = occupant.get_pulse(GETPULSE_TOOL) - else - pulse_result = -1 - - if(pulse_result == ">250") - pulse_result = -3 - - var/displayed_stat = occupant.stat - var/blood_oxygenation = occupant.get_blood_oxygenation() - if(occupant.status_flags & FAKEDEATH) - displayed_stat = DEAD - blood_oxygenation = min(blood_oxygenation, BLOOD_VOLUME_SURVIVE) - - data["stat"] = displayed_stat - data["name"] = occupant.name - data["species"] = occupant.get_species() - data["brain_activity"] = brain_result - data["pulse"] = text2num(pulse_result) - data["blood_pressure"] = occupant.get_blood_pressure() - data["blood_pressure_level"] = occupant.get_blood_pressure_alert() - data["blood_volume"] = occupant.get_blood_volume() - data["blood_o2"] = blood_oxygenation - data["blood_type"] = occupant.dna.b_type - - return data diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 8af9f5bdc50..147e7ba0aaa 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -718,10 +718,7 @@ BREATH ANALYZER add_fingerprint(user) /obj/item/device/advanced_healthanalyzer/proc/print_scan(var/mob/M, var/mob/living/user) - var/obj/item/paper/medscan/R = new(user.loc) - R.color = "#eeffe8" - R.set_content_unsafe("Scan ([M.name])", connected.format_occupant_data(get_occupant_data(M))) - + var/obj/item/paper/medscan/R = new /obj/item/paper/medscan(src, connected.format_occupant_data(get_occupant_data(M)), "Scan ([M.name])", M) connected.print(R, message = "\The [src] beeps, printing \the [R] after a moment.", user = user) /obj/item/device/advanced_healthanalyzer/proc/get_occupant_data(var/mob/living/carbon/human/H) diff --git a/code/modules/heavy_vehicle/equipment/medical.dm b/code/modules/heavy_vehicle/equipment/medical.dm index 4cb878c41d4..2fbb4524270 100644 --- a/code/modules/heavy_vehicle/equipment/medical.dm +++ b/code/modules/heavy_vehicle/equipment/medical.dm @@ -311,10 +311,7 @@ add_fingerprint(user) /obj/item/device/healthanalyzer/mech/proc/print_scan(var/mob/M, var/mob/living/user) - var/obj/item/paper/medscan/R = new(user.loc) - R.color = "#eeffe8" - R.set_content_unsafe("Scan ([M.name])", internal_bodyscanner.format_occupant_data(get_medical_data(M))) - + var/obj/item/paper/medscan/R = new /obj/item/paper/medscan(user.loc, internal_bodyscanner.format_occupant_data(get_medical_data(M)), "Scan ([M.name])", M) if(ishuman(user) && !(user.l_hand && user.r_hand)) user.put_in_hands(R) user.visible_message(SPAN_NOTICE("\The [src] spits out a piece of paper.")) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 9a8a7346f24..2d2be2f9eec 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -45,12 +45,16 @@ pickup_sound = 'sound/items/pickup/paper.ogg' var/can_change_icon_state = TRUE + var/set_unsafe_on_init = FALSE /obj/item/paper/Initialize(mapload, text, title) . = ..() base_state = initial(icon_state) if (text || title) - set_content(title, text ? text : info) + if(set_unsafe_on_init) + set_content_unsafe(title, text ? text : info) + else + set_content(title, text ? text : info) else updateinfolinks() if (mapload) @@ -704,6 +708,13 @@ /obj/item/paper/medscan icon_state = "medscan" + color = "#eeffe8" + set_unsafe_on_init = TRUE + var/datum/weakref/scan_target + +/obj/item/paper/medscan/Initialize(mapload, text, title, var/atom/set_scan_target) + . = ..() + scan_target = WEAKREF(set_scan_target) // // Fluff Papers diff --git a/code/modules/tgui/states/human_adjacent_loc.dm b/code/modules/tgui/states/human_adjacent_loc.dm new file mode 100644 index 00000000000..bb93882458e --- /dev/null +++ b/code/modules/tgui/states/human_adjacent_loc.dm @@ -0,0 +1,18 @@ +/** + * tgui state: human_adjacent_state + * + * In addition to default checks, only allows interaction for a + * human adjacent user. + */ + +var/global/datum/ui_state/human_adjacent_loc_state/human_adjacent_loc_state = new + +/datum/ui_state/human_adjacent_loc_state/can_use_topic(src_object, mob/user) + var/turf/src_object_turf = get_turf(src_object) + + . = user.default_can_use_topic(src_object_turf) + + var/dist = get_dist(src_object_turf, user) + if((dist > 1) || (!ishuman(user))) + // Can't be used unless adjacent and human, even with TK + . = min(., UI_UPDATE) diff --git a/html/changelogs/geeves-patient_monitoring_console.yml b/html/changelogs/geeves-patient_monitoring_console.yml new file mode 100644 index 00000000000..f78e8da0ae3 --- /dev/null +++ b/html/changelogs/geeves-patient_monitoring_console.yml @@ -0,0 +1,6 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "Added an embedded bodyscanner to the patient monitoring console, the caveat being that this scanner needs a printed scan from a real bodyscanner to prime the data before it can work." diff --git a/tgui/packages/tgui/interfaces/BodyScanner.tsx b/tgui/packages/tgui/interfaces/BodyScanner.tsx index d87c591bdcb..ee9c37a3d88 100644 --- a/tgui/packages/tgui/interfaces/BodyScanner.tsx +++ b/tgui/packages/tgui/interfaces/BodyScanner.tsx @@ -1,6 +1,6 @@ import { BooleanLike } from '../../common/react'; import { useBackend } from '../backend'; -import { BlockQuote, Box, Button, LabeledList, Section, Table } from '../components'; +import { BlockQuote, Box, Button, Flex, LabeledList, Section, Table } from '../components'; import { Window } from '../layouts'; export type ScannerData = { @@ -46,6 +46,10 @@ export type ScannerData = { has_external_injuries: BooleanLike; missing_limbs: string; missing_organs: string; + + has_detailed_view: BooleanLike; + has_print_and_eject: BooleanLike; + no_scan_message: string; }; type Organ = { @@ -92,7 +96,7 @@ export const InvalidWindow = (props, context) => { : data.ipc ? 'An object in the scanner bed is interfering with the sensor array.' : data.noscan - ? 'No diagnostics profile installed for this species.' + ? data.no_scan_message : 'Unknown error.'} @@ -106,13 +110,15 @@ export const ScannerWindow = (props, context) => { const { act, data } = useBackend(context); return ( - - - -
+ +
-
- {data.has_external_injuries ? ( - - ) : ( -
- No external injuries detected. -
- )} -
+ ) : null + }> -
- {data.missing_limbs === 'Nothing' ? '' : } -
-
-
- {data.has_internal_injuries ? ( - - ) : ( -
- No internal injuries detected. -
+ {data.name} + {data.has_detailed_view ? ( + + {data.species} + + ) : null} + {data.has_detailed_view ? ( + + {consciousnessText(data.stat)} + + ) : null} + + {brainText(data.brain_activity)} + + + {data.pulse} BPM + + {data.has_detailed_view ? ( + + {data.bodytemp}°C + + ) : null} + {data.has_detailed_view ? null : ( + + {Math.round(data.blood_o2)}% + + )} + {data.has_detailed_view ? null : ( + + {Math.round(data.blood_volume)}% + )} -
- -
- {data.missing_organs === 'Nothing' ? '' : } -
- - -
+ + + {data.has_detailed_view ? ( + +
+ + + {data.blood_pressure} + + + {Math.round(data.blood_o2)}% + + + {Math.round(data.blood_volume)}% + + + {data.blood_type} + + {data.inaprovaline_amount ? ( + + {Math.round(data.inaprovaline_amount)}u + + ) : ( + '' + )} + {data.soporific_amount ? ( + + {Math.round(data.soporific_amount)}u + + ) : ( + '' + )} + {data.bicaridine_amount ? ( + + {Math.round(data.bicaridine_amount)}u + + ) : ( + '' + )} + {data.dermaline_amount ? ( + + {Math.round(data.dermaline_amount)}u + + ) : ( + '' + )} + {data.dexalin_amount ? ( + + {Math.round(data.dexalin_amount)}u + + ) : ( + '' + )} + {data.thetamycin_amount ? ( + + {Math.round(data.thetamycin_amount)}u + + ) : ( + '' + )} + {data.other_amount ? ( + + {Math.round(data.other_amount)}u + + ) : ( + '' + )} + +
+
+ ) : null} + {data.has_detailed_view ? ( + +
+ + + {Math.round(data.rads)} Gy + + + {data.cloneLoss} + + + {data.paralysis + ? Math.round(data.paralysis / 4) + ' Seconds Left' + : 'None'} + + +
+
+ ) : null} + {data.has_detailed_view ? ( + +
+ + + {data.bruteLoss} + + + {data.fireLoss} + + + {data.oxyLoss} + + + {data.toxLoss} + + +
+
+ ) : null} + +
+ {data.has_external_injuries ? ( + + ) : ( +
+ No external injuries detected. +
+ )} +
+
+ +
+ {data.missing_limbs === 'Nothing' ? ( +
+ No missing extremities detected. +
+ ) : ( + + )} +
+
+ +
+ {data.has_internal_injuries ? ( + + ) : ( +
+ No internal injuries detected. +
+ )} +
+
+ +
+ {data.missing_organs === 'Nothing' ? ( +
+ No missing internal organs detected. +
+ ) : ( + + )} +
+
+ ); };