Ody sleeper new UI (#82513)

## About The Pull Request

Removes the old reagent manager menu, and replaces it with a modern tsx
one.
Old:
![Screenshot 2024-04-06
130418](https://github.com/tgstation/tgstation/assets/163439532/71cb46a8-ad3d-4c74-a1de-19c41b07a461)

New:
![Screenshot 2024-04-06
130427](https://github.com/tgstation/tgstation/assets/163439532/008722b5-0473-4cdb-99ec-2eeba3886eeb)

+Candy Stripes:

![image](https://github.com/tgstation/tgstation/assets/163439532/c0f0681d-a298-44a6-a57d-bdb212040524)


## Why It's Good For The Game

Old menu doesn't look good. Fixes some issues old menu had.

## Changelog

🆑
fix: fixes ody sleeper UI not updating to new chems to inject.
fix: fixes ody sleeper not telling user why they can't pick up a person.
code: gives a common method for giving reagent list data to .tsx
/🆑

---------

Co-authored-by: san7890 <the@san7890.com>
This commit is contained in:
Bilbo367
2024-04-10 11:56:15 -07:00
committed by GitHub
co-authored by san7890
parent 430896b5cf
commit 3b786c2b4f
2 changed files with 140 additions and 142 deletions
@@ -17,12 +17,12 @@
/obj/item/mecha_parts/mecha_equipment/medical/sleeper
name = "mounted sleeper"
desc = "Equipment for medical exosuits. A mounted sleeper that stabilizes patients and can inject reagents in the exosuit's reserves."
desc = "Equipment for medical exosuits. A mounted sleeper that stabilizes patients and can inject reagents from a equipped exosuit syringe gun."
icon_state = "mecha_sleeper"
energy_drain = 20
range = MECHA_MELEE
equip_cooldown = 20
///ref to the patient loaded in the sleeper
/// ref to the patient loaded in the sleeper
var/mob/living/carbon/patient
/// amount of chems to inject into patient from other hands syringe gun
var/inject_amount = 10
@@ -32,15 +32,47 @@
content.forceMove(get_turf(src))
return ..()
/obj/item/mecha_parts/mecha_equipment/medical/proc/get_reagent_data(list/datum/reagent/reagent_list)
var/list/contained_reagents = list()
if(length(reagent_list))
for(var/datum/reagent/reagent as anything in reagent_list)
contained_reagents += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01))) // list in a list because Byond merges the first list...
return contained_reagents
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/get_snowflake_data()
var/list/data = list("snowflake_id" = MECHA_SNOWFLAKE_ID_SLEEPER)
if(!patient)
if(isnull(patient))
return data
var/patient_state
switch(patient.stat)
if(0)
patient_state = "Conscious"
if(1)
patient_state = "Unconscious"
if(2)
patient_state = "*dead*"
else
patient_state = "Unknown"
var/core_temp = ""
if(ishuman(patient))
var/mob/living/carbon/human/humi = patient
core_temp = humi.bodytemperature-T0C
data["patient"] = list(
"patientname" = patient.name,
"is_dead" = patient.stat == DEAD,
"patient_name" = patient.name,
"patient_health" = patient.health/patient.maxHealth,
"patient_state" = patient_state,
"core_temp" = core_temp,
"brute_loss" = patient.getBruteLoss(),
"burn_loss" = patient.getFireLoss(),
"toxin_loss" = patient.getToxLoss(),
"oxygen_loss" = patient.getOxyLoss(),
)
data["has_brain_damage"] = patient.get_organ_loss(ORGAN_SLOT_BRAIN) != 0
data["has_traumas"] = length(patient.get_traumas()) != 0
data["contained_reagents"] = get_reagent_data(patient.reagents.reagent_list)
var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate(/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun) in chassis
data["injectible_reagents"] = get_reagent_data(shooter.reagents.reagent_list)
return data
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/handle_ui_act(action, list/params)
@@ -48,10 +80,13 @@
if("eject")
go_out()
return TRUE
if("view_stats")
usr << browse(get_patient_stats(),"window=msleeper")
onclose(usr, "msleeper")
return FALSE
var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/shooter = locate() in chassis
for(var/datum/reagent/medication in shooter.reagents.reagent_list)
if(action == ("inject_reagent_" + medication.name))
inject_reagent(medication, shooter)
break // or maybe return TRUE? i'm not certain
return FALSE
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/action(mob/source, atom/atomtarget, list/modifiers)
if(!action_checks(atomtarget))
@@ -59,7 +94,7 @@
if(!iscarbon(atomtarget))
return
var/mob/living/carbon/target = atomtarget
if(!patient_insertion_check(target))
if(!patient_insertion_check(target, source))
return
to_chat(source, "[icon2html(src, source)][span_notice("You start putting [target] into [src]...")]")
chassis.visible_message(span_warning("[chassis] starts putting [target] into \the [src]."))
@@ -76,7 +111,7 @@
return ..()
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/patient_insertion_check(mob/living/carbon/target, mob/user)
if(target.buckled)
if(!isnull(target.buckled))
to_chat(user, "[icon2html(src, user)][span_warning("[target] will not fit into the sleeper because [target.p_theyre()] buckled to [target.buckled]!")]")
return FALSE
if(target.has_buckled_mobs())
@@ -103,92 +138,7 @@
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/Topic(href,href_list)
. = ..()
if(.)
return
if(!(usr in chassis.occupants))
return
if(href_list["inject"])
var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG = locate() in chassis
var/datum/reagent/R = locate(href_list["inject"]) in SG.reagents.reagent_list
if(istype(R))
inject_reagent(R, SG)
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/get_patient_stats()
if(!patient)
return
return {"<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<title>[patient] statistics</title>
<script language='javascript' type='text/javascript'>
[js_byjax]
</script>
<style>
h3 {margin-bottom:2px;font-size:14px;}
#lossinfo, #reagents, #injectwith {padding-left:15px;}
</style>
</head>
<body>
<h3>Health statistics</h3>
<div id="lossinfo">
[get_patient_dam()]
</div>
<h3>Reagents in bloodstream</h3>
<div id="reagents">
[get_patient_reagents()]
</div>
<div id="injectwith">
[get_available_reagents()]
</div>
</body>
</html>"}
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/get_patient_dam()
var/t1
switch(patient.stat)
if(0)
t1 = "Conscious"
if(1)
t1 = "Unconscious"
if(2)
t1 = "*dead*"
else
t1 = "Unknown"
var/core_temp = ""
if(ishuman(patient))
var/mob/living/carbon/human/humi = patient
core_temp = {"<font color="[humi.coretemperature > 300 ? "#3d5bc3" : "#c51e1e"]"><b>Body Temperature:</b> [humi.bodytemperature-T0C]&deg;C ([humi.bodytemperature*1.8-459.67]&deg;F)</font><br />"}
return {"<font color="[patient.health > 50 ? "#3d5bc3" : "#c51e1e"]"><b>Health:</b> [patient.stat > 1 ? "[t1]" : "[patient.health]% ([t1])"]</font><br />
[core_temp]
<font color="[patient.bodytemperature > 300 ? "#3d5bc3" : "#c51e1e"]"><b>Body Temperature:</b> [patient.bodytemperature-T0C]&deg;C ([patient.bodytemperature*1.8-459.67]&deg;F)</font><br />
<font color="[patient.getBruteLoss() < 60 ? "#3d5bc3" : "#c51e1e"]"><b>Brute Damage:</b> [patient.getBruteLoss()]%</font><br />
<font color="[patient.getOxyLoss() < 60 ? "#3d5bc3" : "#c51e1e"]"><b>Respiratory Damage:</b> [patient.getOxyLoss()]%</font><br />
<font color="[patient.getToxLoss() < 60 ? "#3d5bc3" : "#c51e1e"]"><b>Toxin Content:</b> [patient.getToxLoss()]%</font><br />
<font color="[patient.getFireLoss() < 60 ? "#3d5bc3" : "#c51e1e"]"><b>Burn Severity:</b> [patient.getFireLoss()]%</font><br />
[span_danger("[patient.get_organ_loss(ORGAN_SLOT_BRAIN) ? "Significant brain damage detected." : ""]")]<br />
[span_danger("[length(patient.get_traumas()) ? "Brain Traumas detected." : ""]")]<br />
"}
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/get_patient_reagents()
if(patient.reagents)
for(var/datum/reagent/R in patient.reagents.reagent_list)
if(R.volume > 0)
. += "[R]: [round(R.volume,0.01)]<br />"
return . || "None"
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/get_available_reagents()
var/output
var/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG = locate(/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun) in chassis
if(SG && SG.reagents && islist(SG.reagents.reagent_list))
for(var/datum/reagent/R in SG.reagents.reagent_list)
if(R.volume > 0)
output += "<a href=\"?src=[REF(src)];inject=[REF(R)]\">Inject [R.name]</a><br />"
return output
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/inject_reagent(datum/reagent/R,obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG)
/obj/item/mecha_parts/mecha_equipment/medical/sleeper/proc/inject_reagent(datum/reagent/R, obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/SG)
if(!R || !patient || !SG || !(SG in chassis.flat_equipment))
return
var/to_inject = min(R.volume, inject_amount)
@@ -294,11 +244,7 @@
"total_reagents" = reagents.maximum_volume,
"analyzed_reagents" = analyzed_reagents,
)
var/list/contained_reagents = list()
if(length(reagents.reagent_list))
for(var/datum/reagent/reagent as anything in reagents.reagent_list)
contained_reagents += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01))) // list in a list because Byond merges the first list...
data["contained_reagents"] = contained_reagents
data["contained_reagents"] = get_reagent_data(reagents.reagent_list)
return data
/obj/item/mecha_parts/mecha_equipment/medical/syringe_gun/handle_ui_act(action, list/params)
@@ -228,8 +228,8 @@ const ModuleDetailsBasic = (props) => {
label="Integrity"
buttons={
<Button
content={'Repair'}
icon={'wrench'}
content="Repair"
icon="wrench"
onClick={() =>
act('equip_act', {
ref: ref,
@@ -363,7 +363,7 @@ const SnowflakeWeaponBallistic = (props) => {
!disabledreload &&
projectiles_cache > 0 && (
<Button
icon={'redo'}
icon="redo"
disabled={projectiles >= max_magazine}
onClick={() =>
act('equip_act', {
@@ -396,7 +396,13 @@ const SnowflakeWeaponBallistic = (props) => {
const SnowflakeSleeper = (props) => {
const { act, data } = useBackend<MainData>();
const { ref } = props.module;
const { patient } = props.module.snowflake;
const {
patient,
contained_reagents,
injectible_reagents,
has_brain_damage,
has_traumas,
} = props.module.snowflake;
return !patient ? (
<LabeledList.Item label="Patient">None</LabeledList.Item>
) : (
@@ -416,33 +422,79 @@ const SnowflakeSleeper = (props) => {
/>
}
>
{patient.patientname}
{patient.patient_name}
</LabeledList.Item>
<LabeledList.Item label={'Health'}>
{patient.is_dead ? (
<Box color="red">Patient dead</Box>
) : (
<ProgressBar
ranges={{
good: [0.75, Infinity],
average: [0.25, 0.75],
bad: [-Infinity, 0.25],
}}
value={patient.patient_health}
/>
)}
</LabeledList.Item>
<LabeledList.Item label={'Detailed Vitals'}>
<Button
content={'View'}
onClick={() =>
act('equip_act', {
ref: ref,
gear_action: 'view_stats',
})
}
<LabeledList.Item label="Health">
<ProgressBar
ranges={{
good: [0.75, Infinity],
average: [0.25, 0.75],
bad: [-Infinity, 0.25],
}}
value={patient.patient_health}
/>
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="State">
{patient.patient_state}
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="Temperature">
{patient.core_temp} C
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="Brute Damage">
{patient.brute_loss}
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="Burn Severity">
{patient.burn_loss}
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="Toxin Content">
{patient.toxin_loss}
</LabeledList.Item>
<LabeledList.Item className="candystripe" label="Respiratory Damage">
{patient.oxygen_loss}
</LabeledList.Item>
{!!has_brain_damage && (
<LabeledList.Item className="candystripe" label="Detected">
Brain Damage
</LabeledList.Item>
)}
{!!has_traumas && (
<LabeledList.Item className="candystripe" label="Detected">
Traumatic Damage
</LabeledList.Item>
)}
<LabeledList.Item label="Reagent Details">
{contained_reagents.map((reagent) => (
<LabeledList.Item
key={reagent.name}
className="candystripe"
label={reagent.name}
>
<LabeledList.Item label={`${reagent.volume}u`} />
</LabeledList.Item>
))}
</LabeledList.Item>
<LabeledList.Item label="Reagent Injection">
{injectible_reagents.map((reagent) => (
<LabeledList.Item
className="candystripe"
key={reagent.name}
label={reagent.name}
>
<LabeledList.Item label={`${reagent.volume}u`}>
<Button
onClick={() =>
act('equip_act', {
ref: ref,
gear_action: `inject_reagent_${reagent.name}`,
})
}
>
Inject
</Button>
</LabeledList.Item>
</LabeledList.Item>
))}
</LabeledList.Item>
</>
);
};
@@ -473,17 +525,17 @@ const SnowflakeSyringe = (props) => {
} = props.module.snowflake;
return (
<>
<LabeledList.Item label={'Syringes'}>
<LabeledList.Item label="Syringes">
<ProgressBar value={syringe / max_syringe}>
{`${syringe} of ${max_syringe}`}
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label={'Reagents'}>
<LabeledList.Item label="Reagents">
<ProgressBar value={reagents / total_reagents}>
{`${reagents} of ${total_reagents} units`}
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label={'Mode'}>
<LabeledList.Item label="Mode">
<Button
content={mode}
onClick={() =>
@@ -644,8 +696,8 @@ const SnowflakeAirTank = (props) => {
label="Integrity"
buttons={
<Button
content={'Repair'}
icon={'wrench'}
content="Repair"
icon="wrench"
onClick={() =>
act('equip_act', {
ref: ref,
@@ -933,8 +985,8 @@ const SnowflakeExtinguisher = (props) => {
label="Water"
buttons={
<Button
content={'Refill'}
icon={'fill'}
content="Refill"
icon="fill"
onClick={() =>
act('equip_act', {
ref: ref,
@@ -950,10 +1002,10 @@ const SnowflakeExtinguisher = (props) => {
</LabeledList.Item>
<LabeledList.Item label="Extinguisher">
<Button
content={'Activate'}
color={'red'}
content="Activate"
color="red"
disabled={reagents < reagents_required}
icon={'fire-extinguisher'}
icon="fire-extinguisher"
onClick={() =>
act('equip_act', {
ref: ref,
@@ -1017,9 +1069,9 @@ const SnowflakeLawClaw = (props) => {
label="Handcuff Suspects"
buttons={
<Button
content={'Toggle'}
content="Toggle"
color={autocuff ? 'green' : 'blue'}
icon={'handcuffs'}
icon="handcuffs"
onClick={() =>
act('equip_act', {
ref: ref,