/tg/ui Virology PanD.E.M.I.C 2200 (PanDEMIC) (#24533)

* /tg/ui PanDEMIC

* Fix types on booleans

* [ci skip]

* Fix oversights

[ci skip]

* Fix styling

* Apply suggestions from code review

Co-authored-by: Aylong <69762909+AyIong@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Aylong <69762909+AyIong@users.noreply.github.com>

* Remove unused field

* Optimize ui_data

* Make clone cooldown spin

* Change unit

[ci skip]

* Remove use of !!

* Fix Flex style prop accidental override

* Rewrite Stack to Flex

* Revert changes to Stack

* [ci skip]

* Build and update tgui

* Move force update code

* Build and update tgui

* trigger ci

* Fix style

---------

Co-authored-by: Arthri <41360489+a@users.noreply.github.com>
Co-authored-by: Aylong <69762909+AyIong@users.noreply.github.com>
Co-authored-by: /tg/ui Builder <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
Arthri
2024-05-04 22:10:53 +00:00
committed by GitHub
co-authored by Aylong Arthri /tg/ui Builder <41898282+github-actions[bot]@users.noreply.github.com> Burzah
parent 38d16c4698
commit 4e036c77e3
9 changed files with 715 additions and 270 deletions
+5 -2
View File
@@ -187,7 +187,7 @@ SUBSYSTEM_DEF(tgui)
*
* return int The number of UIs updated.
*/
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object, full_update = FALSE)
var/count = 0
var/key = "[src_object.UID()]"
// No UIs opened for this src_object
@@ -196,7 +196,10 @@ SUBSYSTEM_DEF(tgui)
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(force = TRUE)
if(full_update)
ui.send_full_update(null, TRUE)
else
ui.process(force = TRUE)
count++
return count
@@ -11,6 +11,7 @@
var/temp_html = ""
var/printing = null
var/wait = null
var/selected_strain_index = 1
var/obj/item/reagent_containers/beaker = null
/obj/machinery/computer/pandemic/Initialize(mapload)
@@ -26,23 +27,33 @@
stat |= BROKEN
update_icon()
/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index)
/obj/machinery/computer/pandemic/proc/GetViruses()
if(beaker && beaker.reagents)
if(length(beaker.reagents.reagent_list))
var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
if(BL)
if(BL.data && BL.data["viruses"])
var/list/viruses = BL.data["viruses"]
return viruses[index]
return viruses
/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index)
/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index)
var/list/viruses = GetViruses()
if(viruses && index > 0 && index <= length(viruses))
return viruses[index]
/obj/machinery/computer/pandemic/proc/GetResistances()
if(beaker && beaker.reagents)
if(length(beaker.reagents.reagent_list))
var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
if(BL)
if(BL.data && BL.data["resistances"])
var/list/resistances = BL.data["resistances"]
return resistances[index]
return resistances
/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index)
var/list/resistances = GetResistances()
if(resistances && index > 0 && index <= length(resistances))
return resistances[index]
/obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(index)
var/datum/disease/D = GetVirusByIndex(index)
@@ -68,117 +79,233 @@
if(!wait)
. += "waitlight"
/obj/machinery/computer/pandemic/Topic(href, href_list)
/obj/machinery/computer/pandemic/proc/create_culture(name, bottle_type = "culture", cooldown = 50)
var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc)
B.icon_state = "round_bottle"
B.pixel_x = rand(-3, 3)
B.pixel_y = rand(-3, 3)
replicator_cooldown(cooldown)
B.name = "[name] [bottle_type] bottle"
return B
/obj/machinery/computer/pandemic/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
if(..())
return
usr.set_machine(src)
if(!beaker) return
if(href_list["create_vaccine"])
if(!wait)
var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc)
if(B)
B.pixel_x = rand(-3, 3)
B.pixel_y = rand(-3, 3)
var/path = GetResistancesByIndex(text2num(href_list["create_vaccine"]))
var/vaccine_type = path
var/vaccine_name = "Unknown"
if(!ispath(vaccine_type))
if(GLOB.archive_diseases[path])
var/datum/disease/D = GLOB.archive_diseases[path]
if(D)
vaccine_name = D.name
vaccine_type = path
else if(vaccine_type)
var/datum/disease/D = new vaccine_type(0, null)
if(D)
vaccine_name = D.name
if(vaccine_type)
B.name = "[vaccine_name] vaccine bottle"
B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
replicator_cooldown(200)
else
temp_html = "The replicator is not ready yet."
updateUsrDialog()
if(inoperable())
return
else if(href_list["create_virus_culture"])
if(!wait)
var/type = GetVirusTypeByIndex(text2num(href_list["create_virus_culture"]))//the path is received as string - converting
. = TRUE
switch(action)
if("clone_strain")
if(wait)
atom_say("The replicator is not ready yet.")
return
var/strain_index = text2num(params["strain_index"])
if(isnull(strain_index))
atom_say("Unable to respond to command.")
return
var/datum/disease/virus = GetVirusByIndex(strain_index)
var/datum/disease/D = null
if(!virus)
atom_say("Unable to find requested strain.")
return
var/type = virus.GetDiseaseID()
if(!ispath(type))
D = GetVirusByIndex(text2num(href_list["create_virus_culture"]))
var/datum/disease/advance/A = GLOB.archive_diseases[D.GetDiseaseID()]
var/datum/disease/advance/A = GLOB.archive_diseases[type]
if(A)
D = new A.type(0, A)
else if(type)
if(type in GLOB.diseases) // Make sure this is a disease
D = new type(0, null)
if(!D)
atom_say("Unable to synthesize requested strain.")
return
var/name = tgui_input_text(usr, "Name:", "Name the culture", D.name, MAX_NAME_LEN)
var/default_name = ""
if(D.name == "Unknown" || D.name == "")
default_name = replacetext(beaker.name, new/regex(" culture bottle\\Z", "g"), "")
else
default_name = D.name
var/name = tgui_input_text(usr, "Name:", "Name the culture", default_name, MAX_NAME_LEN)
if(name == null || wait)
return
var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc)
B.icon_state = "round_bottle"
B.pixel_x = rand(-3, 3)
B.pixel_y = rand(-3, 3)
replicator_cooldown(50)
var/list/data = list("viruses"=list(D))
B.name = "[name] culture bottle"
var/obj/item/reagent_containers/glass/bottle/B = create_culture(name)
B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium."
B.reagents.add_reagent("blood",20,data)
updateUsrDialog()
else
temp_html = "The replicator is not ready yet."
updateUsrDialog()
return
else if(href_list["empty_beaker"])
beaker.reagents.clear_reagents()
eject_beaker()
updateUsrDialog()
return
else if(href_list["eject"])
eject_beaker()
updateUsrDialog()
return
else if(href_list["clear"])
temp_html = ""
updateUsrDialog()
return
else if(href_list["name_disease"])
var/new_name = tgui_input_text(usr, "Name the Disease", "New Name", max_length = MAX_NAME_LEN)
if(!new_name)
return
if(..())
return
var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"]))
if(GLOB.archive_diseases[id])
var/datum/disease/advance/A = GLOB.archive_diseases[id]
B.reagents.add_reagent("blood", 20, list("viruses" = list(D)))
if("clone_vaccine")
if(wait)
atom_say("The replicator is not ready yet.")
return
var/resistance_index = text2num(params["resistance_index"])
if(isnull(resistance_index))
atom_say("Unable to find requested antibody.")
return
var/vaccine_type = GetResistancesByIndex(resistance_index)
var/vaccine_name = "Unknown"
if(!ispath(vaccine_type))
if(GLOB.archive_diseases[vaccine_type])
var/datum/disease/D = GLOB.archive_diseases[vaccine_type]
if(D)
vaccine_name = D.name
else if(vaccine_type)
var/datum/disease/D = new vaccine_type(0, null)
if(D)
vaccine_name = D.name
if(!vaccine_type)
atom_say("Unable to synthesize requested antibody.")
return
var/obj/item/reagent_containers/glass/bottle/B = create_culture(vaccine_name, "vaccine", 200)
B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
if("eject_beaker")
eject_beaker()
if("destroy_eject_beaker")
beaker.reagents.clear_reagents()
eject_beaker()
if("print_release_forms")
var/strain_index = text2num(params["strain_index"])
if(isnull(strain_index))
atom_say("Unable to respond to command.")
return
var/type = GetVirusTypeByIndex(strain_index)
if(!type)
atom_say("Unable to find requested strain.")
return
var/datum/disease/advance/A = GLOB.archive_diseases[type]
if(!A)
atom_say("Unable to find requested strain.")
return
print_form(A, usr)
if("name_strain")
var/strain_index = text2num(params["strain_index"])
if(isnull(strain_index))
atom_say("Unable to respond to command.")
return
var/type = GetVirusTypeByIndex(strain_index)
if(!type)
atom_say("Unable to find requested strain.")
return
var/datum/disease/advance/A = GLOB.archive_diseases[type]
if(!A)
atom_say("Unable to find requested strain.")
return
if(A.name != "Unknown")
atom_say("Request rejected. Strain already has a name.")
return
var/new_name = tgui_input_text(usr, "Name the Strain", "New Name", max_length = MAX_NAME_LEN)
if(!new_name)
return
A.AssignName(new_name)
for(var/datum/disease/advance/AD in GLOB.active_diseases)
AD.Refresh()
updateUsrDialog()
else if(href_list["print_form"])
var/datum/disease/D = GetVirusByIndex(text2num(href_list["print_form"]))
D = GLOB.archive_diseases[D.GetDiseaseID()]//We know it's advanced no need to check
print_form(D, usr)
if("switch_strain")
var/strain_index = text2num(params["strain_index"])
if(isnull(strain_index) || strain_index < 1)
atom_say("Unable to respond to command.")
return
var/list/viruses = GetViruses()
if(strain_index > length(viruses))
atom_say("Unable to find requested strain.")
return
selected_strain_index = strain_index;
else
return FALSE
/obj/machinery/computer/pandemic/ui_state(mob/user)
return GLOB.default_state
else
usr << browse(null, "window=pandemic")
updateUsrDialog()
return
/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PanDEMIC", name)
ui.open()
add_fingerprint(usr)
/obj/machinery/computer/pandemic/ui_data(mob/user)
var/datum/reagent/blood/Blood = null
if(beaker)
var/datum/reagents/R = beaker.reagents
for(var/datum/reagent/blood/B in R.reagent_list)
if(B)
Blood = B
break
var/list/data = list(
"synthesisCooldown" = wait ? TRUE : FALSE,
"beakerLoaded" = beaker ? TRUE : FALSE,
"beakerContainsBlood" = Blood ? TRUE : FALSE,
"beakerContainsVirus" = length(Blood?.data["viruses"]) != 0,
"selectedStrainIndex" = selected_strain_index,
)
return data
/obj/machinery/computer/pandemic/ui_static_data(mob/user)
var/list/data = list()
. = data
var/datum/reagent/blood/Blood = null
if(beaker)
var/datum/reagents/R = beaker.reagents
for(var/datum/reagent/blood/B in R.reagent_list)
if(B)
Blood = B
break
var/list/strains = list()
for(var/datum/disease/D in GetViruses())
if(D.visibility_flags & HIDDEN_PANDEMIC)
continue
var/list/symptoms = list()
if(istype(D, /datum/disease/advance))
var/datum/disease/advance/A = D
D = GLOB.archive_diseases[A.GetDiseaseID()]
if(!D)
CRASH("We weren't able to get the advance disease from the archive.")
for(var/datum/symptom/S in A.symptoms)
symptoms += list(list(
"name" = S.name,
"stealth" = S.stealth,
"resistance" = S.resistance,
"stageSpeed" = S.stage_speed,
"transmissibility" = S.transmittable,
"complexity" = S.level,
))
strains += list(list(
"commonName" = D.name,
"description" = D.desc,
"bloodDNA" = Blood.data["blood_DNA"],
"bloodType" = Blood.data["blood_type"],
"diseaseAgent" = D.agent,
"possibleTreatments" = D.cure_text,
"transmissionRoute" = D.spread_text,
"symptoms" = symptoms,
"isAdvanced" = istype(D, /datum/disease/advance),
))
data["strains"] = strains
var/list/resistances = list()
for(var/resistance in GetResistances())
if(!ispath(resistance))
var/datum/disease/D = GLOB.archive_diseases[resistance]
if(D)
resistances += list(D.name)
else if(resistance)
var/datum/disease/D = new resistance(0, null)
if(D)
resistances += list(D.name)
data["resistances"] = resistances
/obj/machinery/computer/pandemic/proc/eject_beaker()
beaker.forceMove(loc)
beaker = null
icon_state = "pandemic0"
selected_strain_index = 1
//Prints a nice virus release form. Props to Urbanliner for the layout
/obj/machinery/computer/pandemic/proc/print_form(datum/disease/advance/D, mob/living/user)
@@ -241,108 +368,16 @@
P.info = info_text.Join("")
P.update_icon()
/obj/machinery/computer/pandemic/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/computer/pandemic/attack_hand(mob/user)
if(..())
return
user.set_machine(src)
var/dat = ""
if(temp_html)
dat = "[temp_html]<BR><BR><A href='byond://?src=[UID()];clear=1'>Main Menu</A>"
else if(!beaker)
dat += "Please insert beaker.<BR>"
dat += "<A href='byond://?src=[user.UID()];mach_close=pandemic'>Close</A>"
else
var/datum/reagents/R = beaker.reagents
var/datum/reagent/blood/Blood = null
for(var/datum/reagent/blood/B in R.reagent_list)
if(B)
Blood = B
break
if(!R.total_volume||!length(R.reagent_list))
dat += "The beaker is empty<BR>"
else if(!Blood)
dat += "No blood sample found in beaker."
else if(!Blood.data)
dat += "No blood data found in beaker."
else
dat += "<h3>Blood sample data:</h3>"
dat += "<b>Blood DNA:</b> [(Blood.data["blood_DNA"]||"none")]<BR>"
dat += "<b>Blood Type:</b> [(Blood.data["blood_type"]||"none")]<BR>"
if(Blood.data["viruses"])
var/list/vir = Blood.data["viruses"]
if(length(vir))
var/i = 0
for(var/thing in Blood.data["viruses"])
var/datum/disease/D = thing
i++
if(!(D.visibility_flags & HIDDEN_PANDEMIC))
if(istype(D, /datum/disease/advance))
var/datum/disease/advance/A = D
D = GLOB.archive_diseases[A.GetDiseaseID()]
if(D)
if(D.name == "Unknown")
dat += "<b><a href='byond://?src=[UID()];name_disease=[i]'>Name Disease</a></b><BR>"
else
dat += "<b><a href='byond://?src=[UID()];print_form=[i]'>Print release form</a></b><BR>"
if(!D)
CRASH("We weren't able to get the advance disease from the archive.")
dat += "<b>Disease Agent:</b> [D?"[D.agent] - <A href='byond://?src=[UID()];create_virus_culture=[i]'>Create virus culture bottle</A>":"none"]<BR>"
dat += "<b>Common name:</b> [(D.name||"none")]<BR>"
dat += "<b>Description: </b> [(D.desc||"none")]<BR>"
dat += "<b>Spread:</b> [(D.spread_text||"none")]<BR>"
dat += "<b>Possible cure:</b> [(D.cure_text||"none")]<BR><BR>"
if(istype(D, /datum/disease/advance))
var/datum/disease/advance/A = D
dat += "<b>Symptoms:</b> "
var/english_symptoms = list()
for(var/datum/symptom/S in A.symptoms)
english_symptoms += S.name
dat += english_list(english_symptoms)
else
dat += "No detectable virus in the sample."
else
dat += "No detectable virus in the sample."
dat += "<BR><b>Contains antibodies to:</b> "
if(Blood.data["resistances"])
var/list/res = Blood.data["resistances"]
if(length(res))
dat += "<ul>"
var/i = 0
for(var/type in Blood.data["resistances"])
i++
var/disease_name = "Unknown"
if(!ispath(type))
var/datum/disease/advance/A = GLOB.archive_diseases[type]
if(A)
disease_name = A.name
else
var/datum/disease/D = new type(0, null)
disease_name = D.name
dat += "<li>[disease_name] - <A href='byond://?src=[UID()];create_vaccine=[i]'>Create vaccine bottle</A></li>"
dat += "</ul><BR>"
else
dat += "nothing<BR>"
else
dat += "nothing<BR>"
dat += "<BR><A href='byond://?src=[UID()];eject=1'>Eject beaker</A>[((R.total_volume&&length(R.reagent_list)) ? "-- <A href='byond://?src=[UID()];empty_beaker=1'>Empty and eject beaker</A>":"")]<BR>"
dat += "<A href='byond://?src=[user.UID()];mach_close=pandemic'>Close</A>"
var/datum/browser/popup = new(user, "pandemic", name, 575, 400)
popup.set_content(dat)
popup.open(0)
onclose(user, "pandemic")
ui_interact(user)
/obj/machinery/computer/pandemic/attack_ghost(mob/user)
ui_interact(user)
/obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params)
if(default_unfasten_wrench(user, I, time = 4 SECONDS))
@@ -361,6 +396,7 @@
beaker.loc = src
to_chat(user, "<span class='notice'>You add the beaker to the machine.</span>")
updateUsrDialog()
SStgui.update_uis(src, TRUE)
icon_state = "pandemic1"
else
return ..()
+3 -2
View File
@@ -24,10 +24,11 @@ export const computeFlexClassName = (props: FlexProps) => {
};
export const computeFlexProps = (props: FlexProps) => {
const { className, direction, wrap, align, justify, inline, ...rest } = props;
const { className, direction, wrap, align, justify, inline, style, ...rest } =
props;
return computeBoxProps({
style: {
...rest.style,
...style,
'flex-direction': direction,
'flex-wrap': wrap === true ? 'wrap' : wrap,
'align-items': align,
+374
View File
@@ -0,0 +1,374 @@
import { BooleanLike } from '../../common/react';
import { InfernoNode } from 'inferno';
import { useBackend } from '../backend';
import {
Button,
Flex,
LabeledList,
Section,
Stack,
Table,
Tabs,
} from '../components';
import { Window } from '../layouts';
interface PathogenSymptom {
name: string;
stealth: number;
resistance: number;
stageSpeed: number;
transmissibility: number;
}
interface PathogenStrain {
commonName?: string;
description?: string;
bloodDNA?: string;
bloodType?: string;
diseaseAgent: string;
possibleTreatments?: string;
transmissionRoute?: string;
symptoms?: PathogenSymptom[];
isAdvanced: BooleanLike;
}
interface PanDEMICData {
synthesisCooldown: BooleanLike;
beakerLoaded: BooleanLike;
beakerContainsBlood: BooleanLike;
beakerContainsVirus: BooleanLike;
selectedStrainIndex: number;
strains?: PathogenStrain[];
resistances?: string[];
}
export const PanDEMIC = (props, context) => {
const { data } = useBackend<PanDEMICData>(context);
const {
beakerLoaded,
beakerContainsBlood,
beakerContainsVirus,
resistances = [],
} = data;
let emptyPlaceholder;
if (!beakerLoaded) {
emptyPlaceholder = <>No container loaded.</>;
} else if (!beakerContainsBlood) {
emptyPlaceholder = <>No blood sample found in the loaded container.</>;
}
return (
<Window width={575} height={510}>
<Window.Content>
<Stack fill vertical>
{emptyPlaceholder && (
<Section
title="Container Information"
buttons={<CommonCultureActions />}
>
{emptyPlaceholder}
</Section>
)}
{!!beakerContainsVirus && <CultureInformationSection />}
{resistances?.length > 0 && <ResistancesSection />}
</Stack>
</Window.Content>
</Window>
);
};
const CommonCultureActions = (props, context) => {
const { act, data } = useBackend<PanDEMICData>(context);
const { beakerLoaded } = data;
return (
<>
<Button
icon="eject"
content="Eject"
disabled={!beakerLoaded}
onClick={() => act('eject_beaker')}
/>
<Button.Confirm
icon="trash-alt"
confirmIcon="eraser"
content="Destroy"
confirmContent="Destroy"
disabled={!beakerLoaded}
onClick={() => act('destroy_eject_beaker')}
/>
</>
);
};
const StrainInformation = (
props: { strain: PathogenStrain; strainIndex: number },
context
) => {
const { act, data } = useBackend<PanDEMICData>(context);
const { beakerContainsVirus } = data;
const {
commonName,
description,
diseaseAgent,
bloodDNA,
bloodType,
possibleTreatments,
transmissionRoute,
isAdvanced,
} = props.strain;
const bloodInformation = (
<>
<LabeledList.Item label="Blood DNA">
{!bloodDNA ? (
'Undetectable'
) : (
<span style={{ 'font-family': "'Courier New', monospace" }}>
{bloodDNA}
</span>
)}
</LabeledList.Item>
<LabeledList.Item label="Blood Type">
{
<div
// blood type can sometimes contain a span
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: bloodType ?? 'Undetectable' }}
/>
}
</LabeledList.Item>
</>
);
if (!beakerContainsVirus) {
return <LabeledList>{bloodInformation}</LabeledList>;
}
let nameButtons;
if (isAdvanced) {
if (
commonName !== undefined &&
commonName !== null &&
commonName !== 'Unknown'
) {
nameButtons = (
<Button
icon="print"
content="Print Release Forms"
onClick={() =>
act('print_release_forms', {
strain_index: props.strainIndex,
})
}
style={{ 'margin-left': 'auto' }}
/>
);
} else {
nameButtons = (
<Button
icon="pen"
content="Name Disease"
onClick={() =>
act('name_strain', { strain_index: props.strainIndex })
}
style={{ 'margin-left': 'auto' }}
/>
);
}
}
return (
<LabeledList>
<LabeledList.Item label="Common Name" className="common-name-label">
<Stack horizontal align="center">
{commonName ?? 'Unknown'}
{nameButtons}
</Stack>
</LabeledList.Item>
{description && (
<LabeledList.Item label="Description">{description}</LabeledList.Item>
)}
<LabeledList.Item label="Disease Agent">{diseaseAgent}</LabeledList.Item>
{bloodInformation}
<LabeledList.Item label="Spread Vector">
{transmissionRoute ?? 'None'}
</LabeledList.Item>
<LabeledList.Item label="Possible Cures">
{possibleTreatments ?? 'None'}
</LabeledList.Item>
</LabeledList>
);
};
const StrainInformationSection = (
props: {
strain: PathogenStrain;
strainIndex: number;
sectionTitle?: string;
sectionButtons?: InfernoNode | InfernoNode[];
},
context
) => {
const { act, data } = useBackend<PanDEMICData>(context);
let synthesisCooldown = !!data.synthesisCooldown;
const appliedSectionButtons = (
<>
<Button
icon={synthesisCooldown ? 'spinner' : 'clone'}
iconSpin={synthesisCooldown}
content="Clone"
disabled={synthesisCooldown}
onClick={() => act('clone_strain', { strain_index: props.strainIndex })}
/>
{props.sectionButtons}
</>
);
return (
<Flex.Item>
<Section
title={props.sectionTitle ?? 'Strain Information'}
buttons={appliedSectionButtons}
>
<StrainInformation
strain={props.strain}
strainIndex={props.strainIndex}
/>
</Section>
</Flex.Item>
);
};
const CultureInformationSection = (props, context) => {
const { act, data } = useBackend<PanDEMICData>(context);
const { selectedStrainIndex, strains } = data;
const selectedStrain = strains[selectedStrainIndex - 1];
if (strains.length === 1) {
return (
<>
<StrainInformationSection
strain={strains[0]}
strainIndex={1}
sectionButtons={<CommonCultureActions />}
/>
{strains[0].symptoms?.length > 0 && (
<StrainSymptomsSection strain={strains[0]} />
)}
</>
);
}
const sectionButtons = <CommonCultureActions />;
return (
<Stack.Item grow>
<Section title="Culture Information" fill buttons={sectionButtons}>
<Flex direction="column" style={{ 'height': '100%' }}>
<Flex.Item>
<Tabs>
{strains.map((strain, i) => (
<Tabs.Tab
key={i}
icon="virus"
selected={selectedStrainIndex - 1 === i}
onClick={() => act('switch_strain', { strain_index: i + 1 })}
>
{strain.commonName ?? 'Unknown'}
</Tabs.Tab>
))}
</Tabs>
</Flex.Item>
<StrainInformationSection
strain={selectedStrain}
strainIndex={selectedStrainIndex}
/>
{selectedStrain.symptoms?.length > 0 && (
<StrainSymptomsSection
className="remove-section-bottom-padding"
strain={selectedStrain}
/>
)}
</Flex>
</Section>
</Stack.Item>
);
};
const sum = (values: number[]) => {
return values.reduce((r, value) => r + value, 0);
};
const StrainSymptomsSection = (props: {
className?: string;
strain: PathogenStrain;
}) => {
const { symptoms } = props.strain;
return (
<Flex.Item grow>
<Section title="Infection Symptoms" fill className={props.className}>
<Table className="symptoms-table">
<Table.Row>
<Table.Cell>Name</Table.Cell>
<Table.Cell>Stealth</Table.Cell>
<Table.Cell>Resistance</Table.Cell>
<Table.Cell>Stage Speed</Table.Cell>
<Table.Cell>Transmissibility</Table.Cell>
</Table.Row>
{symptoms.map((symptom, index) => (
<Table.Row key={index}>
<Table.Cell>{symptom.name}</Table.Cell>
<Table.Cell>{symptom.stealth}</Table.Cell>
<Table.Cell>{symptom.resistance}</Table.Cell>
<Table.Cell>{symptom.stageSpeed}</Table.Cell>
<Table.Cell>{symptom.transmissibility}</Table.Cell>
</Table.Row>
))}
<Table.Row className="table-spacer" />
<Table.Row>
<Table.Cell style={{ 'font-weight': 'bold' }}>Total</Table.Cell>
<Table.Cell>{sum(symptoms.map((s) => s.stealth))}</Table.Cell>
<Table.Cell>{sum(symptoms.map((s) => s.resistance))}</Table.Cell>
<Table.Cell>{sum(symptoms.map((s) => s.stageSpeed))}</Table.Cell>
<Table.Cell>
{sum(symptoms.map((s) => s.transmissibility))}
</Table.Cell>
</Table.Row>
</Table>
</Section>
</Flex.Item>
);
};
const VaccineSynthesisIcons = ['flask', 'vial', 'eye-dropper'];
const ResistancesSection = (props, context) => {
const { act, data } = useBackend<PanDEMICData>(context);
const { synthesisCooldown, beakerContainsVirus, resistances } = data;
return (
<Stack.Item>
<Section
title="Antibodies"
fill
buttons={!beakerContainsVirus && <CommonCultureActions />}
>
<Stack horizontal wrap>
{resistances.map((r, i) => (
<Stack.Item key={i}>
<Button
icon={VaccineSynthesisIcons[i % VaccineSynthesisIcons.length]}
disabled={!!synthesisCooldown}
onClick={() =>
act('clone_vaccine', { resistance_index: i + 1 })
}
mr="0.5em"
/>
{r}
</Stack.Item>
))}
</Stack>
</Section>
</Stack.Item>
);
};
@@ -0,0 +1,30 @@
.symptoms-table {
border-collapse: separate;
border-spacing: 0 0.5ex;
height: 100%;
}
.symptoms-table > tbody > tr:first-child {
width: 100%;
font-weight: bold;
}
.symptoms-table > tbody > tr:nth-child(2) > td:first-child {
padding-top: 0.5ex;
}
.symptoms-table > tbody > tr > td:nth-child(n + 2) {
text-align: center;
}
.common-name-label > .LabeledList__cell {
vertical-align: middle;
}
.table-spacer {
height: 100%;
}
.remove-section-bottom-padding .Section__content {
padding-bottom: 0;
}
+1
View File
@@ -57,6 +57,7 @@
@include meta.load-css('./interfaces/Newscaster.scss');
@include meta.load-css('./interfaces/NuclearBomb.scss');
@include meta.load-css('./interfaces/OreRedemption.scss');
@include meta.load-css('./interfaces/PanDEMIC.scss');
@include meta.load-css('./interfaces/PDA.scss');
@include meta.load-css('./interfaces/PdaPainter.scss');
@include meta.load-css('./interfaces/PoolController.scss');
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long