diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index a109e2b8ec9..a909772a27f 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -22,15 +22,15 @@ ///returns nothing with an alert instead of the message if it contains something in the ic filter, and sanitizes normally if the name is fine. It returns nothing so it backs out of the input the same way as if you had entered nothing. -/proc/sanitize_name(t,allow_numbers=FALSE) - if(is_ic_filtered(t) || is_soft_ic_filtered(t)) +/proc/sanitize_name(target, allow_numbers = FALSE, cap_after_symbols = TRUE) + if(is_ic_filtered(target) || is_soft_ic_filtered(target)) tgui_alert(usr, "You cannot set a name that contains a word prohibited in IC chat!") return "" - var/r = reject_bad_name(t,allow_numbers=allow_numbers,strict=TRUE) - if(!r) + var/result = reject_bad_name(target, allow_numbers = allow_numbers, strict = TRUE, cap_after_symbols = cap_after_symbols) + if(!result) tgui_alert(usr, "Invalid name.") return "" - return sanitize(r) + return sanitize(result) /// Runs byond's html encoding sanitization proc, after replacing new-lines and tabs for the # character. @@ -150,9 +150,9 @@ * * * strict - return null immidiately instead of filtering out * * allow_numbers - allows numbers and common special characters - used for silicon/other weird things names + * * cap_after_symbols - words like Bob's will be capitalized to Bob'S by default. False is good for titles. */ -///proc/reject_bad_name(t_in, allow_numbers = FALSE, max_length = MAX_NAME_LEN, ascii_only = TRUE, strict = FALSE) //ORIGINAL -/proc/reject_bad_name(t_in, allow_numbers = TRUE, max_length = MAX_NAME_LEN, ascii_only = TRUE, strict = FALSE) //SKYRAT EDIT CHANGE +/proc/reject_bad_name(t_in, allow_numbers = TRUE, max_length = MAX_NAME_LEN, ascii_only = TRUE, strict = FALSE, cap_after_symbols = TRUE) // SKYRAT EDIT CHANGE if(!t_in) return //Rejects the input if it is null @@ -179,7 +179,7 @@ // a .. z if(97 to 122) //Lowercase Letters - if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || last_char_group == SYMBOLS_DETECTED) //start of a word + if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || cap_after_symbols && last_char_group == SYMBOLS_DETECTED) //start of a word char = uppertext(char) number_of_alphanumeric++ last_char_group = LETTERS_DETECTED @@ -192,7 +192,6 @@ continue number_of_alphanumeric++ last_char_group = NUMBERS_DETECTED - // ' - . if(39,45,46) //Common name punctuation if(last_char_group == NO_CHARS_DETECTED) diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 8a7d3454138..e8b2ba68c72 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -86,3 +86,22 @@ ///Overload for running after processing. /datum/symptom/proc/OnRemove(datum/disease/advance/A) return + +/** + * Returns a list for all of the traits of this symptom. + * + * + * @returns {list} symptom - The desired symptoms as a list. + */ +/datum/symptom/proc/get_symptom_data() + var/list/data = list() + data["name"] = name + data["desc"] = desc + data["stealth"] = stealth + data["resistance"] = resistance + data["stage_speed"] = stage_speed + data["transmission"] = transmittable + data["level"] = level + data["neutered"] = neutered + data["threshold_desc"] = threshold_descs + return data diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index c04b7907b64..0470486de21 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -1,5 +1,3 @@ -#define MAIN_SCREEN 1 -#define SYMPTOM_DETAILS 2 /obj/machinery/computer/pandemic name = "PanD.E.M.I.C 2200" @@ -12,8 +10,11 @@ resistance_flags = ACID_PROOF circuit = /obj/item/circuitboard/computer/pandemic + /// Whether the pandemic is ready to make another culture/vaccine var/wait + /// The currently selected symptom var/datum/symptom/selected_symptom + /// The inserted beaker var/obj/item/reagent_containers/beaker /obj/machinery/computer/pandemic/Initialize(mapload) @@ -56,85 +57,25 @@ update_appearance() return ..() -/obj/machinery/computer/pandemic/proc/get_by_index(thing, index) - if(!beaker || !beaker.reagents) - return - var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list - if(B?.data[thing]) - return B.data[thing][index] - -/obj/machinery/computer/pandemic/proc/get_virus_id_by_index(index) - var/datum/disease/D = get_by_index("viruses", index) - if(D) - return D.GetDiseaseID() - -/obj/machinery/computer/pandemic/proc/get_viruses_data(datum/reagent/blood/B) - . = list() - var/list/V = B.get_diseases() - var/index = 1 - for(var/virus in V) - var/datum/disease/D = virus - if(!istype(D) || D.visibility_flags & HIDDEN_PANDEMIC) - continue - - var/list/this = list() - this["name"] = D.name - if(istype(D, /datum/disease/advance)) - var/datum/disease/advance/A = D - var/disease_name = SSdisease.get_disease_name(A.GetDiseaseID()) - this["can_rename"] = ((disease_name == "Unknown") && A.mutable) - this["name"] = disease_name - this["is_adv"] = TRUE - this["symptoms"] = list() - for(var/symptom in A.symptoms) - var/datum/symptom/S = symptom - var/list/this_symptom = list() - this_symptom = get_symptom_data(S) - this["symptoms"] += list(this_symptom) - this["resistance"] = A.totalResistance() - this["stealth"] = A.totalStealth() - this["stage_speed"] = A.totalStageSpeed() - this["transmission"] = A.totalTransmittable() - this["index"] = index++ - this["agent"] = D.agent - this["description"] = D.desc || "none" - this["spread"] = D.spread_text || "none" - this["cure"] = D.cure_text || "none" - - . += list(this) - -/obj/machinery/computer/pandemic/proc/get_symptom_data(datum/symptom/S) - . = list() - var/list/this = list() - this["name"] = S.name - this["desc"] = S.desc - this["stealth"] = S.stealth - this["resistance"] = S.resistance - this["stage_speed"] = S.stage_speed - this["transmission"] = S.transmittable - this["level"] = S.level - this["neutered"] = S.neutered - this["threshold_desc"] = S.threshold_descs - . += this - -/obj/machinery/computer/pandemic/proc/get_resistance_data(datum/reagent/blood/B) - . = list() - if(!islist(B.data["resistances"])) - return - var/list/resistances = B.data["resistances"] - for(var/id in resistances) - var/list/this = list() - var/datum/disease/D = SSdisease.archive_diseases[id] - if(D) - this["id"] = id - this["name"] = D.name - - . += list(this) - -/obj/machinery/computer/pandemic/proc/reset_replicator_cooldown() - wait = FALSE +/obj/machinery/computer/pandemic/attackby(obj/item/held_item, mob/user, params) + if(!istype(held_item, /obj/item/reagent_containers) || held_item.item_flags & ABSTRACT || !held_item.is_open_container()) + return ..() + . = TRUE //no afterattack + if(machine_stat & (NOPOWER|BROKEN)) + return ..() + if(beaker) + balloon_alert(user, "pandemic full") + return ..() + if(!user.transferItemToLoc(held_item, src)) + return ..() + beaker = held_item + balloon_alert(user, "beaker loaded") update_appearance() - playsound(src, 'sound/machines/ping.ogg', 30, TRUE) + SStgui.update_uis(src) + +/obj/machinery/computer/pandemic/on_deconstruction() + eject_beaker() + . = ..() /obj/machinery/computer/pandemic/update_icon_state() icon_state = "[base_icon_state][beaker ? 1 : 0][(machine_stat & BROKEN) ? "_b" : (powered() ? null : "_nopower")]" @@ -145,42 +86,36 @@ if(wait) . += "waitlight" -/obj/machinery/computer/pandemic/proc/eject_beaker() - if(beaker) - try_put_in_hand(beaker, usr) - beaker = null - update_appearance() - /obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui) . = ..() ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Pandemic", name) ui.open() + ui.set_autoupdate(FALSE) /obj/machinery/computer/pandemic/ui_data(mob/user) var/list/data = list() data["is_ready"] = !wait - if(beaker) - data["has_beaker"] = TRUE - data["beaker"] = list( - "volume" = round(beaker.reagents?.total_volume, 0.01) || 0, - "capacity" = beaker.volume, - ) - var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list - if(B) - data["has_blood"] = TRUE - data["blood"] = list() - data["blood"]["dna"] = B.data["blood_DNA"] || "none" - data["blood"]["type"] = B.data["blood_type"] || "none" - data["viruses"] = get_viruses_data(B) - data["resistances"] = get_resistance_data(B) - else - data["has_blood"] = FALSE - else + if(!beaker) data["has_beaker"] = FALSE data["has_blood"] = FALSE - + return data + data["has_beaker"] = TRUE + data["beaker"] = list( + "volume" = round(beaker.reagents?.total_volume, 0.01) || 0, + "capacity" = beaker.volume, + ) + var/datum/reagent/blood/blood = locate() in beaker.reagents.reagent_list + if(!blood) + data["has_blood"] = FALSE + return data + data["has_blood"] = TRUE + data["blood"] = list() + data["blood"]["dna"] = blood.data["blood_DNA"] || "none" + data["blood"]["type"] = blood.data["blood_type"] || "none" + data["viruses"] = get_viruses_data(blood) + data["resistances"] = get_resistance_data(blood) return data /obj/machinery/computer/pandemic/ui_act(action, params) @@ -190,80 +125,218 @@ switch(action) if("eject_beaker") eject_beaker() - . = TRUE + return TRUE if("empty_beaker") if(beaker) beaker.reagents.clear_reagents() - . = TRUE + return TRUE if("empty_eject_beaker") if(beaker) beaker.reagents.clear_reagents() eject_beaker() - . = TRUE + return TRUE if("rename_disease") - var/id = get_virus_id_by_index(text2num(params["index"])) - var/datum/disease/advance/A = SSdisease.archive_diseases[id] - if(!A.mutable) - return - if(A) - var/new_name = sanitize_name(html_encode(params["name"]), allow_numbers = TRUE) - if(!new_name || ..()) - return - A.AssignName(new_name) - . = TRUE + rename_disease(params["index"], params["name"]) + return TRUE if("create_culture_bottle") if (wait) - return - var/id = get_virus_id_by_index(text2num(params["index"])) - var/datum/disease/advance/A = SSdisease.archive_diseases[id] - if(!istype(A) || !A.mutable) - to_chat(usr, span_warning("ERROR: Cannot replicate virus strain.")) - return - use_power(active_power_usage) - A = A.Copy() - var/list/data = list("viruses" = list(A)) - var/obj/item/reagent_containers/glass/bottle/B = new(drop_location()) - B.name = "[A.name] culture bottle" - B.desc = "A small bottle. Contains [A.agent] culture in synthblood medium." - B.reagents.add_reagent(/datum/reagent/blood, 20, data) - wait = TRUE - update_appearance() - var/turf/source_turf = get_turf(src) - log_virus("A culture bottle was printed for the virus [A.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]") - addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50) - . = TRUE + return FALSE + create_culture_bottle(params["index"]) + return TRUE if("create_vaccine_bottle") if (wait) - return - use_power(active_power_usage) - var/id = params["index"] - var/datum/disease/D = SSdisease.archive_diseases[id] - var/obj/item/reagent_containers/glass/bottle/B = new(drop_location()) - B.name = "[D.name] vaccine bottle" - B.reagents.add_reagent(/datum/reagent/vaccine, 15, list(id)) - wait = TRUE - update_appearance() - addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 200) - . = TRUE + return FALSE + create_vaccine_bottle(params["index"]) + return TRUE + return FALSE +/** + * Creates a culture bottle (ie: replicates) of the the specified disease. + * + * @param {number} index - The index of the disease to replicate. + * + * @returns {boolean} - Success or failure. + */ +/obj/machinery/computer/pandemic/proc/create_culture_bottle(index) + var/id = get_virus_id_by_index(text2num(index)) + var/datum/disease/advance/adv_disease = SSdisease.archive_diseases[id] + if(!istype(adv_disease) || !adv_disease.mutable) + to_chat(usr, span_warning("ERROR: Cannot replicate virus strain.")) + return FALSE + use_power(active_power_usage) + adv_disease = adv_disease.Copy() + var/list/data = list("viruses" = list(adv_disease)) + var/obj/item/reagent_containers/glass/bottle/bottle = new(drop_location()) + bottle.name = "[adv_disease.name] culture bottle" + bottle.desc = "A small bottle. Contains [adv_disease.agent] culture in synthblood medium." + bottle.reagents.add_reagent(/datum/reagent/blood, 20, data) + wait = TRUE + update_appearance() + var/turf/source_turf = get_turf(src) + log_virus("A culture bottle was printed for the virus [adv_disease.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]") + addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 5 SECONDS) + return TRUE -/obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container()) - . = TRUE //no afterattack - if(machine_stat & (NOPOWER|BROKEN)) - return - if(beaker) - to_chat(user, span_warning("A container is already loaded into [src]!")) - return - if(!user.transferItemToLoc(I, src)) - return +/** + * Creates a vaccine bottle for the specified disease. + * + * @param {number} index - The index of the disease to replicate. + * + * @returns {boolean} - Success or failure. + */ +/obj/machinery/computer/pandemic/proc/create_vaccine_bottle(index) + use_power(active_power_usage) + var/id = index + var/datum/disease/disease = SSdisease.archive_diseases[id] + var/obj/item/reagent_containers/glass/bottle/bottle = new(drop_location()) + bottle.name = "[disease.name] vaccine bottle" + bottle.reagents.add_reagent(/datum/reagent/vaccine, 15, list(id)) + wait = TRUE + update_appearance() + addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 20 SECONDS) + return TRUE - beaker = I - to_chat(user, span_notice("You insert [I] into [src].")) - update_appearance() - else - return ..() +/** + * Supporting proc to eject a beaker from the machine. + * + * Places it in hand if possible. + * + * @returns {boolean} - Success or failure. + */ +/obj/machinery/computer/pandemic/proc/eject_beaker() + if(!beaker) + return FALSE + try_put_in_hand(beaker, usr) + beaker = null + update_appearance() + return TRUE -/obj/machinery/computer/pandemic/on_deconstruction() - eject_beaker() - . = ..() +/** + * Displays a thing if it exists within the contents of a beaker. + * + * @param {any} thing - The key to look for. + * + * @param {any} index - Nested objects within the thing. + * + * @returns {any | boolean} The thing found or FALSE if unsuccessful. + */ +/obj/machinery/computer/pandemic/proc/get_by_index(thing, index) + if(!beaker || !beaker.reagents) + return FALSE + var/datum/reagent/blood/blood = locate() in beaker.reagents.reagent_list + if(blood?.data[thing]) + return blood.data[thing][index] + return FALSE + +/** + * Gets resistances of a given blood sample as a list + * + * @param {reagent/blood} blood - The sample. + * + * @returns {list} - The resistances. + */ +/obj/machinery/computer/pandemic/proc/get_resistance_data(datum/reagent/blood/blood) + var/list/data = list() + if(!islist(blood.data["resistances"])) + return data + var/list/resistances = blood.data["resistances"] + for(var/id in resistances) + var/list/resistance = list() + var/datum/disease/disease = SSdisease.archive_diseases[id] + if(disease) + resistance["id"] = id + resistance["name"] = disease.name + data += list(resistance) + return data + +/** + * A very hefty proc that I am not proud to see. + * + * Given a blood sample, this proc will return a list of viruses that are present in the sample. + * + * Contains traits, symptoms, thresholds etc. + * + * @param {reagent/blood} blood - The sample to analyze. + * + * @returns {list} - A list of virus info present in the sample. + */ +/obj/machinery/computer/pandemic/proc/get_viruses_data(datum/reagent/blood/blood) + var/list/data = list() + var/list/viruses = blood.get_diseases() + var/index = 1 + for(var/datum/disease/disease as anything in viruses) + if(!istype(disease) || disease.visibility_flags & HIDDEN_PANDEMIC) + continue + var/list/traits = list() + traits["agent"] = disease.agent + traits["cure"] = disease.cure_text || "none" + traits["description"] = disease.desc || "none" + traits["index"] = index++ + traits["name"] = disease.name + traits["spread"] = disease.spread_text || "none" + if(!istype(disease, /datum/disease/advance)) // Advanced diseases get more info + continue + var/datum/disease/advance/adv_disease = disease + var/disease_name = SSdisease.get_disease_name(adv_disease.GetDiseaseID()) + traits["can_rename"] = ((disease_name == "Unknown") && adv_disease.mutable) + traits["is_adv"] = TRUE + traits["name"] = disease_name + traits["resistance"] = adv_disease.totalResistance() + traits["stage_speed"] = adv_disease.totalStageSpeed() + traits["stealth"] = adv_disease.totalStealth() + traits["symptoms"] = list() + for(var/datum/symptom/symptom as anything in adv_disease.symptoms) + var/list/this_symptom = list() + this_symptom = symptom.get_symptom_data() + traits["symptoms"] += list(this_symptom) + traits["transmission"] = adv_disease.totalTransmittable() + data += list(traits) + return data + +/** + * Gets the ID of the virus by its index in the list of viruses. + * + * @param {number} index - The index of the virus in the list of viruses. + * + * @returns {string | boolean} - The ID of the virus or FALSE if unable + * to find the virus. + */ +/obj/machinery/computer/pandemic/proc/get_virus_id_by_index(index) + var/datum/disease/disease = get_by_index("viruses", index) + if(!disease) + return FALSE + return disease.GetDiseaseID() + +/** + * Renames an advanced disease after running it through sanitize_name(). + * + * @param {string} id - The ID of the disease to rename. + * + * @param {string} name - The new name of the disease. + * + * @returns {boolean} - Success or failure. + */ +/obj/machinery/computer/pandemic/proc/rename_disease(index, name) + var/id = get_virus_id_by_index(text2num(index)) + var/datum/disease/advance/adv_disease = SSdisease.archive_diseases[id] + if(!adv_disease.mutable) + return FALSE + if(adv_disease) + var/new_name = sanitize_name(name, allow_numbers = TRUE, cap_after_symbols = FALSE) + if(!new_name) + return FALSE + adv_disease.AssignName(new_name) + return TRUE + return FALSE + +/** + * Allows a user to create another vaccine/culture bottle again. + * + * @returns {boolean} - Success or failure. + */ +/obj/machinery/computer/pandemic/proc/reset_replicator_cooldown() + wait = FALSE + SStgui.update_uis(src) + update_appearance() + playsound(src, 'sound/machines/ping.ogg', 30, TRUE) + return TRUE diff --git a/tgui/packages/tgui/interfaces/Pandemic.tsx b/tgui/packages/tgui/interfaces/Pandemic.tsx deleted file mode 100644 index c85e40676bf..00000000000 --- a/tgui/packages/tgui/interfaces/Pandemic.tsx +++ /dev/null @@ -1,538 +0,0 @@ -import { capitalizeFirst } from 'common/string'; -import { BooleanLike } from 'common/react'; -import { useBackend, useLocalState } from 'tgui/backend'; -import { Box, Button, Collapsible, Input, LabeledList, NoticeBox, ProgressBar, Section, Stack, Tabs, Tooltip } from 'tgui/components'; -import { Window } from 'tgui/layouts'; - -type PandemicContext = { - beaker?: Beaker; - blood?: Blood; - has_beaker: BooleanLike; - has_blood: BooleanLike; - is_ready: BooleanLike; - resistances?: Resistance[]; - viruses?: Virus[]; -}; - -type Beaker = { - volume: number; - capacity: number; -}; - -type Blood = { - dna: string; - type: string; -}; - -type Resistance = { - id: string; - name: string; -}; - -type Virus = { - name: string; - can_rename: BooleanLike; - is_adv: BooleanLike; - symptoms?: Symptom[]; - resistance: number; - stealth: number; - stage_speed: number; - transmission: number; - index: number; - agent: string; - description: string; - spread: string; - cure: string; -}; - -type VirusDisplayProps = { - virus: Virus; -}; - -type VirusInfoProps = { - virus: Virus; -}; - -type TabsProps = { - tab: number; - tabHandler: (tab: number) => void; -}; - -type Symptom = { - name: string; - desc: string; - stealth: number; - resistance: number; - stage_speed: number; - transmission: number; - level: number; - neutered: BooleanLike; - threshold_desc: Threshold[]; -}; - -type SymptomDisplayProps = { - symptoms: Symptom[]; -}; - -type SymptomInfoProps = { - symptom: Symptom; -}; - -type Threshold = { - label: string; - descr: string; -}; - -type ThresholdDisplayProps = { - thresholds: Threshold[]; -}; - -export const Pandemic = (props, context) => { - const { data } = useBackend(context); - const { has_beaker, has_blood } = data; - - return ( - - - - - - - {!!has_beaker && !!has_blood && ( - - - - )} - - - - ); -}; - -/** Displays loaded container info, if it exists */ -const BeakerDisplay = (props, context) => { - const { act, data } = useBackend(context); - const { has_beaker, beaker, has_blood } = data; - const cant_empty = !has_beaker || !beaker?.volume; - let content; - if (!has_beaker) { - content = No beaker loaded.; - } else if (!beaker?.volume) { - content = Beaker is empty.; - } else if (!has_blood) { - content = No blood sample loaded.; - } else { - content = ( - - - - - - - - - ); - } - - return ( -
-
- ); -}; - -/** Displays info about the blood type, beaker capacity - volume */ -const BeakerInfoDisplay = (props, context) => { - const { data } = useBackend(context); - const { beaker, blood } = data; - if (!beaker || !blood) { - return No beaker loaded; - } - - return ( - - - - - {capitalizeFirst(blood.dna)} - - - {capitalizeFirst(blood.type)} - - - - - - - - - - - - ); -}; - -/** If antibodies are present, returns buttons to create vaccines */ -const AntibodyInfoDisplay = (props, context) => { - const { act, data } = useBackend(context); - const { is_ready, resistances = [] } = data; - if (!resistances) { - return Nothing detected; - } - - return ( - - - {!resistances.length - ? 'None' - : resistances.map((resistance) => { - return ( - - ); - })} - - - ); -}; - -/** Displays info for the loaded blood, if any */ -const SpecimenDisplay = (props, context) => { - const { act, data } = useBackend(context); - const [tab, setTab] = useLocalState(context, 'tab', 0); - const { is_ready, viruses = [] } = data; - const virus = viruses[tab]; - const setTabHandler = (index: number) => { - setTab(index); - }; - if (!viruses?.length || !virus) { - return Nothing detected.; - } - - return ( -
- {viruses.length > 1 && ( - - - - )} - -
- ); -}; - -/** Virus Tab display - changes the tab for virus info - * Whenever the tab changes, the virus info is updated - */ -const VirusTabs = (props: TabsProps, context) => { - const { data } = useBackend(context); - const { tab, tabHandler } = props; - const { viruses = [] } = data; - - return ( - - {viruses.map((virus, index) => { - return ( - tabHandler(index)} - key={virus.name}> - {virus.name} - - ); - })} - - ); -}; - -/** Displays info about the virus. Child elements display - * the virus's traits and descriptions. - */ -const VirusDisplay = (props: VirusDisplayProps) => { - const { virus } = props; - - return ( - - - - - {virus.is_adv && ( - <> - - - - - - )} - - ); -}; - -/** Displays the description, name and other info for the virus. */ -const VirusTextInfo = (props: VirusInfoProps, context) => { - const { act } = useBackend(context); - const { virus } = props; - - return ( - - - {virus.can_rename ? ( - - act('rename_disease', { - index: virus.index, - name: value, - }) - } - /> - ) : ( - {virus.name} - )} - - - {virus.description} - - - {capitalizeFirst(virus.agent)} - - {virus.spread} - {virus.cure} - - ); -}; - -/** Displays the traits of the virus. This could be iterated over - * with object.keys but you would need a helper function for the tooltips. - * I would rather hard code it here. - */ -const VirusTraitInfo = (props: VirusInfoProps) => { - const { virus } = props; - - return ( -
- - - - {virus.resistance} - - - - - {virus.stage_speed} - - - - - {virus.stealth} - - - - - {virus.transmission} - - - -
- ); -}; - -/** Similar to the virus info display. - * Returns info about symptoms as collapsibles. - */ -const SymptomDisplay = (props: SymptomDisplayProps) => { - const { symptoms = [] } = props; - if (!symptoms || !symptoms.length) { - return No symptoms detected.; - } - - return ( -
- {symptoms.map((symptom) => { - return ( - - - - {symptom.desc} - - - - - - - - - ); - })} -
- ); -}; - -/** Displays the numerical trait modifiers for a virus symptom */ -const SymptomTraitInfo = (props: SymptomInfoProps) => { - const { symptom } = props; - - return ( -
- - - - {symptom.level} - - - - - {symptom.resistance} - - - - - {symptom.stage_speed} - - - - - {symptom.stealth} - - - - - {symptom.transmission} - - - -
- ); -}; - -/** Displays threshold data */ -const ThresholdDisplay = (props: ThresholdDisplayProps) => { - const { thresholds = [] } = props; - let convertedThresholds: Threshold[] = []; - // Converts obj of obj => array of thresholds - // I'm sure there's a more succinct way to do this - Object.entries(thresholds).map((label) => { - return convertedThresholds.push({ - label: label[0], - descr: label[1].toString(), - }); - }); - - return ( -
- {!convertedThresholds.length ? ( - None - ) : ( - - {convertedThresholds.map((threshold) => { - return ( - - {threshold.descr} - - ); - })} - - )} -
- ); -}; - -/** Gives a color gradient based on the severity of the symptom. */ -const GetColor = (severity: number) => { - if (severity <= -10) { - return 'blue'; - } else if (severity <= -5) { - return 'darkturquoise'; - } else if (severity <= 0) { - return 'green'; - } else if (severity <= 7) { - return 'yellow'; - } else if (severity <= 13) { - return 'orange'; - } else { - return 'bad'; - } -}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx b/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx new file mode 100644 index 00000000000..d17c20107ae --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx @@ -0,0 +1,135 @@ +import { capitalizeFirst } from 'common/string'; +import { useBackend } from 'tgui/backend'; +import { Button, LabeledList, NoticeBox, ProgressBar, Section, Stack } from 'tgui/components'; +import { Data } from './types'; + +/** Displays loaded container info, if it exists */ +export const BeakerDisplay = (props, context) => { + const { act, data } = useBackend(context); + const { has_beaker, beaker, has_blood } = data; + const cant_empty = !has_beaker || !beaker?.volume; + let content; + if (!has_beaker) { + content = No beaker loaded.; + } else if (!beaker?.volume) { + content = Beaker is empty.; + } else if (!has_blood) { + content = No blood sample loaded.; + } else { + content = ( + + + + + + + + + ); + } + + return ( +
+
+ ); +}; + +/** Displays info about the blood type, beaker capacity - volume */ +const Info = (props, context) => { + const { data } = useBackend(context); + const { beaker, blood } = data; + if (!beaker || !blood) { + return No beaker loaded; + } + + return ( + + + + + {capitalizeFirst(blood.dna)} + + + {capitalizeFirst(blood.type)} + + + + + + + + + + + + ); +}; + +/** If antibodies are present, returns buttons to create vaccines */ +const Antibodies = (props, context) => { + const { act, data } = useBackend(context); + const { is_ready, resistances = [] } = data; + if (!resistances) { + return Nothing detected; + } + + return ( + + + {!resistances.length + ? 'None' + : resistances.map((resistance) => { + return ( + + ); + })} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx b/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx new file mode 100644 index 00000000000..1e0dbc4edad --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx @@ -0,0 +1,69 @@ +import { useBackend, useLocalState } from 'tgui/backend'; +import { Button, NoticeBox, Section, Stack, Tabs } from 'tgui/components'; +import { Data } from './types'; +import { SymptomDisplay } from './Symptom'; +import { VirusDisplay } from './Virus'; + +export const SpecimenDisplay = (props, context) => { + const { data } = useBackend(context); + const { viruses = [] } = data; + const [tab, setTab] = useLocalState(context, 'tab', 0); + const virus = viruses[tab]; + + return ( +
}> + {!virus ? ( + Nothing detected. + ) : ( + + + + + + {virus?.symptoms && } + + + )} +
+ ); +}; + +const Buttons = (props, context) => { + const { act, data } = useBackend(context); + const { is_ready, viruses = [] } = data; + const [tab, setTab] = useLocalState(context, 'tab', 0); + const virus = viruses[tab]; + + return ( + + {viruses.length > 1 && ( + + + {viruses.map((virus, index) => { + return ( + setTab(index)} + key={index}> + {virus.name} + + ); + })} + + + )} + +