From a5260456aebf4bd74678c3a7e5defcbf6e8fea8a Mon Sep 17 00:00:00 2001 From: Migratingcocofruit <69551563+Migratingcocofruit@users.noreply.github.com> Date: Fri, 8 Aug 2025 04:54:36 +0300 Subject: [PATCH] Advanced Disease Rework (#28451) * Reworks cures, adds strains, adds treatment chems to symptoms * Enables coexistence of different samples of the same strain at different stages within the same container * Adds symptom guessing. Correct guesses reduce analysis time and wrong ones increase it. * Adds a chance for a virus to evolve into a new strain. changing the cure and adding/subtracting symptoms * Revert "Adds a chance for a virus to evolve into a new strain. changing the cure and adding/subtracting symptoms" This reverts commit 124dd485c7c5f62c5ae095513bfc155d6af8c31d. * Adds spontaneous virus mutations, strain tracking. Removes antibody metabolism * change the amount of time added/reduced by each factor * Creates a disease outbreak event that allows admins to cause an outbreak of a virus of their choosing. Allows virus creation when choosing virus to give someone or for an outbreak. * The normal base property values are now the defaults for disease creation * Rebuild TGUI * Rebuild TGUI * Rebuild TGUI * Update tgui.bundle.js * remove redundant lines * improves ui some and moves to using world time for analysis time * fixes a bug with the analysis time and updates the initial known advanced disease list * Buffs the disease outbreak event and adds a base stat row to the PANDEMIC * Update tgui.bundle.js * Implements calibration * Adds a remove from database button * Moves the known disease global list to work per z level * Removes treatments from most chems and makes each instance of a symptom not acting purge some of the reagent that treated it * Update symptoms.dm * Adds stage to unknown diseases * Moves accumulated error to work per z level and change the location of the delete strain data button * Adds confirmation to the delete data button and moves the disease text out of the loop in the copy constructor * fix indentation * Replaces pent with hydrocodone in the advanced cure list * gives more buffs to event disease based on severity. removes longevity from possible symptoms an event disease can get. Fixes a bug with determining analysis time * Update tgui.bundle.js * Update pandemic.dm * Update pandemic.dm * Update tgui.bundle.js * review changes * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * Update tgui.bundle.js * review changes * Update tgui.bundle.js * Makes vaccines only add resistances and no longer cure * improves pandemic ui and fixes calibration button not being properly disabled * Update tgui.bundle.js * Add the new agars to the virologist's fridge * lowered evolution chance of viruses a bit * further lowers evolution chance * Update advance.dm * Changes the scaling of analysis time and fixes stage speed presentation in the pandemic * Scanners can now see virus strains that have been inserted in the pandemic * Allows a mob to be infected by both an event and non event disease * Fixes the +1 syringe of translocation stabbing your feet * Update tgui.bundle.js * Changes spontanous mutation to be per spread and makes disease events less likely. * Diseases less common, mutations happen only on spread now, symptom list ordered alphabetically in the dropdown, fixed some bugs * Update tgui.bundle.js * changes virus alerts * Removes numbers from the virus alert text and fixes a bug that caused excessive virus mutations * Update tgui.bundle.js * refactor disease spread * Changes disease spread logic to give players options to protect themselves and others * Blackbox things * Increases announcement timer and fixes the infection talley * Update disease_outbreak.dm * Adds an incubation period to advanced viruses. bumps sneeze back to 5 range * Adds failsafe to disease outbreaks that will infect a random crew member if none have arrived in 10 minutes * Only record advanced disease infections * Properly handle recording of disease infection events * Changed resistance effect on cure * another adjustment to res effect on cure * Small resist buff, spontanous combustion buff and some calculation fixes * changes spontanous combustion stat bonuses and maluses * Update flesh_eating.dm * Update flesh_eating.dm * Update flesh_eating.dm * Big trait changes and rebalance * adds treatments to fire and necro * Introduces viral contamination of food and reagent containers, as well as disinfection * Fix runtime in reagent transfers * Revert the prefs and config changes * Slowed down diseases and made symptoms more gradual, improved disease alerts to both crew and dchat * Sterilizine now sterilizes things contaminated with viruses. Heat reactions now take place alongside chemical reactions when adding reagents to a beaker * Adds option to order sterile masks, goggles and biosuits in packs of 4. Makes sterile masks as good as internals for viruses * adds logging to pandemic * Changes symptom stats and increased disease varaiety * Fixes some disease bugs * Fixes some bugs and makes it behave better * Adds shiver emote to shiver symptom * Adds admin message/log to contracting an event disease and fixes copy behaviour when infecting * Prefix spread defines * Change the rest of the macros * correct the disinfection temp macro * Moves disease signals to their own file * move disnifection temp back * fix a type * viral contaminations component review changes * var name changes * more style fixes * Update tgui.bundle.js * fixes pandemic * Fixes disease outbreak having two carriers sometimes and changes the virus alert to the new one * Update disease_defines.dm * fixes samples not passing stages on properly * actually fixes the bug * Fixes stabilization being lost on cloning * Finally completely and absolutely fixes the sample stages bug(please) * Makes the pandemic a machine * Update pandemic.dm * removed drinks from cure requirements * lowers chance of major viral outbreak * fixes pandemic not processing properly * Changes the cure system to have more options * Makes mixing event viruses more random * Fixes hardsuits having a 1% chance to be infected * Increases virus protection of most clothing. medical scrubs and surgical hat buffed sunstantially * Slow down progress of diseases * makes spread less likely * envirosuit is now slightly more protective from viruses, while the medical one is as good as scrubs * Adds further protection to helmets * Increases the number of possible cures and the scaling of required cures * Pandemic ui improvements * Update _disease.dm * fix a bug with spread target in the disease outbreak event * changes some clothing permeability * Adds the ability to make viral DNA extracts by injecting a sample into a black slime core. It's equivalent to 2 stages * Adds age to health analyzer, makes viral evolution toggle minor disabilities, confusion symptom now confuses more often * Update pandemic.dm * replace lazarus with acetic acid in the cures * Changes the relation of the amount of cure options to required cures and the scaling of required cures with resistance * Fixes virus renaming * changes description and permeability of some medical jumpsuits * Adds a list of all diseases infecting client mobs to the disease outbreak event * uses event UID instead of ref in the disease * Adds advanced disease carriers to admin antag menu * Adds viral eraser which removes tracking and stabilizing. Admin spawn only for now * Update tgui.bundle.js * Adds the ability to create viral genetic material using radiation. it can be used in addition to that produced in xenobio * changes alert logic, disease generation logic, adds treatments to weakness, cough and sneeze now more gradual and cough spreads at range 3 * makes disease advance a bit faster * reduce time to announcement to 6 minutes after 3 people are infected * Changes medibot disease detection to be the same as analyzers. Treatment chems now put the symptom on a cooldown that persists after they leave the body. * Make sure the disease outbreak event only selects crew * Fix abductor virus gland virus generation * Adds sterilizine to viro locker * Fixes null viruses appearing when merging virusless blood * Adds oculine to hyphema treatments * Rebalances weight loss and flesh eating * Improvements to advanced disease carrier tab on the TGUI antag menu * change the flag on away missions * Update mob_spawn.dm * Update tgui.bundle.js * adds sterilizine recipe back * Adds viral contamination to surgery tools and surgery steps * small changes * fixes pandemic ui. should work properly now(I hope) * CI compliance * Update flesh_eating.dm * Update vomit.dm * Update tgui.bundle.js * Initial revision of analysis and fixes the UI issue with multiple stage samples * refactors treatment and makes treatment chems more adjustable * var correction * symptoms and their contribution now show up on the pandemic * Update tgui.bundle.js * Update code/datums/diseases/advance/symptoms/cough.dm Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * change wizard disease and zombie disease macros to new ones * Update tgui.bundle.js * changes the part requirements on the pandemic boards to fit the actual parts * fix misbehaviour when inserting samples without symptoms * Some changes to difficulty scaling, reverts the level of projectile vomiting back to 4, and disallows germaphobes to avoid infection by injection * Adds viral contamination to IV bags --------- Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> --- .../RandomRuins/SpaceRuins/debris2.dmm | 2 +- .../RandomRuins/SpaceRuins/deepstorage.dmm | 2 +- .../SpaceRuins/syndie_space_base.dmm | 2 +- _maps/map_files/shuttles/admin_hospital.dmm | 2 +- _maps/map_files/stations/boxstation.dmm | 2 +- _maps/map_files/stations/cerestation.dmm | 2 +- _maps/map_files/stations/deltastation.dmm | 2 +- _maps/map_files/stations/emeraldstation.dmm | 2 +- _maps/map_files/stations/metastation.dmm | 2 +- code/__DEFINES/dcs/disease_signals.dm | 4 + code/__DEFINES/disease_defines.dm | 40 ++ code/__HELPERS/lists.dm | 21 + code/__HELPERS/radiation_helpers.dm | 5 +- code/__HELPERS/trait_helpers.dm | 6 + code/_globalvars/lists/reagents_lists.dm | 4 +- code/_globalvars/traits.dm | 4 +- code/datums/components/viral_contamination.dm | 45 ++ code/datums/diseases/_MobProcs.dm | 314 +++++++------- code/datums/diseases/_disease.dm | 126 ++++-- code/datums/diseases/advance/advance.dm | 385 +++++++++++++----- .../diseases/advance/disease_presets.dm | 36 +- .../datums/diseases/advance/symptoms/beard.dm | 54 ++- .../diseases/advance/symptoms/choking.dm | 46 +-- .../diseases/advance/symptoms/confusion.dm | 28 +- .../datums/diseases/advance/symptoms/cough.dm | 33 +- .../advance/symptoms/damage_converter.dm | 15 +- .../diseases/advance/symptoms/deafness.dm | 31 +- .../datums/diseases/advance/symptoms/dizzy.dm | 26 +- .../datums/diseases/advance/symptoms/fever.dm | 24 +- code/datums/diseases/advance/symptoms/fire.dm | 62 +-- .../diseases/advance/symptoms/flesh_eating.dm | 32 +- code/datums/diseases/advance/symptoms/hair.dm | 50 ++- .../diseases/advance/symptoms/hallucigen.dm | 32 +- .../diseases/advance/symptoms/headache.dm | 20 +- code/datums/diseases/advance/symptoms/heal.dm | 27 +- .../diseases/advance/symptoms/itching.dm | 17 +- .../diseases/advance/symptoms/oxygen.dm | 24 +- .../diseases/advance/symptoms/sensory.dm | 60 ++- .../diseases/advance/symptoms/shedding.dm | 10 +- .../diseases/advance/symptoms/shivering.dm | 19 +- code/datums/diseases/advance/symptoms/skin.dm | 59 ++- .../diseases/advance/symptoms/sneeze.dm | 28 +- .../diseases/advance/symptoms/symptoms.dm | 55 ++- .../datums/diseases/advance/symptoms/viral.dm | 46 ++- .../diseases/advance/symptoms/vision.dm | 57 +-- .../diseases/advance/symptoms/voice_change.dm | 40 +- .../datums/diseases/advance/symptoms/vomit.dm | 55 +-- .../diseases/advance/symptoms/weakness.dm | 43 +- .../diseases/advance/symptoms/weight.dm | 37 +- .../datums/diseases/advance/symptoms/youth.dm | 56 ++- code/datums/diseases/anxiety.dm | 4 +- code/datums/diseases/appendicitis.dm | 8 +- code/datums/diseases/beesease.dm | 4 +- code/datums/diseases/berserker.dm | 6 +- code/datums/diseases/brainrot.dm | 4 +- code/datums/diseases/cold.dm | 2 +- code/datums/diseases/cold9.dm | 4 +- code/datums/diseases/critical.dm | 10 +- code/datums/diseases/fake_gbs.dm | 4 +- code/datums/diseases/flu.dm | 2 +- code/datums/diseases/fluspanish.dm | 2 +- code/datums/diseases/food_poisoning.dm | 6 +- code/datums/diseases/gbs.dm | 8 +- code/datums/diseases/kingstons.dm | 4 +- code/datums/diseases/kuru.dm | 6 +- code/datums/diseases/lycancoughy.dm | 4 +- code/datums/diseases/magnitis.dm | 2 +- code/datums/diseases/pierrot_throat.dm | 2 +- code/datums/diseases/retrovirus.dm | 4 +- code/datums/diseases/rhumba_beat.dm | 4 +- code/datums/diseases/transformation.dm | 6 +- code/datums/diseases/tuberculosis.dm | 2 +- code/datums/diseases/wizard_diseases.dm | 30 +- code/datums/diseases/wizarditis.dm | 2 +- code/datums/diseases/zombie_virus.dm | 8 +- code/game/data_huds.dm | 2 +- .../gamemodes/miniantags/abduction/gland.dm | 5 +- code/game/gamemodes/wizard/magic_tarot.dm | 2 +- code/game/machinery/adv_med.dm | 4 +- .../game/machinery/computer/buildandrepair.dm | 6 - code/game/machinery/dish_drive.dm | 1 + code/game/machinery/machine_frame.dm | 8 + code/game/objects/items/devices/scanners.dm | 14 +- code/game/objects/objs.dm | 2 + code/game/turfs/space/space_turf.dm | 2 + code/modules/admin/admin_verbs.dm | 52 ++- code/modules/admin/menus/antagonist_menu.dm | 22 + .../antagonists/changeling/powers/panacea.dm | 2 +- .../antagonists/zombie/datum_zombie.dm | 3 +- code/modules/awaymissions/mob_spawn.dm | 4 +- code/modules/clothing/clothing.dm | 9 +- code/modules/clothing/glasses/hudglasses.dm | 2 +- code/modules/clothing/glasses/hudgoggles.dm | 5 + .../modules/clothing/gloves/colored_gloves.dm | 9 +- code/modules/clothing/gloves/misc_gloves.dm | 1 + code/modules/clothing/head/helmet.dm | 2 + code/modules/clothing/head/job_hats.dm | 1 + code/modules/clothing/masks/misc_masks.dm | 3 + code/modules/clothing/suits/labcoat.dm | 3 + .../clothing/under/jobs/medical_jumpsuits.dm | 17 +- .../under/jobs/plasmamen/_plasmamen.dm | 1 + .../clothing/under/jobs/plasmamen/medsci.dm | 2 + code/modules/events/disease_outbreak.dm | 115 ++++-- code/modules/events/event_container.dm | 3 +- code/modules/food_and_drinks/food_base.dm | 10 + .../kitchen_machinery/smartfridge.dm | 5 + .../mob/living/carbon/carbon_defense.dm | 9 +- .../modules/mob/living/carbon/carbon_procs.dm | 6 + .../living/carbon/human/species/_species.dm | 2 +- .../mob/living/simple_animal/bot/medbot.dm | 2 +- code/modules/mob/new_player/new_player.dm | 4 +- .../reagents/chemistry/machinery/pandemic.dm | 380 +++++++++++++---- .../chemistry/reagents/admin_reagents.dm | 2 +- .../reagents/chemistry/reagents/disease.dm | 35 ++ .../reagents/chemistry/reagents/medicine.dm | 29 ++ .../reagents/chemistry/reagents/water.dm | 59 ++- .../reagents/chemistry/reagents_datum.dm | 7 +- .../reagents/chemistry/reagents_holder.dm | 54 ++- .../chemistry/recipes/medicine_reactions.dm | 7 + .../reagents/chemistry/recipes/others.dm | 53 ++- .../chemistry/recipes/slime_extracts.dm | 22 + .../reagent_containers/chemical_bottle.dm | 20 + .../reagent_containers/glass_containers.dm | 11 +- .../reagents/reagent_containers/iv_bag.dm | 2 + .../reagents/reagent_containers/syringes.dm | 4 + .../supply/supply_packs/pack_medical.dm | 29 ++ code/modules/surgery/organs/blood.dm | 8 +- code/modules/surgery/surgery.dm | 2 + code/modules/virology/virology_goals.dm | 14 +- paradise.dme | 3 + sound/AI/outbreak_virus.ogg | Bin 0 -> 89732 bytes tgui/packages/tgui/interfaces/PanDEMIC.tsx | 286 ++++++++++--- tgui/public/tgui.bundle.js | 2 +- 133 files changed, 2484 insertions(+), 1117 deletions(-) create mode 100644 code/__DEFINES/dcs/disease_signals.dm create mode 100644 code/__DEFINES/disease_defines.dm create mode 100644 code/datums/components/viral_contamination.dm create mode 100644 sound/AI/outbreak_virus.ogg diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm index 19e721a1ec9..e1ec3664589 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm @@ -126,7 +126,7 @@ /turf/simulated/floor/plating/damaged/airless, /area/ruin/unpowered) "aZ" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel/airless, /area/ruin/unpowered) "Di" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm index 1de1b1d5637..14f8f4a0d0c 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm @@ -4554,7 +4554,7 @@ }, /area/ruin/space/deepstorage) "BU" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "white" diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm index d01d2418716..5604f99d938 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndie_space_base.dmm @@ -4728,7 +4728,7 @@ }, /area/ruin/unpowered/syndicate_space_base/genetics) "Br" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/shuttles/admin_hospital.dmm b/_maps/map_files/shuttles/admin_hospital.dmm index 1107280fbd1..cb4db56bbdf 100644 --- a/_maps/map_files/shuttles/admin_hospital.dmm +++ b/_maps/map_files/shuttles/admin_hospital.dmm @@ -264,7 +264,7 @@ /turf/simulated/floor/plating, /area/shuttle/administration) "bp" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/mineral/titanium, /area/shuttle/administration) "br" = ( diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm index e0306dbfd9d..c876e336b30 100644 --- a/_maps/map_files/stations/boxstation.dmm +++ b/_maps/map_files/stations/boxstation.dmm @@ -32777,7 +32777,7 @@ }, /area/station/science/genetics) "cpL" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whitegreen" diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm index bbadd4fd2eb..d1afc5e6066 100644 --- a/_maps/map_files/stations/cerestation.dmm +++ b/_maps/map_files/stations/cerestation.dmm @@ -79121,7 +79121,7 @@ /turf/simulated/floor/wood, /area/station/hallway/secondary/entry/south) "ptq" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ icon_state = "dark" }, diff --git a/_maps/map_files/stations/deltastation.dmm b/_maps/map_files/stations/deltastation.dmm index 8d588c69ddf..bae15843496 100644 --- a/_maps/map_files/stations/deltastation.dmm +++ b/_maps/map_files/stations/deltastation.dmm @@ -95690,7 +95690,7 @@ /turf/simulated/floor/plasteel/white, /area/station/medical/virology) "wWX" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /obj/effect/turf_decal/delivery/green, /turf/simulated/floor/plasteel{ dir = 8; diff --git a/_maps/map_files/stations/emeraldstation.dmm b/_maps/map_files/stations/emeraldstation.dmm index 8d31fdbc78f..90265e2f758 100644 --- a/_maps/map_files/stations/emeraldstation.dmm +++ b/_maps/map_files/stations/emeraldstation.dmm @@ -8647,7 +8647,7 @@ /area/station/maintenance/dorms/port) "bJK" = ( /obj/machinery/newscaster/directional/east, -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "whitegreen" diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm index 1842a9450ab..e49eca9f628 100644 --- a/_maps/map_files/stations/metastation.dmm +++ b/_maps/map_files/stations/metastation.dmm @@ -21015,7 +21015,7 @@ /turf/simulated/floor/plasteel, /area/station/hallway/primary/central/south) "ccp" = ( -/obj/machinery/computer/pandemic, +/obj/machinery/pandemic, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "whitegreen" diff --git a/code/__DEFINES/dcs/disease_signals.dm b/code/__DEFINES/dcs/disease_signals.dm new file mode 100644 index 00000000000..44946d9f78c --- /dev/null +++ b/code/__DEFINES/dcs/disease_signals.dm @@ -0,0 +1,4 @@ +/// Sent when the conditions to disinfect an atom are met +#define COMSIG_ATOM_DISINFECTED "atom_disinfected" +/// Sent when something is eaten or drunk by a mob +#define COMSIG_MOB_REAGENT_EXCHANGE "mob_reagent_exchange" diff --git a/code/__DEFINES/disease_defines.dm b/code/__DEFINES/disease_defines.dm new file mode 100644 index 00000000000..11232d52e90 --- /dev/null +++ b/code/__DEFINES/disease_defines.dm @@ -0,0 +1,40 @@ +/// Temperature at which objects contaminated by viruses are disinfected +#define VIRUS_DISINFECTION_TEMP (T0C + 60) + +/// The maximum amount of symptoms a virus can have +#define VIRUS_SYMPTOM_LIMIT 6 + +/// The maximum amount of time a symptom will be delayed for after the treating reagents have left the body +#define VIRUS_MAX_TREATMENT_TIMER 300 +#define VIRUS_TREATMENT_TIMER_MOD 2 + +/// Evoltion chance each cycle in percents. +/// The base chance in % that a virus will mutate as it spreads. Further modified by stage speed and the viral evolutionary acceleration trait +#define VIRUS_EVOLUTION_CHANCE 2 + +//Visibility Flags +#define VIRUS_HIDDEN_SCANNER (1<<0) +#define VIRUS_HIDDEN_PANDEMIC (1<<1) + +//Disease Flags +#define VIRUS_CURABLE (1<<0) +#define VIRUS_CAN_CARRY (1<<1) +#define VIRUS_CAN_RESIST (1<<2) + +//Spread Flags +#define SPREAD_SPECIAL (1<<0) +#define SPREAD_NON_CONTAGIOUS (1<<1) +#define SPREAD_BLOOD (1<<2) +#define SPREAD_CONTACT_FEET (1<<3) +#define SPREAD_CONTACT_HANDS (1<<4) +#define SPREAD_CONTACT_GENERAL (1<<5) +#define SPREAD_AIRBORNE (1<<6) + + +//Severity Defines +#define VIRUS_NONTHREAT "No threat" +#define VIRUS_MINOR "Minor" +#define VIRUS_MEDIUM "Medium" +#define VIRUS_HARMFUL "Harmful" +#define VIRUS_DANGEROUS "Dangerous!" +#define VIRUS_BIOHAZARD "BIOHAZARD THREAT!" diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index b3c050d8081..414926c99ca 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -115,6 +115,27 @@ return "[output][and_text][input[index]]" +// Returns a map in plain english as a string +/proc/english_map(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + var/total = length(input) + if(!total) + return "[nothing_text]" + else if(total == 1) + return "[input[1]]" + else if(total == 2) + return "[input[1]][and_text][input[2]]" + else + var/output = "" + var/index = 1 + while(index < total) + if(index == total - 1) + comma_text = final_comma_text + + output += "[input[index]] : [input[input[index]]][comma_text]" + index++ + + return "[output][and_text][input[index]] : [input[input[index]]]" + //Returns list element or null. Should prevent "index out of bounds" error. /proc/listgetindex(list/list, index) if(istype(list) && length(list)) diff --git a/code/__HELPERS/radiation_helpers.dm b/code/__HELPERS/radiation_helpers.dm index 983d95da2a7..b5ee6832832 100644 --- a/code/__HELPERS/radiation_helpers.dm +++ b/code/__HELPERS/radiation_helpers.dm @@ -31,8 +31,9 @@ insulation = thing.rad_insulation_beta if(GAMMA_RAD) insulation = thing.rad_insulation_gamma - /// 1 means no rad insulation, which means perfectly permeable, so no interaction with it directly, but the contents might be relevant. - if(insulation < 1) + // 1 means no rad insulation, which means perfectly permeable, so no interaction with it directly, but the contents might be relevant. + // HAS_TRAIT is used manually here since the macros for HAS_TRAIT as well as the traits aren't being recognized here + if(insulation < 1 || (thing.status_traits ? (thing.status_traits["absorb_rads"] ? TRUE : FALSE) : FALSE)) if(istype(thing, /obj/structure/window)) window_priority += thing else if(istype(thing, /obj/machinery/power/rad_collector)) diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index add506a6415..b667dce3037 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -315,6 +315,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// A surgical tool; If a surgical tool has this flag it can be used as an alternative to an open hand in surgery #define TRAIT_SURGICAL_OPEN_HAND "surgical_hand_alternative" +/// A wearable item that protects the covered areas from viral infection +#define TRAIT_ANTI_VIRAL "anti_viral" + +/// Given to items that have something that absorbs radiation in them that is neither the item or in it's contents +#define TRAIT_ABSORB_RADS "absorb_rads" + /// Prevent mobs on the turf from being affected by anything below that turf, such as a pulse demon going under it. Added by a /obj/structure with creates_cover set to TRUE #define TRAIT_TURF_COVERED "turf_covered" diff --git a/code/_globalvars/lists/reagents_lists.dm b/code/_globalvars/lists/reagents_lists.dm index 088d61d6329..978acdbcd68 100644 --- a/code/_globalvars/lists/reagents_lists.dm +++ b/code/_globalvars/lists/reagents_lists.dm @@ -13,7 +13,7 @@ GLOBAL_LIST_INIT(drinks, list("beer2","hot_coco","orangejuice","tomatojuice","li "berryjuice","poisonberryjuice","watermelonjuice","lemonjuice","banana", "bungojuice", "nothing","potato","milk","soymilk","cream","coffee","tea","icecoffee", "icetea","cola","nuka_cola","spacemountainwind","thirteenloko","dr_gibb", - "space_up","lemon_lime","beer","whiskey","gin","rum","vodka","holywater", + "space_up","lemon_lime","triple_citrus","beer","whiskey","gin","rum","vodka","holywater", "tequila","vermouth","wine","tonic","kahlua","cognac","ale","sodawater", "ice","bilk","atomicbomb","threemileisland","goldschlager","patron","gintonic", "cubalibre","whiskeycola","martini","vodkamartini","whiterussian","screwdrivercocktail", @@ -50,5 +50,5 @@ GLOBAL_LIST_INIT(blocked_chems, list("polonium", "initropidril", "concentrated_i "teslium_paste", "omnizine_no_addiction", "zombiecure1", "zombiecure2", "zombiecure3", "zombiecure4", "admincleaner_all", "admincleaner_item", "admincleaner_mob", - "synthetic_omnizine_no_addiction", "surge_plus" + "synthetic_omnizine_no_addiction", "surge_plus", "viral_eraser" )) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 4dd7f20bbe5..4fbe48bfaf5 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -146,7 +146,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_ITEM_ACTIVE" = TRAIT_ITEM_ACTIVE, "TRAIT_NO_STRIP" = TRAIT_NO_STRIP, "TRAIT_SKIP_EXAMINE" = TRAIT_SKIP_EXAMINE, - "TRAIT_I_WANT_BRAINS_ORGAN" = TRAIT_I_WANT_BRAINS_ORGAN + "TRAIT_I_WANT_BRAINS_ORGAN" = TRAIT_I_WANT_BRAINS_ORGAN, + "TRAIT_ANTI_VIRAL" = TRAIT_ANTI_VIRAL, + "TRAIT_ABSORB_RADS" = TRAIT_ABSORB_RADS ), /turf = list( diff --git a/code/datums/components/viral_contamination.dm b/code/datums/components/viral_contamination.dm new file mode 100644 index 00000000000..402ca20973c --- /dev/null +++ b/code/datums/components/viral_contamination.dm @@ -0,0 +1,45 @@ +/datum/component/viral_contamination + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Asoc list of virus Ids to virus refs. + var/list/viruses = list() + +/datum/component/viral_contamination/Initialize(_viruses) + if(!isobj(parent)) + return COMPONENT_INCOMPATIBLE + for(var/datum/disease/virus in _viruses) + // If we don't have a copy of this virus, put one it + if(virus.spread_flags >= SPREAD_BLOOD && !(virus.GetDiseaseID() in viruses)) + viruses += list("[virus.GetDiseaseID()]" = virus.Copy()) + RegisterSignal(parent, COMSIG_MOB_REAGENT_EXCHANGE, PROC_REF(infect_target)) + RegisterSignal(parent, COMSIG_ATOM_DISINFECTED, PROC_REF(disinfect)) + +/datum/component/viral_contamination/InheritComponent(datum/component/viral_contamination/C, i_am_original, _viruses) + if(!i_am_original) + return + var/list/virus_list = list() + if(C) + virus_list = C.viruses + // Add a copy of each virus to our list if we don't already have one + for(var/disease_id in virus_list) + if(!(disease_id in viruses)) + var/datum/disease/virus = C.viruses["[disease_id]"] + viruses += list("[disease_id]" = virus.Copy()) + else + virus_list = _viruses + // Add a copy of each virus to our list if we don't already have on + for(var/datum/disease/virus in virus_list) + if(virus.spread_flags >= SPREAD_BLOOD && !(virus.GetDiseaseID() in viruses)) + viruses += list(list("[virus.GetDiseaseID()]" = virus.Copy())) + +/datum/component/viral_contamination/proc/infect_target(atom/source, mob/target) + SIGNAL_HANDLER // COMSIG_MOB_REAGENT_EXCHANGE + if(!istype(target)) + return + for(var/disease_id in viruses) + var/datum/disease/virus = viruses[disease_id] + if(istype(virus)) + target.ForceContractDisease(virus, FALSE) + +/datum/component/viral_contamination/proc/disinfect(atom/source) + SIGNAL_HANDLER // COMSIG_ATOM_DISINFECTED + qdel(src) diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index a7aefe4a03e..0e7261df8fe 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -1,13 +1,14 @@ - -/mob/proc/HasDisease(datum/disease/D) - for(var/thing in viruses) - var/datum/disease/DD = thing - if(DD.IsSame(D)) - return TRUE - return FALSE +/* +MARK: Contraction -/mob/proc/CanContractDisease(datum/disease/D) + + + +MARK: Helpers +*/ + +/mob/proc/check_contraction_mob(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) if(stat == DEAD && !D.allow_dead) return FALSE @@ -17,145 +18,30 @@ if(HasDisease(D)) return FALSE - if(istype(D, /datum/disease/advance) && count_by_type(viruses, /datum/disease/advance) > 0) - return FALSE + if(istype(D, /datum/disease/advance)) + var/datum/disease/advance/advanced = D + for(var/datum/disease/advance/exists in viruses) + if(exists.event == advanced.event) + return FALSE if(!(type in D.viable_mobtypes)) return -1 //for stupid fucking monkies return TRUE +/mob/living/carbon/proc/check_contraction_carbon(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + // positive satiety makes it harder to contract the disease. + return (spread_method & SPREAD_BLOOD) || !((satiety > 0 && prob(satiety/10)) || (prob(15/D.permeability_mod))) -/mob/proc/ContractDisease(datum/disease/D) - if(!CanContractDisease(D)) - return 0 - AddDisease(D) - return TRUE +/mob/living/carbon/human/proc/check_contraction_human(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + if((HAS_TRAIT(src, TRAIT_VIRUSIMMUNE) && !D.bypasses_immunity)) + return FALSE - -/mob/proc/AddDisease(datum/disease/D, respect_carrier = FALSE) - var/datum/disease/DD = new D.type(1, D, 0) - viruses += DD - DD.affected_mob = src - GLOB.active_diseases += DD //Add it to the active diseases list, now that it's actually in a mob and being processed. - - //Copy properties over. This is so edited diseases persist. - var/list/skipped = list("affected_mob","holder","carrier","stage","type","parent_type","vars","transformed") - if(respect_carrier) - skipped -= "carrier" - for(var/V in DD.vars) - if(V in skipped) - continue - if(istype(DD.vars[V],/list)) - var/list/L = D.vars[V] - DD.vars[V] = L.Copy() - else - DD.vars[V] = D.vars[V] - - create_log(MISC_LOG, "has contacted the virus \"[DD]\"") - DD.affected_mob.med_hud_set_status() - - -/mob/living/carbon/ContractDisease(datum/disease/D) - if(!CanContractDisease(D)) - return 0 + // We transfer to the blood directly + if(spread_method == SPREAD_BLOOD) + return TRUE if(src.mind && HAS_TRAIT(src.mind, TRAIT_GERMOPHOBE) && prob(85)) - return 0 - - var/obj/item/clothing/Cl = null - var/passed = 1 - - var/head_ch = 100 - var/body_ch = 100 - var/hands_ch = 25 - var/feet_ch = 25 - - if(D.spread_flags & CONTACT_HANDS) - head_ch = 0 - body_ch = 0 - hands_ch = 100 - feet_ch = 0 - if(D.spread_flags & CONTACT_FEET) - head_ch = 0 - body_ch = 0 - hands_ch = 0 - feet_ch = 100 - - if(prob(15/D.permeability_mod)) - return - - if(satiety > 0 && prob(satiety/10)) // positive satiety makes it harder to contract the disease. - return - - var/target_zone = pick(head_ch;1,body_ch;2,hands_ch;3,feet_ch;4) - - if(ishuman(src)) - var/mob/living/carbon/human/H = src - - switch(target_zone) - if(1) - if(isobj(H.head) && !istype(H.head, /obj/item/paper)) - Cl = H.head - passed = prob((Cl.permeability_coefficient*100) - 1) - if(passed && isobj(H.wear_mask)) - Cl = H.wear_mask - passed = prob((Cl.permeability_coefficient*100) - 1) - if(2) - if(isobj(H.wear_suit)) - Cl = H.wear_suit - passed = prob((Cl.permeability_coefficient*100) - 1) - if(passed && isobj(H.w_uniform)) - Cl = H.w_uniform - passed = prob((Cl.permeability_coefficient*100) - 1) - if(3) - if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered&HANDS) - Cl = H.wear_suit - passed = prob((Cl.permeability_coefficient*100) - 1) - - if(passed && isobj(H.gloves)) - Cl = H.gloves - passed = prob((Cl.permeability_coefficient*100) - 1) - if(4) - if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered&FEET) - Cl = H.wear_suit - passed = prob((Cl.permeability_coefficient*100) - 1) - - if(passed && isobj(H.shoes)) - Cl = H.shoes - passed = prob((Cl.permeability_coefficient*100) - 1) - - - if(!passed && (D.spread_flags & AIRBORNE) && !internal) - passed = (prob((50*D.permeability_mod) - 1)) - - if(passed) - AddDisease(D) - return passed - - -/** - * Forces the mob to contract a virus. If the mob can have viruses. Ignores clothing and other protection - * Returns TRUE if it succeeds. False if it doesn't - * - * Arguments: - * * D - the disease the mob will try to contract - * * respect_carrier - if set to TRUE will not ignore the disease carrier flag - * * notify_ghosts - will notify ghosts of infection if set to TRUE - */ -//Same as ContractDisease, except never overidden clothes checks -/mob/proc/ForceContractDisease(datum/disease/D, respect_carrier, notify_ghosts = FALSE) - if(!CanContractDisease(D)) - return FALSE - if(notify_ghosts) - for(var/mob/ghost as anything in GLOB.dead_mob_list) //Announce outbreak to dchat - to_chat(ghost, "Disease outbreak: [src] ([ghost_follow_link(src, ghost)]) [D.carrier ? "is now a carrier of" : "has contracted"] [D]!") - AddDisease(D, respect_carrier) - return TRUE - - -/mob/living/carbon/human/CanContractDisease(datum/disease/D) - if(HAS_TRAIT(src, TRAIT_VIRUSIMMUNE) && !D.bypasses_immunity) return FALSE for(var/organ in D.required_organs) @@ -166,13 +52,157 @@ if(locate(organ) in bodyparts) continue return FALSE - return ..() -/mob/living/carbon/human/monkey/CanContractDisease(datum/disease/D) + var/passed = FALSE + + if(spread_method & (SPREAD_CONTACT_FEET | SPREAD_CONTACT_HANDS | SPREAD_CONTACT_GENERAL)) + passed = TRUE + var/obj/item/clothing/covering_garment = null + + var/head_ch = 100 + var/body_ch = 100 + var/hands_ch = 25 + var/feet_ch = 25 + + if(D.spread_flags & SPREAD_CONTACT_HANDS) + head_ch = 0 + body_ch = 0 + hands_ch = 100 + feet_ch = 0 + if(D.spread_flags & SPREAD_CONTACT_FEET) + head_ch = 0 + body_ch = 0 + hands_ch = 0 + feet_ch = 100 + + var/target_zone = pick(head_ch;1,body_ch;2,hands_ch;3,feet_ch;4) + + switch(target_zone) + if(1) + if(isobj(head) && !istype(head, /obj/item/paper)) + covering_garment = head + passed = prob((covering_garment.permeability_coefficient*100) - 1) + if(passed && isobj(wear_mask)) + covering_garment = wear_mask + passed = prob((covering_garment.permeability_coefficient*100) - 1) + if(2) + if(isobj(wear_suit)) + covering_garment = wear_suit + passed = prob((covering_garment.permeability_coefficient*100) - 1) + if(passed && isobj(w_uniform)) + covering_garment = w_uniform + passed = prob((covering_garment.permeability_coefficient*100) - 1) + if(3) + if(isobj(wear_suit) && wear_suit.body_parts_covered&HANDS) + covering_garment = wear_suit + passed = prob((covering_garment.permeability_coefficient*100) - 1) + + if(passed && isobj(gloves)) + covering_garment = gloves + passed = prob((covering_garment.permeability_coefficient*100) - 1) + if(4) + if(isobj(wear_suit) && wear_suit.body_parts_covered&FEET) + covering_garment = wear_suit + passed = prob((covering_garment.permeability_coefficient*100) - 1) + + if(passed && isobj(shoes)) + covering_garment = shoes + passed = prob((covering_garment.permeability_coefficient*100) - 1) + + // If spreading by air we need to get through the mask and helmet. + if(!passed && (spread_method & SPREAD_AIRBORNE)) + // Check for eye protection + var/eye_check = !((wear_mask?.flags_cover & MASKCOVERSEYES) || (head?.flags_cover & HEADCOVERSEYES) || (glasses?.flags_cover & GLASSESCOVERSEYES)) + // Masks and hoods protect our mouth from sneezes, but not truely airborn viruses + var/mouth_check = ((prob((wear_mask?.permeability_coefficient*100) - 1) || !(wear_mask?.flags_cover & MASKCOVERSMOUTH)) && (prob((head?.permeability_coefficient*100) - 1) || !(head?.flags_cover & HEADCOVERSMOUTH))) + // A further check on the mask against airborne diseases + var/breath_check = !internal && (D.spread_flags & SPREAD_AIRBORNE) && prob(100 * D.permeability_mod) && !((wear_mask?.flags_cover & MASKCOVERSMOUTH && HAS_TRAIT(wear_mask, TRAIT_ANTI_VIRAL)) || (head?.flags_cover & MASKCOVERSMOUTH && HAS_TRAIT(head, TRAIT_ANTI_VIRAL))) + // If anything gets penetrated we contract the disease. + passed = eye_check || mouth_check || breath_check + + return passed + +// MARK: Mob + +/// Grinds a mob in the turbine +/mob/compressor_grind() + gib() + +/// Returns whether or not the mob has the disease +/mob/proc/HasDisease(datum/disease/D) + for(var/datum/disease/DD in viruses) + if(DD.IsSame(D)) + return TRUE + return FALSE + +/// Checks if a mob can contract the disease +/mob/proc/can_contract_disease(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + return check_contraction_mob(D, spread_method) + +/mob/proc/can_spread_disease(D, spread_method = SPREAD_CONTACT_GENERAL) + return TRUE + +// Attempt contracting a diseas +/mob/proc/ContractDisease(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + if(!can_contract_disease(D, spread_method)) + return FALSE + AddDisease(D) + return TRUE + +/** + * Forces the mob to contract a virus. If the mob can have viruses. Ignores clothing and other protection + * Returns TRUE if it succeeds. False if it doesn't + * + * Arguments: + * * D - the disease the mob will try to contract + * * respect_carrier - if set to TRUE will not ignore the disease carrier flag + * * notify_ghosts - will notify ghosts of infection if set to TRUE + */ +//Same as ContractDisease, except it ignores the clothes check +/mob/proc/ForceContractDisease(datum/disease/D, respect_carrier, notify_ghosts = FALSE) + if(!can_contract_disease(D, SPREAD_BLOOD)) + return FALSE + if(notify_ghosts) + for(var/mob/ghost as anything in GLOB.dead_mob_list) //Announce outbreak to dchat + to_chat(ghost, "Disease outbreak: [src] ([ghost_follow_link(src, ghost)]) [D.carrier ? "is now a carrier of" : "has contracted"] [D]!") + message_admins("[key_name(src)] [D.carrier ? "is now a carrier of" : "has contracted"] [D]!") + log_admin("[key_name(src)] [D.carrier ? "is now a carrier of" : "has contracted"] [D]!") + AddDisease(D, respect_carrier) + return TRUE + +/// Directly adds a disease to a mob. +/mob/proc/AddDisease(datum/disease/D, respect_carrier = FALSE, start_stage = 1) + if(client) + D.record_infection() + var/datum/disease/DD = D.Copy(FALSE) + DD.stage = start_stage + viruses += DD + DD.affected_mob = src + GLOB.active_diseases += DD //Add it to the active diseases list, now that it's actually in a mob and being processed. + + if(respect_carrier) + DD.carrier = D.carrier + + create_log(MISC_LOG, "has contracted the virus \"[DD]\"") + DD.affected_mob.med_hud_set_status() + DD.after_infect() + +// MARK: Carbon +/mob/living/carbon/can_spread_disease(D, spread_method = SPREAD_CONTACT_GENERAL) + return check_contraction_carbon(D, spread_method) + +/mob/living/carbon/can_contract_disease(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + return ..() && check_contraction_carbon(D) + +//MARK: Human +/mob/living/carbon/human/can_spread_disease(D, spread_method = SPREAD_CONTACT_GENERAL) + return check_contraction_human(D, spread_method) + +/mob/living/carbon/human/can_contract_disease(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) + return check_contraction_human(D, spread_method) && ..() + +/mob/living/carbon/human/monkey/can_contract_disease(datum/disease/D, spread_method = SPREAD_CONTACT_GENERAL) . = ..() if(. == -1) if(D.viable_mobtypes.Find(/mob/living/carbon/human)) return 1 //this is stupid as fuck but because monkeys are only half the time actually subtypes of humans they need this - -/mob/compressor_grind() - gib() diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index a12fd49f217..51d3b4c226a 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -1,42 +1,12 @@ -#define VIRUS_SYMPTOM_LIMIT 6 - -//Visibility Flags -#define HIDDEN_SCANNER (1<<0) -#define HIDDEN_PANDEMIC (1<<1) - -//Disease Flags -#define CURABLE (1<<0) -#define CAN_CARRY (1<<1) -#define CAN_RESIST (1<<2) - -//Spread Flags -#define SPECIAL (1<<0) -#define NON_CONTAGIOUS (1<<1) -#define BLOOD (1<<2) -#define CONTACT_FEET (1<<3) -#define CONTACT_HANDS (1<<4) -#define CONTACT_GENERAL (1<<5) -#define AIRBORNE (1<<6) - - -//Severity Defines -#define NONTHREAT "No threat" -#define MINOR "Minor" -#define MEDIUM "Medium" -#define HARMFUL "Harmful" -#define DANGEROUS "Dangerous!" -#define BIOHAZARD "BIOHAZARD THREAT!" - - GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) /datum/disease //Flags var/visibility_flags = 0 - var/disease_flags = CURABLE|CAN_CARRY|CAN_RESIST - var/spread_flags = AIRBORNE + var/disease_flags = VIRUS_CURABLE|VIRUS_CAN_CARRY|VIRUS_CAN_RESIST + var/spread_flags = SPREAD_AIRBORNE //Fluff /// Used for identification of viruses in the Medical Records Virus Database @@ -60,19 +30,21 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) //Other var/list/viable_mobtypes = list() //typepaths of viable mobs var/mob/living/carbon/affected_mob - var/list/cures = list() //list of cures if the disease has the CURABLE flag, these are reagent ids + var/list/cures = list() //list of cures if the disease has the VIRUS_CURABLE flag, these are reagent ids var/infectivity = 65 var/cure_chance = 8 var/carrier = FALSE //If our host is only a carrier var/bypasses_immunity = FALSE //Does it skip species virus immunity check? Some things may diseases and not viruses var/virus_heal_resistant = FALSE // Some things aren't technically viruses/traditional diseases and should be immune to edge case cure methods, like healing viruses. var/permeability_mod = 1 - var/severity = NONTHREAT + var/severity = VIRUS_NONTHREAT var/list/required_organs = list() var/needs_all_cures = TRUE var/list/strain_data = list() //dna_spread special bullshit /// Allow the virus to infect and process while the affected_mob is dead var/allow_dead = FALSE + /// How many cycles we should incubate + var/incubation = 0 /datum/disease/Destroy() affected_mob = null @@ -80,10 +52,19 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) return ..() /datum/disease/proc/stage_act() + // Still incubating, do nothing + if(incubation) + if(is_species(affected_mob, /datum/species/monkey)) + incubation = 0 + else + incubation-- + return FALSE + // We have no host if(!affected_mob) return FALSE var/cure = has_cure() + // This is patient 0 and we aren't curing them. Do nothing if(carrier && !cure) return FALSE @@ -104,14 +85,14 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) if(has_cure && prob(cure_chance)) stage = max(stage - 1, 1) - if(disease_flags & CURABLE) + if(disease_flags & VIRUS_CURABLE) if(has_cure && prob(cure_chance)) cure() return FALSE return TRUE /datum/disease/proc/has_cure() - if(!(disease_flags & CURABLE)) + if(!(disease_flags & VIRUS_CURABLE)) return 0 var/cures_found = 0 @@ -132,7 +113,7 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) if(!affected_mob) return - if((spread_flags & SPECIAL || spread_flags & NON_CONTAGIOUS || spread_flags & BLOOD) && !force_spread) + if((spread_flags & SPREAD_SPECIAL || spread_flags & SPREAD_NON_CONTAGIOUS || spread_flags & SPREAD_BLOOD) && !force_spread) return if(affected_mob.reagents.has_reagent("spaceacillin") || (affected_mob.satiety > 0 && prob(affected_mob.satiety/10))) @@ -143,17 +124,29 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) if(force_spread) spread_range = force_spread - if(spread_flags & AIRBORNE) + if(spread_flags & SPREAD_AIRBORNE) spread_range++ + var/spread_method = NONE + // If we do an airborne spread we will do that as well as other spreads + if((spread_flags & SPREAD_AIRBORNE) || spread_range > 1) + spread_method |= SPREAD_AIRBORNE + var/turf/target = affected_mob.loc if(istype(target)) for(var/mob/living/carbon/C in oview(spread_range, affected_mob)) + // Assume we are touching + spread_method |= (SPREAD_CONTACT_GENERAL | SPREAD_CONTACT_FEET | SPREAD_CONTACT_HANDS) var/turf/current = get_turf(C) if(current) while(TRUE) + // Found a path from target to source that isn't atmos blocked. Try to give them the disease if(current == target) - C.ContractDisease(src) + // If we are further than 1 tile we aren't touching + if(get_dist(target, C) > 1) + spread_method &= ~(SPREAD_CONTACT_GENERAL | SPREAD_CONTACT_FEET | SPREAD_CONTACT_HANDS) + // We also want to test our own mob's permeability so people in hardsuits with internals won't just infect others with sneezes or touch + affected_mob.can_spread_disease(src, spread_method) && C.ContractDisease(src, spread_method) break var/direction = get_dir(current, target) var/turf/next = get_step(current, direction) @@ -161,16 +154,23 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) break current = next +/datum/disease/proc/after_infect() + return /datum/disease/proc/cure() if(affected_mob) - if(disease_flags & CAN_RESIST) + if(disease_flags & VIRUS_CAN_RESIST) if(!(type in affected_mob.resistances)) affected_mob.resistances += type remove_virus() affected_mob.create_log(MISC_LOG, "has been cured from the virus \"[src]\"") qdel(src) +// Gives the received mob a resistance to this disease. Does not cure it if they are already infected +/datum/disease/proc/make_resistant(mob/living/target) + if(target && disease_flags & VIRUS_CAN_RESIST && !(type in target.resistances)) + target.resistances += type + /datum/disease/proc/IsSame(datum/disease/D) if(ispath(D)) return istype(src, D) @@ -183,12 +183,35 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) D.strain_data = strain_data.Copy() return D +/datum/disease/proc/get_required_cures() + return needs_all_cures ? length(cures) : 1 + +/datum/disease/proc/is_stabilized() + return TRUE + +/datum/disease/proc/get_tracker() + return "" + +/datum/disease/proc/get_stage() + return stage /datum/disease/proc/GetDiseaseID() return type +// Id for the pandemic +/datum/disease/proc/get_ui_id() + return name + +// Another Id for the pandemic. We need multiple procs so we can override them in different ways +/datum/disease/proc/get_strain_id() + return name + +// Another Id for the pandemic. +/datum/disease/proc/get_full_strain_id() + return name + /datum/disease/proc/IsSpreadByTouch() - if(spread_flags & CONTACT_FEET || spread_flags & CONTACT_HANDS || spread_flags & CONTACT_GENERAL) + if(spread_flags & SPREAD_CONTACT_FEET || spread_flags & SPREAD_CONTACT_HANDS || spread_flags & SPREAD_CONTACT_GENERAL) return 1 return 0 @@ -196,3 +219,24 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) /datum/disease/proc/remove_virus() affected_mob.viruses -= src //remove the datum from the list affected_mob.med_hud_set_status() + +/// Not interested in normal diseases right now +/datum/disease/proc/record_infection() + return + +/// Returns whether or not the disease is known +/datum/disease/proc/is_known(z) + return TRUE + +/// Returns an Asoc list of disease's symptoms and their properties in a format usable by the PANDEMIC +/datum/disease/proc/get_pandemic_symptoms() + return list() + +/// Returns the Disease's base stats +/datum/disease/proc/get_pandemic_base_stats() + return list( + "resistance" = 0, + "stealth" = 0, + "stageSpeed" = 0, + "transmissibility" = 0, + ) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index ccfabbd9a88..b0c2e6ef19b 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -1,3 +1,4 @@ + /* Advance Disease is a system for Virologist to Engineer their own disease with symptoms that have effects and properties @@ -6,16 +7,30 @@ If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus. */ +GLOBAL_VAR_INIT(next_unique_strain, 1) + GLOBAL_LIST_EMPTY(archive_diseases) // The order goes from easy to cure to hard to cure. -GLOBAL_LIST_INIT(advance_cures, list( +GLOBAL_LIST_INIT(standard_cures, list( "sodiumchloride", "sugar", "orangejuice", "spaceacillin", "salglu_solution", "ethanol", "teporone", "diphenhydramine", "lipolicide", "silver", "gold" )) +GLOBAL_LIST_INIT(advanced_cures, list( + "atropine", "mitocholide", "acetic_acid", + "cryoxadone", "hydrocodone", "haloperidol", + "degreaser", "perfluorodecalin" +)) + +GLOBAL_LIST_INIT(plant_cures,list( + "bicaridine", "kelotane", "omnizine", + "synaptizine", "weak_omnizine", "morphine", + "cbd", "thc", "nicotine" , "psilocybin" +)) + /* PROPERTIES @@ -31,12 +46,31 @@ GLOBAL_LIST_INIT(advance_cures, list( max_stages = 5 spread_text = "Unknown" viable_mobtypes = list(/mob/living/carbon/human) + incubation = 60 // NEW VARS - var/list/symptoms = list() // The symptoms of the disease. + /// The base properties of the virus. retained between strains + var/list/base_properties = list("resistance" = 1, "stealth" = 0, "stage rate" = 1, "transmissibility" = 1) + /// Chance of the virus evolving on spread + var/evolution_chance = VIRUS_EVOLUTION_CHANCE + /// The symptoms of the disease. + var/list/symptoms = list() + /// A unique ID for the strain and symptoms. var/id = "" + /// Saves an ID. If that ID is analyzed the virus will be automatically analyzed when inserted into the PANDEMIC + var/tracker = "" var/processing = FALSE + /// A unique ID for the strain. Uses the unique_datum_id of the first virus datum that is of that strain. + var/strain = "" + /// The event the virus came from, if it did + var/event + /// How far along the disease has progressed? This is tied with stage but is separate to give more granularity to symptom effects + var/progress = 0 + /// The time at which the disease last advanced + var/last_advancement = 0 + /// The number of cures from the cure list required to cure a patient + var/cures_required = 1 /* @@ -44,46 +78,102 @@ GLOBAL_LIST_INIT(advance_cures, list( */ -/datum/disease/advance/New(process = 1, datum/disease/advance/D) - if(!istype(D)) - D = null +/datum/disease/advance/New(datum/disease/advance/to_copy, _event = NONE, copy_stage = TRUE) + if(!istype(to_copy)) + to_copy = null + strain = "origin" + event = _event + last_advancement = world.time + // whether to generate a new cure or not + var/new_cure = TRUE // Generate symptoms if we weren't given any. - if(!symptoms || !length(symptoms)) - - if(!D || !D.symptoms || !length(D.symptoms)) + if(!to_copy || !to_copy.symptoms || !length(to_copy.symptoms)) symptoms = GenerateSymptoms(0, 2) else - for(var/datum/symptom/S in D.symptoms) + for(var/datum/symptom/S in to_copy.symptoms) symptoms += new S.type + // Copy cure, evolution ability and strain if we are copying an existing disease + if(to_copy) + name = to_copy.name + base_properties = to_copy.base_properties.Copy() + evolution_chance = to_copy.evolution_chance + tracker = to_copy.tracker + for(var/cure_reagent in to_copy.cures) + cures += cure_reagent + cure_text = to_copy.cure_text + strain = to_copy.strain + if(copy_stage) + stage = to_copy.stage + event = to_copy.event + new_cure = FALSE + + Refresh(FALSE, FALSE , new_cure, FALSE) - Refresh() - ..(process, D) return /datum/disease/advance/Destroy() if(processing) for(var/datum/symptom/S in symptoms) S.End(src) + if(event) + var/datum/event/disease_outbreak/outbreak = locateUID(event) + if(istype(outbreak) && !QDELETED(outbreak)) + outbreak.infected_players -= src return ..() +/// Randomly mutate the disease +/datum/disease/advance/after_infect() + if(event && affected_mob.mind) + var/datum/event/disease_outbreak/outbreak = locateUID(event) + if(istype(outbreak) && !QDELETED(outbreak)) + outbreak.infected_players |= src + if(prob(evolution_chance)) + if(affected_mob.mind) + SSblackbox.record_feedback("tally", "Advanced Disease", 1, "Spontanous Evolution") + var/min = rand(1, 6) + var/max = rand(min, 6) + var/last_strain = strain + // In most cases we try to gain a symptom, rarely we lose one + if(prob(95)) + Evolve(min, max) + else + Devolve() + // Create a new strain even if we didn't gain or lose symptoms + if(last_strain == strain) + Refresh() + // Randomly pick a symptom to activate. /datum/disease/advance/stage_act() if(!..()) return FALSE if(symptoms && length(symptoms)) - + var/list/mob_reagents = list() + for(var/datum/reagent/chem in affected_mob.reagents.reagent_list) + mob_reagents += list("[chem.id]" = chem.volume) if(!processing) processing = TRUE for(var/datum/symptom/S in symptoms) S.Start(src) - for(var/datum/symptom/S in symptoms) S.Activate(src) else CRASH("We do not have any symptoms during stage_act()!") return TRUE +/datum/disease/advance/spread(force_spread) + if(carrier || force_spread || prob(40 + progress)) + return ..() + +/datum/disease/advance/handle_stage_advance(has_cure = FALSE) + if(!has_cure && (prob(stage_prob) || world.time > last_advancement + 1000)) + last_advancement = world.time + progress = min(progress + 6, 100) + stage = min(ceil(progress / 20), max_stages) + if(!discovered && stage >= CEILING(max_stages * discovery_threshold, 1)) // Once we reach a late enough stage, medical HUDs can pick us up even if we regress + discovered = TRUE + affected_mob.med_hud_set_status() + // Compares type then ID. /datum/disease/advance/IsSame(datum/disease/advance/D) if(ispath(D)) @@ -97,6 +187,11 @@ GLOBAL_LIST_INIT(advance_cures, list( return TRUE +/datum/disease/advance/handle_cure_testing(has_cure = FALSE) + . = ..() + if(has_cure) + progress = min(progress, stage * 20) + // To add special resistances. /datum/disease/advance/cure(resistance=1) if(affected_mob) @@ -106,9 +201,19 @@ GLOBAL_LIST_INIT(advance_cures, list( remove_virus() qdel(src) //delete the datum to stop it processing +// Gives the mob a resistance to this disease. Does not cure it if they are already infected +/datum/disease/advance/make_resistant(mob/living/target) + if(target) + var/id = "[GetDiseaseID()]" + if(!(id in target.resistances)) + target.resistances[id] = id + // Returns the advance disease with a different reference memory. -/datum/disease/advance/Copy(process = 0) - return new /datum/disease/advance(process, src, 1) +/datum/disease/advance/Copy(copy_stage = TRUE) + return new /datum/disease/advance(to_copy = src, copy_stage = copy_stage) + +/datum/disease/advance/record_infection() + SSblackbox.record_feedback("tally", "Advanced Disease", 1, "[name]_[strain] Infection") /* @@ -119,7 +224,12 @@ GLOBAL_LIST_INIT(advance_cures, list( // Mix the symptoms of two diseases (the src and the argument) /datum/disease/advance/proc/Mix(datum/disease/advance/D) if(!(IsSame(D))) - var/list/possible_symptoms = shuffle(D.symptoms) + var/list/possible_symptoms = list() + // Wild viruses are less predictable when mixed + if(D.event || event) + possible_symptoms = shuffle(D.symptoms + D.GenerateSymptoms(1, 6, 3)) + else + possible_symptoms = shuffle(D.symptoms) for(var/datum/symptom/S in possible_symptoms) AddSymptom(new S.type) @@ -177,55 +287,63 @@ GLOBAL_LIST_INIT(advance_cures, list( return generated -/datum/disease/advance/proc/Refresh(new_name = FALSE, archive = FALSE) +/// Called after changes are made to a disease to apply them properly +/datum/disease/advance/proc/Refresh(new_name = FALSE, archive = FALSE, new_cure = TRUE, new_strain = TRUE) + if(new_strain) + strain = "adv_[num2text(GLOB.next_unique_strain++, 8)]" + evolution_chance = VIRUS_EVOLUTION_CHANCE var/list/properties = GenerateProperties() - AssignProperties(properties) + AssignProperties(properties, new_cure) id = null if(!GLOB.archive_diseases[GetDiseaseID()]) if(new_name) AssignName() GLOB.archive_diseases[GetDiseaseID()] = src // So we don't infinite loop - GLOB.archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) + GLOB.archive_diseases[GetDiseaseID()] = new /datum/disease/advance(src) var/datum/disease/advance/A = GLOB.archive_diseases[GetDiseaseID()] AssignName(A.name) -//Generate disease properties based on the effects. Returns an associated list. +/// Generate disease properties based on the symptoms and base properties. Returns an associated list. /datum/disease/advance/proc/GenerateProperties() if(!symptoms || !length(symptoms)) CRASH("We did not have any symptoms before generating properties.") - var/list/properties = list("resistance" = 1, "stealth" = 0, "stage rate" = 1, "transmittable" = 1, "severity" = 0) - + var/list/properties = base_properties.Copy() for(var/datum/symptom/S in symptoms) - properties["resistance"] += S.resistance properties["stealth"] += S.stealth properties["stage rate"] += S.stage_speed - properties["transmittable"] += S.transmittable + properties["transmissibility"] += S.transmissibility properties["severity"] = max(properties["severity"], S.severity) // severity is based on the highest severity symptom return properties -// Assign the properties that are in the list. -/datum/disease/advance/proc/AssignProperties(list/properties = list()) - +/// Set the characteristics of the disease depending on the received properties +/datum/disease/advance/proc/AssignProperties(list/properties = list(), new_cure = TRUE) if(properties && length(properties)) switch(properties["stealth"]) - if(2) - visibility_flags = HIDDEN_SCANNER - if(3 to INFINITY) - visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC + if(0 to 2) + visibility_flags = 0 + if(2 to INFINITY) + visibility_flags = VIRUS_HIDDEN_SCANNER - // The more symptoms we have, the less transmittable it is but some symptoms can make up for it. - SetSpread(clamp(2 ** (properties["transmittable"] - length(symptoms)), BLOOD, AIRBORNE)) - permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1) - cure_chance = 15 - clamp(properties["resistance"], -5, 5) // can be between 10 and 20 - stage_prob = max(properties["stage rate"], 2) + // The more symptoms we have, the less transmissibility it is but some symptoms can make up for it. + SetSpread(clamp(2 ** (properties["transmissibility"] - length(symptoms)), SPREAD_BLOOD, SPREAD_AIRBORNE)) + permeability_mod = max(CEILING(0.4 * properties["transmissibility"], 1), 1) + cure_chance = 20 - clamp(properties["resistance"], -5, 5) // can be between 10 and 20 + // The amount of cures needed to cure the disease. clamped between 1 and 6 because we generate 6 possible cures + cures_required = round(max(2.2 * (1.1 ** properties["resistance"]), 1)) + // 9 stage rate is twice as fast as 0 stage rate, -9 stage rate is half as fast as 0. + stage_prob = 4 * (1.08 ** properties["stage rate"]) SetSeverity(properties["severity"]) - GenerateCure(properties) + // Calculate evolution chance, unless stabilized with stabilizing agar + if(evolution_chance) + evolution_chance = VIRUS_EVOLUTION_CHANCE * (1 + sqrtor0(properties["stage rate"]) / 6) + if(new_cure) + GenerateCure(properties) else CRASH("Our properties were empty or null!") @@ -233,13 +351,13 @@ GLOBAL_LIST_INIT(advance_cures, list( // Assign the spread type and give it the correct description. /datum/disease/advance/proc/SetSpread(spread_id) switch(spread_id) - if(NON_CONTAGIOUS, SPECIAL) + if(SPREAD_NON_CONTAGIOUS, SPREAD_SPECIAL) spread_text = "Non-contagious" - if(CONTACT_GENERAL, CONTACT_HANDS, CONTACT_FEET) + if(SPREAD_CONTACT_GENERAL, SPREAD_CONTACT_HANDS, SPREAD_CONTACT_FEET) spread_text = "On contact" - if(AIRBORNE) + if(SPREAD_AIRBORNE) spread_text = "Airborne" - if(BLOOD) + if(SPREAD_BLOOD) spread_text = "Blood" spread_flags = spread_id @@ -249,33 +367,57 @@ GLOBAL_LIST_INIT(advance_cures, list( switch(level_sev) if(-INFINITY to 0) - severity = NONTHREAT + severity = VIRUS_NONTHREAT if(1) - severity = MINOR + severity = VIRUS_MINOR if(2) - severity = MEDIUM + severity = VIRUS_MEDIUM if(3) - severity = HARMFUL + severity = VIRUS_HARMFUL if(4) - severity = DANGEROUS + severity = VIRUS_DANGEROUS if(5 to INFINITY) - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD else severity = "Unknown" +/datum/disease/advance/has_cure() + if(!(disease_flags & VIRUS_CURABLE)) + return 0 + + var/cures_found = 0 + for(var/C_id in cures) + if(C_id == "ethanol") + for(var/datum/reagent/consumable/ethanol/booze in affected_mob.reagents.reagent_list) + cures_found++ + break + else if(affected_mob.reagents.has_reagent(C_id)) + cures_found++ + + return cures_found >= cures_required + +/datum/disease/advance/proc/cure_pick(list/curelist = list()) + var/list/options = curelist - cures + return pick(options) // Will generate a random cure, the less resistance the symptoms have, the harder the cure. /datum/disease/advance/proc/GenerateCure(list/properties = list()) if(properties && length(properties)) - var/res = clamp(properties["resistance"] - (length(symptoms) / 2), 1, length(GLOB.advance_cures)) -// to_chat(world, "Res = [res]") - cures = list(GLOB.advance_cures[res]) - - // Get the cure name from the cure_id - var/datum/reagent/D = GLOB.chemical_reagents_list[cures[1]] - cure_text = D.name - - + cures = list() + cure_text = "" + cures += pick(GLOB.standard_cures) + for(var/i in 1 to cures_required + 2) + switch(pick(40;1, 40;2, 20;3)) + if(1) + cures += cure_pick(GLOB.advanced_cures) + if(2) + cures += cure_pick(GLOB.plant_cures) + if(3) + cures += cure_pick(GLOB.drinks) + for(var/cure in cures) + // Get the cure name from the cure_id + var/datum/reagent/D = GLOB.chemical_reagents_list[cure] + cure_text += cure_text == "" ? "[D.name]" : ", [D.name]" return // Randomly generate a symptom, has a chance to lose or gain a symptom. @@ -296,8 +438,11 @@ GLOBAL_LIST_INIT(advance_cures, list( return // Name the disease. -/datum/disease/advance/proc/AssignName(name = "Unknown") - src.name = name +/datum/disease/advance/proc/AssignName(_name = "Unknown") + name = _name + if(GLOB.archive_diseases[GetDiseaseID()]) + var/datum/disease/advance/virus = GLOB.archive_diseases[GetDiseaseID()] + virus.name = _name return // Return a unique ID of the disease. @@ -306,7 +451,8 @@ GLOBAL_LIST_INIT(advance_cures, list( var/list/L = list() for(var/datum/symptom/S in symptoms) L += S.id - L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in. + L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in and add the strain to the end + L += strain var/result = jointext(L, ":") id = result return id @@ -326,11 +472,29 @@ GLOBAL_LIST_INIT(advance_cures, list( symptoms += S return +/datum/disease/advance/proc/has_symptom_path(symptom_path) + for(var/datum/symptom/current_symptom in symptoms) + if(current_symptom.type == symptom_path) + return TRUE + return FALSE + +/datum/disease/advance/proc/add_symptom_path(symptom_path) + if(has_symptom_path(symptom_path)) + return + if(length(symptoms) < VIRUS_SYMPTOM_LIMIT) + symptoms += new symptom_path + // Simply removes the symptom. /datum/disease/advance/proc/RemoveSymptom(datum/symptom/S) symptoms -= S return +/datum/disease/advance/proc/clear_symptoms() + for(var/datum/symptom/current_symptom in symptoms) + symptoms -= current_symptom + qdel(current_symptom) + + /* Static Procs @@ -362,7 +526,9 @@ GLOBAL_LIST_INIT(advance_cures, list( diseases -= D1 var/datum/disease/advance/D2 = pick(diseases) - D2.Mix(D1) + // So that we don't mix a virus with itself + if(D2.GetDiseaseID() != D1.GetDiseaseID()) + D2.Mix(D1) // Should be only 1 entry left, but if not let's only return a single entry // to_chat(world, "END MIXING!!!!!") @@ -385,78 +551,113 @@ GLOBAL_LIST_INIT(advance_cures, list( if(!user) return + var/datum/disease/advance/admin_disease = new(_event = TRUE) + + var/base_props = list("resistance" = 1, "stealth" = 0, "stage rate" = 1, "transmissibility" = 1) + + for(var/prop in base_props) + var/current_prop = input(user, "Enter base [prop]", "Base Stats", null) + if(current_prop) + admin_disease.base_properties[prop] = text2num(current_prop) + var/i = VIRUS_SYMPTOM_LIMIT - var/datum/disease/advance/D = new(0, null) - D.symptoms = list() + admin_disease.symptoms = list() var/list/symptoms = list() symptoms += "Done" symptoms += GLOB.list_symptoms.Copy() do if(user) - var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in symptoms + var/symptom = tgui_input_list(user, "Choose a Symptom", "Choose a symptom to add ([i] remaining)", symptoms) if(isnull(symptom)) return else if(istext(symptom)) i = 0 else if(ispath(symptom)) var/datum/symptom/S = new symptom - if(!D.HasSymptom(S)) - D.symptoms += S + if(!admin_disease.HasSymptom(S)) + admin_disease.symptoms += S i -= 1 while(i > 0) - if(length(D.symptoms) > 0) + if(length(admin_disease.symptoms) > 0) var/new_name = stripped_input(user, "Name your new disease.", "New Name") if(!new_name) return - D.AssignName(new_name) - D.Refresh() - - for(var/datum/disease/advance/AD in GLOB.active_diseases) - AD.Refresh() - - for(var/thing in shuffle(GLOB.human_list)) - var/mob/living/carbon/human/H = thing - if(H.stat == DEAD || !is_station_level(H.z)) - continue - if(!H.HasDisease(D)) - H.ForceContractDisease(D) - break - - var/list/name_symptoms = list() - for(var/datum/symptom/S in D.symptoms) - name_symptoms += S.name - message_admins("[key_name_admin(user)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") - + admin_disease.AssignName(new_name) + admin_disease.Refresh(FALSE, TRUE, TRUE, TRUE) + return admin_disease /datum/disease/advance/proc/totalStageSpeed() - var/total_stage_speed = 0 + var/total_stage_speed = base_properties["stage rate"] for(var/i in symptoms) var/datum/symptom/S = i total_stage_speed += S.stage_speed return total_stage_speed /datum/disease/advance/proc/totalStealth() - var/total_stealth = 0 + var/total_stealth = base_properties["stealth"] for(var/i in symptoms) var/datum/symptom/S = i total_stealth += S.stealth return total_stealth /datum/disease/advance/proc/totalResistance() - var/total_resistance = 0 + var/total_resistance = base_properties["resistance"] for(var/i in symptoms) var/datum/symptom/S = i total_resistance += S.resistance return total_resistance -/datum/disease/advance/proc/totalTransmittable() - var/total_transmittable = 0 +/datum/disease/advance/proc/totaltransmissibility() + var/total_transmissibility = base_properties["transmissibility"] for(var/i in symptoms) var/datum/symptom/S = i - total_transmittable += S.transmittable - return total_transmittable + total_transmissibility += S.transmissibility + return total_transmissibility + +/datum/disease/advance/get_required_cures() + return cures_required + +/datum/disease/advance/is_stabilized() + return !evolution_chance + +/datum/disease/advance/get_tracker() + return tracker + +/datum/disease/advance/get_ui_id() + return id + +/datum/disease/advance/get_strain_id() + return strain + +/datum/disease/advance/get_full_strain_id() + return GetDiseaseID() + +/datum/disease/advance/is_known(z) + return (GetDiseaseID() in GLOB.known_advanced_diseases["[z]"]) + +/datum/disease/advance/get_pandemic_symptoms(z) + . = list() + var/disease_known = is_known(z) + for(var/datum/symptom/virus_symptom in symptoms) + var/known = disease_known || (virus_symptom.name in GLOB.detected_advanced_diseases["[z]"][GetDiseaseID()]["known_symptoms"]) + . += list(list( + "name" = known ? virus_symptom.name : "UNKNOWN", + "stealth" = known ? virus_symptom.stealth : "UNKNOWN", + "resistance" = known ? virus_symptom.resistance : "UNKNOWN", + "stageSpeed" = known ? virus_symptom.stage_speed : "UNKNOWN", + "transmissibility" = known ? virus_symptom.transmissibility : "UNKNOWN", + "complexity" = known ? virus_symptom.level : "UNKNOWN", + )) + +/datum/disease/advance/get_pandemic_base_stats() + return list( + "resistance" = base_properties["resistance"], + "stageSpeed" = base_properties["stage rate"], + "stealth" = base_properties["stealth"], + "transmissibility" = base_properties["transmissibility"], + ) diff --git a/code/datums/diseases/advance/disease_presets.dm b/code/datums/diseases/advance/disease_presets.dm index 0cf111358c9..f654980b6c1 100644 --- a/code/datums/diseases/advance/disease_presets.dm +++ b/code/datums/diseases/advance/disease_presets.dm @@ -1,51 +1,51 @@ // Cold -/datum/disease/advance/cold/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/cold/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Cold" symptoms = list(new/datum/symptom/sneeze) - ..(process, D, copy) + ..() // Flu -/datum/disease/advance/flu/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/flu/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Flu" symptoms = list(new/datum/symptom/cough) - ..(process, D, copy) + ..() // Voice Changing -/datum/disease/advance/voice_change/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/voice_change/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Epiglottis Mutation" symptoms = list(new/datum/symptom/voice_change) - ..(process, D, copy) + ..() // Toxin Filter -/datum/disease/advance/heal/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/heal/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Liver Enhancer" symptoms = list(new/datum/symptom/heal) - ..(process, D, copy) + ..() // Hullucigen -/datum/disease/advance/hullucigen/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/hullucigen/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Reality Impairment" symptoms = list(new/datum/symptom/hallucigen) - ..(process, D, copy) + ..() // Sensory Restoration -/datum/disease/advance/sensory_restoration/New(process = 1, datum/disease/advance/D, copy = 0) - if(!D) +/datum/disease/advance/sensory_restoration/New(datum/disease/advance/to_copy, _event = FALSE, copy_stage = FALSE) + if(!to_copy) name = "Reality Enhancer" symptoms = list(new/datum/symptom/sensory_restoration) - ..(process, D, copy) + ..() diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm index b3eaaba83cc..048dcff0fec 100644 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ b/code/datums/diseases/advance/symptoms/beard.dm @@ -17,36 +17,32 @@ BONUS /datum/symptom/beard name = "Facial Hypertrichosis" - stealth = -3 - resistance = -1 - stage_speed = -3 - transmittable = -1 + stealth = -1 + stage_speed = 2 + transmissibility = -1 level = 4 severity = 1 -/datum/symptom/beard/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/head/head_organ = H.get_organ("head") - if(!istype(head_organ)) - return - switch(A.stage) - if(1, 2) - to_chat(H, "Your chin itches.") - if(head_organ.f_style == "Shaved") - head_organ.f_style = "Jensen Beard" - H.update_fhair() - if(3, 4) - to_chat(H, "You feel tough.") - if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard") && !(head_organ.f_style == "Full Beard")) - head_organ.f_style = "Full Beard" - H.update_fhair() - else - to_chat(H, "You feel manly!") - if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard")) - head_organ.f_style = pick("Dwarf Beard", "Very Long Beard") - H.update_fhair() +/datum/symptom/beard/symptom_act(datum/disease/advance/A, unmitigated) + if(ishuman(A.affected_mob)) + var/mob/living/carbon/human/H = A.affected_mob + var/obj/item/organ/external/head/head_organ = H.get_organ("head") + if(!istype(head_organ)) + return + switch(A.stage) + if(1, 2) + to_chat(H, "Your chin itches.") + if(head_organ.f_style == "Shaved") + head_organ.f_style = "Jensen Beard" + H.update_fhair() + if(3, 4) + to_chat(H, "You feel tough.") + if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard") && !(head_organ.f_style == "Full Beard")) + head_organ.f_style = "Full Beard" + H.update_fhair() + else + to_chat(H, "You feel manly!") + if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard")) + head_organ.f_style = pick("Dwarf Beard", "Very Long Beard") + H.update_fhair() return diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index f0a3eaa9ce0..e5e6b84ab02 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -19,35 +19,33 @@ Bonus name = "Choking" stealth = -3 - resistance = -2 stage_speed = -2 - transmittable = -4 + transmissibility = -4 level = 3 severity = 3 + chem_treatments = list( + "perfluorodecalin" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/choking/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2) - to_chat(M, "[pick("You're having difficulty breathing.", "Your breathing becomes heavy.")]") - if(3, 4) - to_chat(M, "[pick("Your windpipe feels like a straw.", "Your breathing becomes tremendously difficult.")]") - Choke_stage_3_4(M, A) - M.emote("gasp") - else - to_chat(M, "[pick("You're choking!", "You can't breathe!")]") - Choke(M, A) - M.emote("gasp") +/datum/symptom/choking/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2) + to_chat(M, "[pick("You're having difficulty breathing.", "Your breathing becomes heavy.")]") + if(3, 4) + to_chat(M, "[pick("Your windpipe feels like a straw.", "Your breathing becomes tremendously difficult.")]") + Choke(M, A, unmitigated) + M.emote("gasp") + else + to_chat(M, "[pick("You're choking!", "You can't breathe!")]") + Choke(M, A) + M.emote("gasp") return -/datum/symptom/choking/proc/Choke_stage_3_4(mob/living/M, datum/disease/advance/A) - var/get_damage = sqrtor0(21+A.totalStageSpeed()*0.5)+sqrtor0(16+A.totalStealth()) - M.adjustOxyLoss(get_damage) - return 1 - -/datum/symptom/choking/proc/Choke(mob/living/M, datum/disease/advance/A) - var/get_damage = sqrtor0(21+A.totalStageSpeed()*0.5)+sqrtor0(16+A.totalStealth()*5) +/datum/symptom/choking/proc/Choke(mob/living/M, datum/disease/advance/A, unmitigated) + var/get_damage = unmitigated + if(A.stage < 5) + get_damage *= sqrtor0(21 + A.totalStageSpeed() * 0.5)+sqrtor0(16+A.totalStealth()) + else + get_damage *= sqrtor0(21 + A.totalStageSpeed() * 0.5)+sqrtor0(16+A.totalStealth() * 5) M.adjustOxyLoss(get_damage) return 1 diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index fa0733bc0dd..d68b38defc1 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -6,7 +6,7 @@ Confusion Little bit hidden. Lowers resistance. Decreases stage speed. - Not very transmittable. + Not very transmissibility. Intense Level. Bonus @@ -19,21 +19,25 @@ Bonus name = "Confusion" stealth = 1 - resistance = -1 + resistance = 1 stage_speed = -3 level = 4 severity = 2 + chem_treatments = list( + "synaptazine" = list("multiplier" = 0, "timer" = 0), + "ephedrine" = list("multiplier" = 0, "timer" = 0), + "methamphetamine" = list("multiplier" = 0, "timer" = 0), + "coffee" = list("multiplier" = 0.7, "timer" = 0), + "tea"= list("multiplier" = 0.7, "timer" = 0)) -/datum/symptom/confusion/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("Your head hurts.", "Your mind blanks for a moment.")]") - else - to_chat(M, "You can't think straight!") - M.AdjustConfused(16 SECONDS, bound_lower = 0, bound_upper = 200 SECONDS) +/datum/symptom/confusion/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + if(prob(A.progress ** 2)) + to_chat(M, "You can't think straight!") + M.AdjustConfused(16 SECONDS * unmitigated, bound_lower = 0, bound_upper = 200 SECONDS) + + else + to_chat(M, "[pick("Your head hurts.", "Your mind blanks for a moment.")]") return diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index 5f495718fff..e2613992bd3 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -6,7 +6,7 @@ Coughing Noticable. Little Resistance. Doesn't increase stage speed much. - Transmittable. + transmissibility. Low Level. BONUS @@ -18,23 +18,24 @@ BONUS /datum/symptom/cough name = "Cough" - stealth = -1 - resistance = 3 + stealth = 2 stage_speed = 1 - transmittable = 2 + transmissibility = 2 level = 1 severity = 1 + chem_treatments = list( + "salbutamol" = list("multiplier" = 0, "timer" = 0), + "perfluorodecalin" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/cough/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) - to_chat(M, "[pick("You swallow excess mucus.", "You lightly cough.")]") - else - M.emote("cough") - var/obj/item/I = M.get_active_hand() - if(I && I.w_class == 1) - M.drop_item() +/datum/symptom/cough/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(prob(A.progress + 20)) + M.emote("cough") + var/obj/item/I = M.get_active_hand() + if(prob(A.progress * unmitigated) && I && I.w_class == 1) + M.drop_item() + // smaller spread than sneeze + A.spread(3 * unmitigated) + else + to_chat(M, "[pick("You swallow excess mucus.", "You lightly cough.")]") return diff --git a/code/datums/diseases/advance/symptoms/damage_converter.dm b/code/datums/diseases/advance/symptoms/damage_converter.dm index 9d8c47451f8..13d24137dc5 100644 --- a/code/datums/diseases/advance/symptoms/damage_converter.dm +++ b/code/datums/diseases/advance/symptoms/damage_converter.dm @@ -21,16 +21,15 @@ Bonus stealth = 1 resistance = -4 stage_speed = -4 - transmittable = -2 + transmissibility = -2 level = 4 + activation_prob = SYMPTOM_ACTIVATION_PROB * 10 -/datum/symptom/damage_converter/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 10)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - Convert(M) +/datum/symptom/damage_converter/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + Convert(M) return /datum/symptom/damage_converter/proc/Convert(mob/living/M) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index 6e08a7b0ad4..9c06171cf52 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -17,23 +17,22 @@ Bonus /datum/symptom/deafness name = "Deafness" - stealth = -1 - resistance = -2 + stealth = 1 stage_speed = -1 - transmittable = -3 + transmissibility = -3 level = 4 severity = 3 + chem_treatments = list( + "oculine" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/deafness/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - var/obj/item/organ/internal/ears/ears = M.get_organ_slot("ears") - if(!ears) - return //cutting off your ears to cure the deafness: the ultimate own - switch(A.stage) - if(3, 4) - to_chat(M, "[pick("You hear a ringing in your ear.", "Your ears pop.")]") - if(5) - to_chat(M, "Your ears pop and begin ringing loudly!") - M.Deaf(40 SECONDS) +/datum/symptom/deafness/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + var/obj/item/organ/internal/ears/ears = M.get_organ_slot("ears") + if(!ears) + return //cutting off your ears to cure the deafness: the ultimate own + switch(A.stage) + if(3, 4) + to_chat(M, "[pick("You hear a ringing in your ear.", "Your ears pop.")]") + if(5) + to_chat(M, "Your ears pop and begin ringing loudly!") + M.Deaf(40 SECONDS * unmitigated) diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index ede04d3da1f..9151f169ed1 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -20,20 +20,20 @@ Bonus name = "Dizziness" stealth = 2 - resistance = -2 - stage_speed = -3 - transmittable = -1 + stage_speed = -1 + transmissibility = -1 level = 4 severity = 2 + chem_treatments = list( + "antihol" = list("multiplier" = 0, "timer" = 0), + "ephedrine" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/dizzy/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel dizzy.", "Your head spins.")]") - else - to_chat(M, "A wave of dizziness washes over you!") - M.Dizzy(10 SECONDS) +/datum/symptom/dizzy/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You feel dizzy.", "Your head spins.")]") + else + to_chat(M, "A wave of dizziness washes over you!") + M.Dizzy(10 SECONDS * unmitigated) return diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index 845ff9f5abc..770fe993061 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -6,7 +6,7 @@ Fever No change to hidden. Increases resistance. Increases stage speed. - Little transmittable. + Little transmissibility. Low level. Bonus @@ -18,23 +18,21 @@ Bonus /datum/symptom/fever name = "Fever" - resistance = 3 + stealth = 2 + resistance = 1 stage_speed = 3 - transmittable = 2 + transmissibility = 2 level = 2 severity = 2 -/datum/symptom/fever/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - to_chat(M, "[pick("You feel hot.", "You feel like you're burning.")]") - if(M.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) - Heat(M, A) - +/datum/symptom/fever/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + to_chat(M, "[pick("You feel hot.", "You feel like you're burning.")]") + if(M.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) + Heat(M, A, unmitigated) return -/datum/symptom/fever/proc/Heat(mob/living/M, datum/disease/advance/A) - var/get_heat = (sqrtor0(21+A.totalTransmittable()*2))+(sqrtor0(20+A.totalStageSpeed()*3)) +/datum/symptom/fever/proc/Heat(mob/living/M, datum/disease/advance/A, unmitigated) + var/get_heat = unmitigated * ((sqrtor0(21 + A.totaltransmissibility() * 2)) + (sqrtor0(20 + A.totalStageSpeed() * 3))) M.bodytemperature = min(M.bodytemperature + (get_heat * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) return 1 diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm index b88f04d1eea..93c05935cb0 100644 --- a/code/datums/diseases/advance/symptoms/fire.dm +++ b/code/datums/diseases/advance/symptoms/fire.dm @@ -18,40 +18,42 @@ Bonus /datum/symptom/fire name = "Spontaneous Combustion" - stealth = 1 - resistance = -4 - stage_speed = -4 - transmittable = -4 + stealth = -3 + resistance = 3 + stage_speed = 1 + transmissibility = -4 level = 6 - severity = 5 + severity = 6 + chem_treatments = list( + "frostoil" = list("multiplier" = 0, "timer" = 0), + "menthol" = list("multiplier" = 0, "timer" = 0), + "water" = list("multiplier" = 0.82, "timer" = 0)) -/datum/symptom/fire/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(3) - to_chat(M, "[pick("You feel hot.", "You hear a crackling noise.", "You smell smoke.")]") - if(4) - Firestacks_stage_4(M, A) - M.IgniteMob() - to_chat(M, "Your skin bursts into flames!") - M.emote("scream") - if(5) - Firestacks_stage_5(M, A) - M.IgniteMob() - to_chat(M, "Your skin erupts into an inferno!") - M.emote("scream") +/datum/symptom/fire/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.progress) + if(30 to 59) + to_chat(M, "[pick("You feel hot.", "You hear a crackling noise.", "You smell smoke.")]") + if(60 to 79) + Firestacks(M, A, unmitigated) + M.IgniteMob() + to_chat(M, "Your skin bursts into flames!") + M.emote("scream") + if(80 to INFINITY) + Firestacks(M, A, unmitigated) + M.IgniteMob() + to_chat(M, "Your skin erupts into an inferno!") + M.emote("scream") return -/datum/symptom/fire/proc/Firestacks_stage_4(mob/living/M, datum/disease/advance/A) - var/get_stacks = max((sqrtor0(20 + A.totalStageSpeed() * 2)) - (sqrtor0(16 + A.totalStealth())), 1) - M.adjust_fire_stacks(get_stacks) - M.adjustFireLoss(get_stacks * 0.5) - return 1 +/datum/symptom/fire/proc/Firestacks(mob/living/M, datum/disease/advance/A, unmitigated) + var/get_stacks = unmitigated + if(A.stage < 5) + get_stacks *= ((A.progress / 100) ** 2) * max(sqrtor0(5 + A.totalStageSpeed() * 2), 1) + M.adjustFireLoss(get_stacks * 2) + else + get_stacks *= ((A.progress / 100) ** 2) * max(sqrtor0(10 + A.totalStageSpeed() * 3), 1) + M.adjustFireLoss(get_stacks * 4) -/datum/symptom/fire/proc/Firestacks_stage_5(mob/living/M, datum/disease/advance/A) - var/get_stacks = max((sqrtor0(20 + A.totalStageSpeed() * 3))-(sqrtor0(16 + A.totalStealth())), 1) M.adjust_fire_stacks(get_stacks) - M.adjustFireLoss(get_stacks) return 1 diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 9de5ce72b3c..48c81d4aa79 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -18,25 +18,27 @@ Bonus /datum/symptom/flesh_eating name = "Necrotizing Fasciitis" - stealth = -3 - resistance = -4 - transmittable = -4 + stealth = -4 + resistance = 3 + transmissibility = -3 level = 6 - severity = 5 + severity = 6 + chem_treatments = list( + "synthflesh" = list("multiplier" = 0, "timer" = 0), + "lazarus_reagent" = list("multiplier" = 0, "timer" = 0)) + +/datum/symptom/flesh_eating/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(A.stage > 1) + if(prob(A.progress)) + to_chat(M, "[pick("You cringe as a violent pain takes over your body.", "It feels like your body is eating itself inside out.", "IT HURTS.")]") + Flesheat(M, A) + else + to_chat(M, "[pick("You feel a sudden pain across your body.", "Drops of blood appear suddenly on your skin.")]") -/datum/symptom/flesh_eating/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(2,3) - to_chat(M, "[pick("You feel a sudden pain across your body.", "Drops of blood appear suddenly on your skin.")]") - if(4,5) - to_chat(M, "[pick("You cringe as a violent pain takes over your body.", "It feels like your body is eating itself inside out.", "IT HURTS.")]") - Flesheat(M, A) return /datum/symptom/flesh_eating/proc/Flesheat(mob/living/M, datum/disease/advance/A) - var/get_damage = ((sqrtor0(16-A.totalStealth()))*5) + var/get_damage = (A.progress / 100) * ((sqrtor0(49 + 2 * A.totalStageSpeed()))*5) M.adjustBruteLoss(get_damage) return 1 diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm index 60419bd5b25..c6bcf6434c0 100644 --- a/code/datums/diseases/advance/symptoms/hair.dm +++ b/code/datums/diseases/advance/symptoms/hair.dm @@ -16,34 +16,32 @@ BONUS /datum/symptom/hair name = "Cranial Hypertrichosis" - stealth = -3 + stealth = -1 resistance = -1 - stage_speed = -3 - transmittable = -1 + stage_speed = 1 + transmissibility = 1 level = 4 severity = 1 -/datum/symptom/hair/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - if(!ishuman(A.affected_mob)) - return - var/mob/living/carbon/human/H = A.affected_mob - if(H.dna.species.bodyflags & BALD) - return - var/obj/item/organ/external/head/head_organ = H.get_organ("head") - if(!istype(head_organ)) - return - switch(A.stage) - if(1, 2, 3) - to_chat(H, "Your scalp itches.") - head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name) - else - to_chat(H, "Hair bursts forth from your scalp!") - var/datum/sprite_accessory/tmp_hair_style = GLOB.hair_styles_full_list["Very Long Hair"] +/datum/symptom/hair/symptom_act(datum/disease/advance/A, unmitigated) + if(!ishuman(A.affected_mob)) + return + var/mob/living/carbon/human/H = A.affected_mob + if(H.dna.species.bodyflags & BALD) + return + var/obj/item/organ/external/head/head_organ = H.get_organ("head") + if(!istype(head_organ)) + return + switch(A.stage) + if(1, 2, 3) + to_chat(H, "Your scalp itches.") + head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name) + else + to_chat(H, "Hair bursts forth from your scalp!") + var/datum/sprite_accessory/tmp_hair_style = GLOB.hair_styles_full_list["Very Long Hair"] - if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them. - head_organ.h_style = "Very Long Hair" - else //Otherwise, give them a random hair style. - head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name) - H.update_hair() + if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them. + head_organ.h_style = "Very Long Hair" + else //Otherwise, give them a random hair style. + head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name) + H.update_hair() diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index 15506312836..3d2e63c7602 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -6,7 +6,7 @@ Hallucigen Very noticable. Lowers resistance considerably. Decreases stage speed. - Reduced transmittable. + Reduced transmissibility. Critical Level. Bonus @@ -19,23 +19,23 @@ Bonus name = "Hallucigen" stealth = -2 - resistance = -3 - stage_speed = -3 - transmittable = -1 + stage_speed = -4 + transmissibility = 1 level = 5 severity = 3 + chem_treatments = list( + "synaptazine" = list("multiplier" = 0, "timer" = 0), + "ephedrine" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/hallucigen/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2) - to_chat(M, "[pick("Something appears in your peripheral vision, then winks out.", "You hear a faint whisper with no source.", "Your head aches.")]") - if(3, 4) - to_chat(M, "[pick("Something is following you.", "You are being watched.", "You hear a whisper in your ear.", "Thumping footsteps slam toward you from nowhere.")]") - else - to_chat(M, "[pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something in the shadows...")]") - M.AdjustHallucinate(5 SECONDS) +/datum/symptom/hallucigen/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + switch(A.stage) + if(1, 2) + to_chat(M, "[pick("Something appears in your peripheral vision, then winks out.", "You hear a faint whisper with no source.", "Your head aches.")]") + if(3, 4) + to_chat(M, "[pick("Something is following you.", "You are being watched.", "You hear a whisper in your ear.", "Thumping footsteps slam toward you from nowhere.")]") + else + to_chat(M, "[pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something in the shadows...")]") + M.AdjustHallucinate(5 SECONDS) return diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 769abc9e987..4f7b0ad932c 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -6,7 +6,7 @@ Headache Noticable. Highly resistant. Increases stage speed. - Not transmittable. + Not transmissibility. Low Level. BONUS @@ -19,15 +19,17 @@ BONUS /datum/symptom/headache name = "Headache" - stealth = -1 - resistance = 4 - stage_speed = 2 + stealth = 2 + transmissibility = 3 level = 1 severity = 1 + chem_treatments = list( + "hydrocodone" = list("multiplier" = 0, "timer" = 0), + "morphine" = list("multiplier" = 0, "timer" = 0), + "sal_acid" = list("multiplier" = 0, "timer" = 0), + "tea" = list("multiplier" = 0.5, "timer" = 0)) -/datum/symptom/headache/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") +/datum/symptom/headache/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") return diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index c52c7df54b6..d3cb4f43ae9 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -21,21 +21,22 @@ Bonus stealth = 1 resistance = -4 stage_speed = -4 - transmittable = -4 + transmissibility = -4 level = 6 + chem_treatments = list( + "frostoil" = list("multiplier" = 0.5, "timer" = 0)) + activation_prob = SYMPTOM_ACTIVATION_PROB * 10 -/datum/symptom/heal/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 10)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - Heal(M, A) +/datum/symptom/heal/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + Heal(M, A, unmitigated) return -/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A) +/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A, unmitigated) var/get_damage = (sqrtor0(20+A.totalStageSpeed())*(1+rand())) - M.adjustToxLoss(-get_damage) + M.adjustToxLoss(-get_damage * unmitigated) return 1 /* @@ -55,6 +56,7 @@ Bonus ////////////////////////////////////// */ +/* /datum/symptom/heal/metabolism name = "Anti-Bodies Metabolism" @@ -87,6 +89,7 @@ Bonus M.resistances -= res to_chat(M, "You feel weaker.") +*/ /* ////////////////////////////////////// @@ -110,7 +113,7 @@ Bonus stealth = 3 resistance = 4 stage_speed = 4 - transmittable = 4 + transmissibility = 4 level = 3 var/longevity = 30 @@ -146,7 +149,7 @@ Bonus stealth = -1 resistance = -1 stage_speed = 0 - transmittable = -3 + transmissibility = -3 level = 5 /datum/symptom/heal/dna/Heal(mob/living/carbon/M, datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 6d62a443210..4d12932eeea 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -6,7 +6,7 @@ Itching Not noticable or unnoticable. Resistant. Increases stage speed. - Little transmittable. + Little transmissibility. Low Level. BONUS @@ -19,15 +19,16 @@ BONUS /datum/symptom/itching name = "Itching" - resistance = 3 + stealth = 2 + resistance = -1 stage_speed = 3 - transmittable = 1 + transmissibility = 1 level = 1 severity = 1 + chem_treatments = list( + "silver_sulfadiazine" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/itching/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") +/datum/symptom/itching/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") return diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm index fbe43efe554..3e628d6eaab 100644 --- a/code/datums/diseases/advance/symptoms/oxygen.dm +++ b/code/datums/diseases/advance/symptoms/oxygen.dm @@ -21,18 +21,18 @@ Bonus stealth = 1 resistance = -3 stage_speed = -3 - transmittable = -4 + transmissibility = -4 level = 6 + chem_treatments = list( + "cyanide" = list("multiplier" = 0, "timer" = 0)) + activation_prob = SYMPTOM_ACTIVATION_PROB * 5 -/datum/symptom/oxygen/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - if(M.reagents.get_reagent_amount("salbutamol") < 20) - M.reagents.add_reagent("salbutamol", 20) - else - if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - to_chat(M, "[pick("Your lungs feel great.", "You realize you haven't been breathing.", "You don't feel the need to breathe.")]") +/datum/symptom/oxygen/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + if(M.reagents.get_reagent_amount("salbutamol") < 20) + M.reagents.add_reagent("salbutamol", 20) + else + to_chat(M, "[pick("Your lungs feel great.", "You realize you haven't been breathing.", "You don't feel the need to breathe.")]") return diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 0945ce9f5d1..42f88702263 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -20,45 +20,43 @@ Bonus stealth = -1 resistance = -4 stage_speed = -4 - transmittable = -3 + transmissibility = -3 level = 5 + activation_prob = SYMPTOM_ACTIVATION_PROB * 3 -/datum/symptom/mind_restoration/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 3)) - var/mob/living/M = A.affected_mob - var/datum/reagents/RD = M.reagents +/datum/symptom/mind_restoration/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + var/datum/reagents/RD = M.reagents - if(A.stage >= 3) - M.AdjustSlur(-4 SECONDS) - M.AdjustDrunk(-8 SECONDS) - M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3) - if(A.stage >= 4) - M.AdjustDrowsy(-4 SECONDS) - if(RD.has_reagent("lsd")) - RD.remove_reagent("lsd", 5) - if(RD.has_reagent("histamine")) - RD.remove_reagent("histamine", 5) - M.AdjustHallucinate(-10 SECONDS) - if(A.stage >= 5) - RD.check_and_add("mannitol", 10, 10) + if(A.stage >= 3) + M.AdjustSlur(-4 SECONDS) + M.AdjustDrunk(-8 SECONDS) + M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3) + if(A.stage >= 4) + M.AdjustDrowsy(-4 SECONDS) + if(RD.has_reagent("lsd")) + RD.remove_reagent("lsd", 5) + if(RD.has_reagent("histamine")) + RD.remove_reagent("histamine", 5) + M.AdjustHallucinate(-10 SECONDS) + if(A.stage >= 5) + RD.check_and_add("mannitol", 10, 10) /datum/symptom/sensory_restoration name = "Sensory Restoration" stealth = -1 resistance = -3 stage_speed = -2 - transmittable = -4 + transmissibility = -4 level = 4 + activation_prob = SYMPTOM_ACTIVATION_PROB * 5 -/datum/symptom/sensory_restoration/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - if(M.reagents.get_reagent_amount("oculine") < 20) - M.reagents.add_reagent("oculine", 20) - else - if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your hearing feels more acute.")]") +/datum/symptom/sensory_restoration/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + if(M.reagents.get_reagent_amount("oculine") < 20) + M.reagents.add_reagent("oculine", 20) + else + if(prob(SYMPTOM_ACTIVATION_PROB * 5)) + to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your hearing feels more acute.")]") diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm index 917ccbf14f6..7d3dabb5d8e 100644 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ b/code/datums/diseases/advance/symptoms/shedding.dm @@ -5,7 +5,7 @@ Alopecia Noticable. Decreases resistance slightly. Reduces stage speed slightly. - Transmittable. + transmissibility. Intense Level. BONUS @@ -19,15 +19,11 @@ BONUS name = "Alopecia" stealth = -1 resistance = -1 - stage_speed = -1 - transmittable = 2 + transmissibility = 2 level = 4 severity = 1 -/datum/symptom/shedding/Activate(datum/disease/advance/A) - ..() - if(!prob(SYMPTOM_ACTIVATION_PROB)) - return +/datum/symptom/shedding/symptom_act(datum/disease/advance/A, unmitigated) if(!ishuman(A.affected_mob)) return var/mob/living/carbon/human/H = A.affected_mob diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm index e0cb6a9b563..e33b35d735a 100644 --- a/code/datums/diseases/advance/symptoms/shivering.dm +++ b/code/datums/diseases/advance/symptoms/shivering.dm @@ -6,7 +6,7 @@ Shivering No change to hidden. Increases resistance. Increases stage speed. - Little transmittable. + Little transmissibility. Low level. Bonus @@ -18,19 +18,18 @@ Bonus /datum/symptom/shivering name = "Shivering" - resistance = 2 + stealth = 1 stage_speed = 2 - transmittable = 2 + transmissibility = 3 level = 2 severity = 2 -/datum/symptom/shivering/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - to_chat(M, "[pick("You feel cold.", "You start shivering.")]") - if(M.bodytemperature > BODYTEMP_COLD_DAMAGE_LIMIT) - Chill(M, A) +/datum/symptom/shivering/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + to_chat(M, "[pick("You feel cold.", "You start shivering.")]") + M.emote("shiver") + if(M.bodytemperature > BODYTEMP_COLD_DAMAGE_LIMIT) + Chill(M, A) return /datum/symptom/shivering/proc/Chill(mob/living/M, datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index b5c79f7ff4c..e736af707ea 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -18,25 +18,24 @@ BONUS name = "Vitiligo" stealth = -3 - resistance = -1 - stage_speed = -1 - transmittable = -2 + stage_speed = 1 + transmissibility = 2 level = 4 severity = 1 + chem_treatments = list( + "synthflesh" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/vitiligo/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.s_tone == 85) - return - switch(A.stage) - if(5) - H.change_skin_tone(85, TRUE) - else - H.visible_message("[H] looks a bit pale...", "Your skin suddenly appears lighter...") +/datum/symptom/vitiligo/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.s_tone == 85) + return + switch(A.stage) + if(5) + H.change_skin_tone(85, TRUE) + else + H.visible_message("[H] looks a bit pale...", "Your skin suddenly appears lighter...") return @@ -63,22 +62,22 @@ BONUS stealth = -3 resistance = -1 stage_speed = -1 - transmittable = -2 + transmissibility = -2 level = 4 severity = 1 + chem_treatments = list( + "synthflesh" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/revitiligo/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.s_tone == -85) - return - switch(A.stage) - if(5) - H.change_skin_tone(-85, TRUE) - else - H.visible_message("[H] looks a bit dark...", "Your skin suddenly appears darker...") +/datum/symptom/revitiligo/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.s_tone == -85) + return + switch(A.stage) + if(5) + H.change_skin_tone(-85, TRUE) + else + H.visible_message("[H] looks a bit dark...", "Your skin suddenly appears darker...") return diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 8564360fa94..97e7684538d 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -6,11 +6,11 @@ Sneezing Very Noticable. Increases resistance. Doesn't increase stage speed. - Very transmittable. + Very transmissibility. Low Level. Bonus - Forces a spread type of AIRBORNE + Forces a spread type of SPREAD_AIRBORNE with extra range! ////////////////////////////////////// @@ -20,19 +20,19 @@ Bonus name = "Sneezing" stealth = -2 - resistance = 3 - transmittable = 4 + stage_speed = 2 + transmissibility = 5 level = 1 severity = 1 + chem_treatments = list( + "salbutamol" = list("multiplier" = 0, "timer" = 0), + "perfluorodecalin" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/sneeze/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) - M.emote("sniff") - else - M.emote("sneeze") - A.spread(5) +/datum/symptom/sneeze/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(prob(A.progress + 20)) + M.emote("sneeze") + A.spread(5 * unmitigated) + else + M.emote("sniff") return diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 454f71fc8bf..1c9c5fea8ac 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -8,13 +8,20 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) var/stealth = 0 var/resistance = 0 var/stage_speed = 0 - var/transmittable = 0 - // The type level of the symptom. Higher is harder to generate. + var/transmissibility = 0 + /// The type level of the symptom. Higher is harder to generate. var/level = 0 - // The severity level of the symptom. Higher is more dangerous. + /// The severity level of the symptom. Higher is more dangerous. var/severity = 0 - // The hash tag for our diseases, we will add it up with our other symptoms to get a unique id! ID MUST BE UNIQUE!!! + /// The hash tag for our diseases, we will add it up with our other symptoms to get a unique id! ID MUST BE UNIQUE!!! var/id = "" + /// Asoc list of treatment reagents to multiplier and timer. Multiplier multiplies the frequency and strength of the symptom + var/list/chem_treatments = list() + /// Amount of treatment reagents the symptom will consume + var/purge_amount = 0.4 + /// How likely the symptom is to activate each process cycle + var/activation_prob = SYMPTOM_ACTIVATION_PROB + /datum/symptom/New() var/list/S = GLOB.list_symptoms @@ -32,6 +39,46 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) /datum/symptom/proc/End(datum/disease/advance/A) return +/// Checks the conditions for symptom activation /datum/symptom/proc/Activate(datum/disease/advance/A) + var/unmitigated = check_treatment(A) + if(prob(unmitigated * activation_prob)) + symptom_act(A, unmitigated) return +/// Applies the symptom effects +/datum/symptom/proc/symptom_act(datum/disease/advance/A, unmitigated) + return + +/// Checks all forms of treatment and +/datum/symptom/proc/check_treatment(datum/disease/advance/A) + . = 1 + . *= check_chem_treatment(A) + . *= check_phys_treatment(A) + post_treatment(A, .) + +/// Actions that are taken after the treatment check like reverting or lessening effects. +/datum/symptom/proc/post_treatment(datum/disease/advance/A, unmitigated) + return + +/// Default behaviour for treatment chems. Mitigate the symptom for a while after being consumed. +/datum/symptom/proc/check_chem_treatment(datum/disease/advance/A) + . = 1 + // Make an assoc id to volume list we can easily search through and use + var/list/mob_reagents = list() + for(var/datum/reagent/chem in A.affected_mob.reagents.reagent_list) + mob_reagents += list("[chem.id]" = chem.volume) + // Go over each treatment and apply its mitigation if it still has timer. increase its timer if present in the patient + for(var/treatment in chem_treatments) + if(chem_treatments[treatment]["timer"] >= 1) + . *= chem_treatments[treatment]["multiplier"] + chem_treatments[treatment]["timer"]-- + if(treatment in mob_reagents) + // Consume as much as we need but no more than we have + var/consumption_mod = min(mob_reagents[treatment] / purge_amount, (VIRUS_MAX_TREATMENT_TIMER - chem_treatments[treatment]["timer"]) / VIRUS_TREATMENT_TIMER_MOD, 1) + A.affected_mob.reagents.remove_reagent(treatment, purge_amount * consumption_mod) + // Add to timer according to the amount consumed + chem_treatments[treatment]["timer"] += VIRUS_TREATMENT_TIMER_MOD * consumption_mod + +/datum/symptom/proc/check_phys_treatment(datum/disease/advance/A) + return 1 diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm index a4ddc9d5f86..95580b0f5d9 100644 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ b/code/datums/diseases/advance/symptoms/viral.dm @@ -20,15 +20,13 @@ BONUS stage_speed = -3 level = 3 -/datum/symptom/viraladaptation/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1) - to_chat(M, "You feel off, but no different from before.") - if(5) - to_chat(M, "You feel better, but nothing interesting happens.") +/datum/symptom/viraladaptation/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1) + to_chat(M, "You feel off, but no different from before.") + if(5) + to_chat(M, "You feel better, but nothing interesting happens.") /* ////////////////////////////////////// @@ -47,18 +45,24 @@ BONUS */ /datum/symptom/viralevolution name = "Viral evolutionary acceleration" - stealth = -2 - resistance = -3 + stealth = -4 stage_speed = 5 - transmittable = 3 + transmissibility = 3 level = 3 + var/static/list/possible_blocks + +/datum/symptom/viralevolution/Start(datum/disease/advance/A) + . = ..() + A.evolution_chance *= 1.5 + possible_blocks = list(GLOB.hornsblock, GLOB.loudblock, GLOB.comicblock, GLOB.swedeblock, GLOB.chavblock, GLOB.nervousblock, GLOB.lispblock) + +/datum/symptom/viralevolution/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(prob(A.progress * 2)) + // randomly set the value of a minor disability block + to_chat(M, "You feel like something is changing") + A.affected_mob.dna.SetSEValue(pick(possible_blocks), rand(0, 4095)) + domutcheck(A.affected_mob) + else + to_chat(M, "You feel a weird") -/datum/symptom/viralevolution/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1) - to_chat(M, "You feel better, but no different from before.") - if(5) - to_chat(M, "You feel off, but nothing interesting happens.") diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index 7fe551f0f02..76b1f75c447 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -18,34 +18,35 @@ Bonus /datum/symptom/visionloss name = "Hyphema" - stealth = -1 - resistance = -4 - stage_speed = -4 - transmittable = -3 + stealth = 2 + resistance = 1 + stage_speed = -1 + transmissibility = -4 level = 5 severity = 4 + chem_treatments = list( + "oculine" = list("multiplier" = 0, "timer" = 0) + ) -/datum/symptom/visionloss/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - var/obj/item/organ/internal/eyes/eyes = M.get_int_organ(/obj/item/organ/internal/eyes) - if(!eyes) //NO EYES, NO PROBLEM! Rip out your eyes today to prevent future blindness! - return - switch(A.stage) - if(1, 2) - to_chat(M, "Your eyes itch.") - if(3, 4) - to_chat(M, "Your eyes burn!") - M.EyeBlurry(40 SECONDS) - eyes.receive_damage(1) - else - to_chat(M, "Your eyes burn horrificly!") - M.EyeBlurry(60 SECONDS) - eyes.receive_damage(5) - if(eyes.damage >= 10) - M.become_nearsighted(EYE_DAMAGE) - if(prob(eyes.damage - 10 + 1)) - if(!M.AmountBlinded()) - to_chat(M, "You go blind!") - eyes.receive_damage(eyes.max_damage) +/datum/symptom/visionloss/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + var/obj/item/organ/internal/eyes/eyes = M.get_int_organ(/obj/item/organ/internal/eyes) + if(!eyes) //NO EYES, NO PROBLEM! Rip out your eyes today to prevent future blindness! + return + switch(A.stage) + if(1, 2) + to_chat(M, "Your eyes itch.") + if(3, 4) + to_chat(M, "Your eyes burn!") + M.EyeBlurry(40 SECONDS * unmitigated) + eyes.receive_damage(1 * unmitigated) + else + to_chat(M, "Your eyes burn horrificly!") + M.EyeBlurry(60 SECONDS * unmitigated) + eyes.receive_damage(5 * unmitigated) + if(eyes.damage >= 10) + M.become_nearsighted(EYE_DAMAGE) + if(prob(eyes.damage - 10 + 1) * unmitigated) + if(!M.AmountBlinded()) + to_chat(M, "You go blind!") + eyes.receive_damage(eyes.max_damage) diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm index 8ba3ed64008..af173b71960 100644 --- a/code/datums/diseases/advance/symptoms/voice_change.dm +++ b/code/datums/diseases/advance/symptoms/voice_change.dm @@ -6,7 +6,7 @@ Voice Change Very Very noticable. Lowers resistance considerably. Decreases stage speed. - Reduced transmittable. + Reduced transmissibility. Fatal Level. Bonus @@ -18,28 +18,34 @@ Bonus /datum/symptom/voice_change name = "Voice Change" - stealth = -2 + stealth = 2 resistance = -3 - stage_speed = -3 - transmittable = -1 + stage_speed = 3 + transmissibility = -2 level = 6 severity = 2 + chem_treatments = list( + "honey" = list("multiplier" = 0, "timer" = 0), + "lemonjuice" = list("multiplier" = 0, "timer" = 0), + "orangejuice" = list("multiplier" = 0, "timer" = 0), + "tea" = list("multiplier" = 0, "timer" = 0), + ) -/datum/symptom/voice_change/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("Your throat hurts.", "You clear your throat.")]") - else - if(ishuman(M)) - var/mob/living/carbon/human/H = M - H.SetSpecialVoice(H.dna.species.get_random_name(H.gender)) - +/datum/symptom/voice_change/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/carbon/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("Your throat hurts.", "You clear your throat.")]") + else + if(ishuman(M)) + var/mob/living/carbon/human/H = M + H.SetSpecialVoice(H.dna.species.get_random_name(H.gender)) return +/datum/symptom/voice_change/post_treatment(datum/disease/advance/A, unmitigated) + if(!unmitigated) + End() + /datum/symptom/voice_change/End(datum/disease/advance/A) ..() if(ishuman(A.affected_mob)) diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index 650f7bb380f..e69027adfb6 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -6,7 +6,7 @@ Vomiting Very Very Noticable. Decreases resistance. Doesn't increase stage speed. - Little transmittable. + Little transmissibility. Medium Level. Bonus @@ -22,26 +22,30 @@ Bonus /datum/symptom/vomit name = "Vomiting" - stealth = -2 - resistance = -1 - transmittable = 1 + stealth = -3 + stage_speed = -2 + transmissibility = 1 level = 3 - severity = 4 + severity = 3 + chem_treatments = list( + "calomel" = list("multiplier" = 0, "timer" = 0), + "charcoal" = list("multiplier" = 0, "timer" = 0), + "pen_acid" = list("multiplier" = 0, "timer" = 0)) -/datum/symptom/vomit/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB / 2)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) +/datum/symptom/vomit/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(A.progress > 20 && prob(A.progress)) + Vomit(M, A.progress) + else + switch(A.progress) + if(0 to 59) to_chat(M, "[pick("You feel nauseous.", "You feel like you're going to throw up!")]") - else - Vomit(M) - + if(60 to INFINITY) + to_chat(M, "[pick("You feel extremely nauseous!", "You barely manage to not throw up!")]") return -/datum/symptom/vomit/proc/Vomit(mob/living/carbon/M) - M.vomit(20) +/datum/symptom/vomit/proc/Vomit(mob/living/carbon/M, progress) + M.vomit(20 * (progress / 100)) /* ////////////////////////////////////// @@ -51,7 +55,7 @@ Vomiting Blood Very Very Noticable. Decreases resistance. Decreases stage speed. - Little transmittable. + Little transmissibility. Intense level. Bonus @@ -66,12 +70,14 @@ Bonus /datum/symptom/vomit/blood name = "Blood Vomiting" - stage_speed = -1 + stealth = -2 + resistance = 2 + stage_speed = -3 level = 4 severity = 5 -/datum/symptom/vomit/blood/Vomit(mob/living/carbon/M) - M.vomit(0, 1) +/datum/symptom/vomit/blood/Vomit(mob/living/carbon/M, progress) + M.vomit(35 * ((progress / 100) ** 2), TRUE, TRUE, distance = 1) /* @@ -82,7 +88,7 @@ Projectile Vomiting Very Very Noticable. Decreases resistance. Doesn't increase stage speed. - Little transmittable. + Little transmissibility. Medium Level. Bonus @@ -95,7 +101,10 @@ Bonus /datum/symptom/vomit/projectile name = "Projectile Vomiting" + stealth = -2 + resistance = 1 level = 4 + severity = 4 -/datum/symptom/vomit/projectile/Vomit(mob/living/carbon/M) - M.vomit(6,0,FALSE,5,1) +/datum/symptom/vomit/projectile/Vomit(mob/living/carbon/M, progress) + M.vomit(10 * ((progress / 100) ** 2), FALSE, TRUE, 6, 1) diff --git a/code/datums/diseases/advance/symptoms/weakness.dm b/code/datums/diseases/advance/symptoms/weakness.dm index 6ae1028594a..7dc93726d39 100644 --- a/code/datums/diseases/advance/symptoms/weakness.dm +++ b/code/datums/diseases/advance/symptoms/weakness.dm @@ -18,27 +18,30 @@ Bonus /datum/symptom/weakness name = "Weakness" - stealth = -1 - resistance = -1 + stealth = -2 + resistance = 1 stage_speed = -2 - transmittable = -2 + transmissibility = -1 level = 3 - severity = 3 + severity = 4 + chem_treatments = list( + "synaptazine" = list("multiplier" = 0, "timer" = 0), + "ephedrine" = list("multiplier" = 0, "timer" = 0), + "coffee" = list("multiplier" = 0.6, "timer" = 0), + "tea" = list("multiplier" = 0.6, "timer" = 0)) -/datum/symptom/weakness/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2) - to_chat(M, "[pick("You feel weak.", "You feel lazy.")]") - if(3, 4) - to_chat(M, "[pick("You feel very frail.", "You think you might faint.")]") - M.adjustStaminaLoss(15) - else - to_chat(M, "[pick("You feel tremendously weak!", "Your body trembles as exhaustion creeps over you.")]") - M.adjustStaminaLoss(30) - if(M.getStaminaLoss() > 60 && !M.stat) - M.visible_message("[M] faints!", "You swoon and faint...") - M.AdjustSleeping(10 SECONDS) +/datum/symptom/weakness/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2) + to_chat(M, "[pick("You feel weak.", "You feel lazy.")]") + if(3, 4) + to_chat(M, "[pick("You feel very frail.", "You think you might faint.")]") + M.adjustStaminaLoss(15 * unmitigated) + else + to_chat(M, "[pick("You feel tremendously weak!", "Your body trembles as exhaustion creeps over you.")]") + M.adjustStaminaLoss(30 * unmitigated) + if(M.getStaminaLoss() > 60 && !M.stat) + M.visible_message("[M] faints!", "You swoon and faint...") + M.AdjustSleeping(10 SECONDS * unmitigated) return diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index 732e56b69ed..fcb9fbf2366 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -6,7 +6,7 @@ Weight Loss Very Very Noticable. Decreases resistance. Decreases stage speed. - Reduced Transmittable. + Reduced transmissibility. High level. Bonus @@ -19,21 +19,26 @@ Bonus /datum/symptom/weight_loss name = "Weight Loss" - stealth = -3 - resistance = -2 + stealth = -2 + resistance = 1 stage_speed = -2 - transmittable = -2 + transmissibility = -3 level = 3 - severity = 1 + severity = 4 + chem_treatments = list( + "frostoil" = list("multiplier" = 0, "timer" = 0), + "protein" = list("multiplier" = 0.8, "timer" = 0), + "sugar" = list("multiplier" = 0.9, "timer" = 0), + "plantmatter" = list("multiplier" = 0.9, "timer" = 0), + "vitamin" = list("multiplier" = 0.8, "timer" = 0) + ) + +/datum/symptom/weight_loss/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(prob(A.progress)) + to_chat(M, "[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]") + M.overeatduration = max(M.overeatduration - A.progress * unmitigated, 0) + M.adjust_nutrition(-A.progress * unmitigated) + else + to_chat(M, "[pick("You feel hungry.", "You crave for food.")]") -/datum/symptom/weight_loss/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel hungry.", "You crave for food.")]") - else - to_chat(M, "[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]") - M.overeatduration = max(M.overeatduration - 100, 0) - M.adjust_nutrition(-100) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index e95196aa254..425a9483dfb 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -21,35 +21,33 @@ BONUS stealth = 3 resistance = 4 stage_speed = 4 - transmittable = -4 + transmissibility = -4 level = 5 + activation_prob = SYMPTOM_ACTIVATION_PROB * 2 -/datum/symptom/youth/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB * 2)) - var/mob/living/M = A.affected_mob - if(ishuman(M)) - var/mob/living/carbon/human/H = M - switch(A.stage) - if(1) - if(H.age > 41) - H.age = 41 - to_chat(H, "You haven't had this much energy in years!") - if(2) - if(H.age > 36) - H.age = 36 - to_chat(H, "You're suddenly in a good mood.") - if(3) - if(H.age > 31) - H.age = 31 - to_chat(H, "You begin to feel more lithe.") - if(4) - if(H.age > 26) - H.age = 26 - to_chat(H, "You feel reinvigorated.") - if(5) - if(H.age > 21) - H.age = 21 - to_chat(H, "You feel like you can take on the world!") - +/datum/symptom/youth/symptom_act(datum/disease/advance/A, unmitigated) + var/mob/living/M = A.affected_mob + if(ishuman(M)) + var/mob/living/carbon/human/H = M + switch(A.stage) + if(1) + if(H.age > 41) + H.age = 41 + to_chat(H, "You haven't had this much energy in years!") + if(2) + if(H.age > 36) + H.age = 36 + to_chat(H, "You're suddenly in a good mood.") + if(3) + if(H.age > 31) + H.age = 31 + to_chat(H, "You begin to feel more lithe.") + if(4) + if(H.age > 26) + H.age = 26 + to_chat(H, "You feel reinvigorated.") + if(5) + if(H.age > 21) + H.age = 21 + to_chat(H, "You feel like you can take on the world!") return diff --git a/code/datums/diseases/anxiety.dm b/code/datums/diseases/anxiety.dm index c5c97033116..0b0f4a5b2f9 100644 --- a/code/datums/diseases/anxiety.dm +++ b/code/datums/diseases/anxiety.dm @@ -3,13 +3,13 @@ form = "Infection" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Ethanol" cures = list("ethanol") agent = "Excess Lepidopticides" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) desc = "If left untreated subject will regurgitate butterflies." - severity = MINOR + severity = VIRUS_MINOR /datum/disease/anxiety/stage_act() if(!..()) diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm index 674abaafe2a..502bfa99f9d 100644 --- a/code/datums/diseases/appendicitis.dm +++ b/code/datums/diseases/appendicitis.dm @@ -3,14 +3,14 @@ name = "Appendicitis" max_stages = 3 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Surgery" agent = "Shitty Appendix" viable_mobtypes = list(/mob/living/carbon/human) desc = "If left untreated the subject will become very weak, and may vomit often." - severity = MINOR - disease_flags = CAN_CARRY|CAN_RESIST - visibility_flags = HIDDEN_PANDEMIC + severity = VIRUS_MINOR + disease_flags = VIRUS_CAN_CARRY|VIRUS_CAN_RESIST + visibility_flags = VIRUS_HIDDEN_PANDEMIC required_organs = list(/obj/item/organ/internal/appendix) bypasses_immunity = TRUE virus_heal_resistant = TRUE diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm index 348837c8d9f..ef4f0279737 100644 --- a/code/datums/diseases/beesease.dm +++ b/code/datums/diseases/beesease.dm @@ -3,13 +3,13 @@ form = "Infection" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Sugar" cures = list("sugar") agent = "Apidae Infection" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) desc = "If left untreated subject will regurgitate bees." - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD /datum/disease/beesease/stage_act() if(!..()) diff --git a/code/datums/diseases/berserker.dm b/code/datums/diseases/berserker.dm index 0a310eeb8d6..0ff6fecdf77 100644 --- a/code/datums/diseases/berserker.dm +++ b/code/datums/diseases/berserker.dm @@ -3,15 +3,15 @@ max_stages = 2 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Anti-Psychotics" cures = list("haloperidol") agent = "Jagged Crystals" cure_chance = 10 viable_mobtypes = list(/mob/living/carbon/human) desc = "Swearing, shouting, attacking nearby crew members uncontrollably." - severity = BIOHAZARD - disease_flags = CURABLE + severity = VIRUS_BIOHAZARD + disease_flags = VIRUS_CURABLE /datum/disease/berserker/stage_act() if(!..()) diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index ae5c5a23782..5f5bd15f458 100644 --- a/code/datums/diseases/brainrot.dm +++ b/code/datums/diseases/brainrot.dm @@ -2,7 +2,7 @@ name = "Brainrot" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Mannitol" cures = list("mannitol") agent = "Cryptococcus Cosmosis" @@ -10,7 +10,7 @@ cure_chance = 15 desc = "This disease destroys the braincells, causing brain fever, brain necrosis and general intoxication." required_organs = list(/obj/item/organ/internal/brain) - severity = HARMFUL + severity = VIRUS_HARMFUL /datum/disease/brainrot/stage_act() if(!..()) diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index f2ddacc0dce..7363cc96725 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -8,7 +8,7 @@ viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) permeability_mod = 0.5 desc = "If left untreated the subject will contract the flu." - severity = MINOR + severity = VIRUS_MINOR /datum/disease/cold/stage_act() if(!..()) diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm index 584b284fd52..8b31fc36d6d 100644 --- a/code/datums/diseases/cold9.dm +++ b/code/datums/diseases/cold9.dm @@ -3,13 +3,13 @@ medical_name = "ICE9 Cold" max_stages = 3 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Spaceacillin" cures = list("spaceacillin") agent = "ICE9-rhinovirus" viable_mobtypes = list(/mob/living/carbon/human) desc = "If left untreated the subject will slow, as if partly frozen." - severity = HARMFUL + severity = VIRUS_HARMFUL /datum/disease/cold9/stage_act() if(!..()) diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm index 2ec4c68e7fe..d582fd35730 100644 --- a/code/datums/diseases/critical.dm +++ b/code/datums/diseases/critical.dm @@ -1,11 +1,11 @@ /datum/disease/critical form = "Medical Emergency" max_stages = 3 - spread_flags = SPECIAL + spread_flags = SPREAD_SPECIAL viable_mobtypes = list(/mob/living/carbon/human) - severity = MINOR - disease_flags = CURABLE - visibility_flags = HIDDEN_PANDEMIC + severity = VIRUS_MINOR + disease_flags = VIRUS_CURABLE + visibility_flags = VIRUS_HIDDEN_PANDEMIC bypasses_immunity = TRUE virus_heal_resistant = TRUE @@ -87,7 +87,7 @@ cures = list("atropine", "epinephrine", "heparin", "syndicate_nanites", "stimulative_agent") cure_chance = 10 stage_prob = 5 - severity = HARMFUL + severity = VIRUS_HARMFUL required_organs = list(ORGAN_DATUM_HEART) /datum/disease/critical/heart_failure/stage_act() diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm index 1981a40967e..e9efece8bb6 100644 --- a/code/datums/diseases/fake_gbs.dm +++ b/code/datums/diseases/fake_gbs.dm @@ -2,13 +2,13 @@ name = "GBS" max_stages = 5 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Diphenhydramine & Sulfur" cures = list("diphenhydramine","sulfur") agent = "Gravitokinetic Bipotential SADS-" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) desc = "If left untreated death will occur." - severity = BIOHAZARD // Mimicking real GBS + severity = VIRUS_BIOHAZARD // Mimicking real GBS /datum/disease/fake_gbs/stage_act() if(!..()) diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index 15540321fb7..a56b18a4dbe 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -9,7 +9,7 @@ viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) permeability_mod = 0.75 desc = "If left untreated the subject will feel quite unwell." - severity = MINOR + severity = VIRUS_MINOR /datum/disease/flu/stage_act() if(!..()) diff --git a/code/datums/diseases/fluspanish.dm b/code/datums/diseases/fluspanish.dm index 6245d9bce0b..17c1a1604df 100644 --- a/code/datums/diseases/fluspanish.dm +++ b/code/datums/diseases/fluspanish.dm @@ -9,7 +9,7 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "If left untreated the subject will burn to death for being a heretic." - severity = HARMFUL + severity = VIRUS_HARMFUL /datum/disease/fluspanish/stage_act() if(!..()) diff --git a/code/datums/diseases/food_poisoning.dm b/code/datums/diseases/food_poisoning.dm index 30ba51330d9..c28bc006343 100644 --- a/code/datums/diseases/food_poisoning.dm +++ b/code/datums/diseases/food_poisoning.dm @@ -3,15 +3,15 @@ max_stages = 3 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Sleep" agent = "Salmonella" cures = list("chicken_soup") cure_chance = 10 viable_mobtypes = list(/mob/living/carbon/human) desc = "Nausea, sickness, and vomitting." - severity = MINOR - disease_flags = CURABLE + severity = VIRUS_MINOR + disease_flags = VIRUS_CURABLE virus_heal_resistant = TRUE /datum/disease/food_poisoning/stage_act() diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm index 3044da57aa0..33c0984a06a 100644 --- a/code/datums/diseases/gbs.dm +++ b/code/datums/diseases/gbs.dm @@ -2,13 +2,13 @@ name = "GBS" max_stages = 5 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Diphenhydramine & Sulfur" cures = list("diphenhydramine","sulfur") cure_chance = 15 // Higher chance to cure, since two reagents are required agent = "Gravitokinetic Bipotential SADS+" viable_mobtypes = list(/mob/living/carbon/human) - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD /datum/disease/gbs/stage_act() if(!..()) @@ -41,9 +41,9 @@ name = "Non-Contagious GBS" stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Cryoxadone" cures = list("cryoxadone") cure_chance = 10 agent = "gibbis" - disease_flags = CURABLE + disease_flags = VIRUS_CURABLE diff --git a/code/datums/diseases/kingstons.dm b/code/datums/diseases/kingstons.dm index add3612f618..b00912038e1 100644 --- a/code/datums/diseases/kingstons.dm +++ b/code/datums/diseases/kingstons.dm @@ -9,7 +9,7 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "If left untreated the subject will turn into a feline. In felines it has... OTHER... effects." - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD /datum/disease/kingstons/stage_act() if(!..()) @@ -59,7 +59,7 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "If left untreated the subject will mutate to a different species." - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD var/list/virspecies = list(/datum/species/human, /datum/species/tajaran, /datum/species/unathi,/datum/species/skrell, /datum/species/vulpkanin, /datum/species/diona, /datum/species/slime, /datum/species/kidan, /datum/species/drask, /datum/species/grey, /datum/species/moth) // No IPCs (not organic), or vox+plasmemes because of air requirements var/list/virsuffix = list("pox", "rot", "flu", "cough", "-gitis", "cold", "rash", "itch", "decay") diff --git a/code/datums/diseases/kuru.dm b/code/datums/diseases/kuru.dm index 8bb5ca3a95b..96dc5d07dfa 100644 --- a/code/datums/diseases/kuru.dm +++ b/code/datums/diseases/kuru.dm @@ -4,13 +4,13 @@ max_stages = 4 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Incurable" agent = "Prions" viable_mobtypes = list(/mob/living/carbon/human) desc = "Uncontrollable laughing." - severity = BIOHAZARD - disease_flags = CAN_CARRY + severity = VIRUS_BIOHAZARD + disease_flags = VIRUS_CAN_CARRY bypasses_immunity = TRUE // Kuru is a prion disorder, not a virus virus_heal_resistant = TRUE diff --git a/code/datums/diseases/lycancoughy.dm b/code/datums/diseases/lycancoughy.dm index 60ff4ad780a..883ef6d6573 100644 --- a/code/datums/diseases/lycancoughy.dm +++ b/code/datums/diseases/lycancoughy.dm @@ -3,13 +3,13 @@ form = "Infection" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Ethanol" cures = list("ethanol") agent = "Excess Snuggles" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) desc = "If left untreated subject will regurgitate... puppies." - severity = HARMFUL + severity = VIRUS_HARMFUL var/barklimit = 10 var/list/puppy_types = list(/mob/living/simple_animal/pet/dog/corgi/puppy, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/pet/dog/fox) var/list/plush_types = list(/obj/item/toy/plushie/orange_fox, /obj/item/toy/plushie/corgi, /obj/item/toy/plushie/robo_corgi, /obj/item/toy/plushie/pink_fox) diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm index ddb6782f6db..4a1009e0528 100644 --- a/code/datums/diseases/magnitis.dm +++ b/code/datums/diseases/magnitis.dm @@ -8,7 +8,7 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field." - severity = MINOR + severity = VIRUS_MINOR /datum/disease/magnitis/stage_act() if(!..()) diff --git a/code/datums/diseases/pierrot_throat.dm b/code/datums/diseases/pierrot_throat.dm index dd2c7580924..1a9dc8334b4 100644 --- a/code/datums/diseases/pierrot_throat.dm +++ b/code/datums/diseases/pierrot_throat.dm @@ -9,7 +9,7 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "If left untreated the subject will probably drive others to insanity and go insane themselves." - severity = MINOR + severity = VIRUS_MINOR /datum/disease/pierrot_throat/stage_act() if(!..()) diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm index e764d037ccc..f5531b6eafc 100644 --- a/code/datums/diseases/retrovirus.dm +++ b/code/datums/diseases/retrovirus.dm @@ -2,13 +2,13 @@ name = "Retrovirus" max_stages = 4 spread_text = "Contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Rest or an injection of mutadone" cure_chance = 6 agent = "" viable_mobtypes = list(/mob/living/carbon/human) desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly." - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD permeability_mod = 0.4 stage_prob = 2 var/SE diff --git a/code/datums/diseases/rhumba_beat.dm b/code/datums/diseases/rhumba_beat.dm index 80856f108f3..b8ef0322a2d 100644 --- a/code/datums/diseases/rhumba_beat.dm +++ b/code/datums/diseases/rhumba_beat.dm @@ -2,12 +2,12 @@ name = "The Rhumba Beat" max_stages = 5 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = SPREAD_CONTACT_GENERAL cure_text = "Chick Chicky Boom!" cures = list("plasma") agent = "Unknown" viable_mobtypes = list(/mob/living/carbon/human) - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD /datum/disease/rhumba_beat/stage_act() if(!..()) diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index 8ade68def32..242b32d9867 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -2,13 +2,13 @@ name = "Transformation" max_stages = 5 spread_text = "Acute" - spread_flags = SPECIAL + spread_flags = SPREAD_SPECIAL cure_text = "A coder's love (theoretical)." agent = "Shenanigans" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/alien) - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD stage_prob = 10 - disease_flags = CURABLE + disease_flags = VIRUS_CURABLE var/list/stage1 = list("You feel unremarkable.") var/list/stage2 = list("You feel boring.") var/list/stage3 = list("You feel utterly plain.") diff --git a/code/datums/diseases/tuberculosis.dm b/code/datums/diseases/tuberculosis.dm index e8f6bb317b5..f8709892d16 100644 --- a/code/datums/diseases/tuberculosis.dm +++ b/code/datums/diseases/tuberculosis.dm @@ -10,7 +10,7 @@ cure_chance = 5 // Like hell are you getting out of hell desc = "A rare highly transmittable virulent virus. Few samples exist, rumoured to be carefully grown and cultured by clandestine bio-weapon specialists. Causes fever, blood vomiting, lung damage, weight loss, and fatigue." required_organs = list(ORGAN_DATUM_LUNGS) - severity = HARMFUL + severity = VIRUS_HARMFUL bypasses_immunity = TRUE //Fungal and bacterial in nature; also infects the lungs /datum/disease/tuberculosis/stage_act() diff --git a/code/datums/diseases/wizard_diseases.dm b/code/datums/diseases/wizard_diseases.dm index 48e2e2fc864..39a8bf52610 100644 --- a/code/datums/diseases/wizard_diseases.dm +++ b/code/datums/diseases/wizard_diseases.dm @@ -1,39 +1,39 @@ /datum/disease/beesease/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/cold9/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/fluspanish/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/kingstons_advanced/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/dna_retrovirus/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/tuberculosis/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/wizarditis/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/anxiety/wizard_variant - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS /datum/disease/grut_gut name = "Grut Gut" max_stages = 5 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Pyrotech stabilizing agents" agent = "Eruca Stomachum" cures = list("stabilizing_agent") viable_mobtypes = list(/mob/living/carbon/human) - desc = "A magic-infused disease that builds up dangerously high pressure bile within the stomach." - severity = DANGEROUS + desc = "A magic-infused disease that builds up VIRUS_DANGEROUSly high pressure bile within the stomach." + severity = VIRUS_DANGEROUS virus_heal_resistant = TRUE /datum/disease/grut_gut/stage_act() @@ -98,13 +98,13 @@ max_stages = 5 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "Acetaldehyde" agent = "nasum magicum" cures = list("acetaldehyde") viable_mobtypes = list(/mob/living/carbon/human) desc = "A magic-infused disease that replaces one's nose hairs with tiny wands. Avoid nasal irritants." - severity = DANGEROUS + severity = VIRUS_DANGEROUS virus_heal_resistant = TRUE /datum/disease/wand_rot/stage_act() @@ -165,13 +165,13 @@ max_stages = 5 stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS cure_text = "liquid dark matter" agent = "Spatio Ventrem" cures = list("liquid_dark_matter") viable_mobtypes = list(/mob/living/carbon/human) desc = "A magic-infused disease that resides in the gut, converting gastric juices into space-matter." - severity = DANGEROUS + severity = VIRUS_DANGEROUS virus_heal_resistant = TRUE /datum/disease/mystic_malaise/stage_act() diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm index 2dd23b8734a..11861edc222 100644 --- a/code/datums/diseases/wizarditis.dm +++ b/code/datums/diseases/wizarditis.dm @@ -9,7 +9,7 @@ agent = "Rincewindus Vulgaris" viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 - severity = MINOR + severity = VIRUS_MINOR /// A mapping of `num2text(ITEM_SLOT_XYZ)` -> item path var/list/magic_fashion = list() diff --git a/code/datums/diseases/zombie_virus.dm b/code/datums/diseases/zombie_virus.dm index ce080ee0e0f..661fdad1b98 100644 --- a/code/datums/diseases/zombie_virus.dm +++ b/code/datums/diseases/zombie_virus.dm @@ -4,14 +4,14 @@ desc = "This virus infects humanoids and drives them insane with a hunger for flesh, along with possessing regenerative abilities." max_stages = 7 spread_text = "Blood and Saliva" - spread_flags = BLOOD + spread_flags = SPREAD_BLOOD cure_text = "Anti-plague viral solutions" cures = list() agent = "" viable_mobtypes = list(/mob/living/carbon/human) - severity = BIOHAZARD + severity = VIRUS_BIOHAZARD allow_dead = TRUE - disease_flags = CAN_CARRY + disease_flags = VIRUS_CAN_CARRY virus_heal_resistant = TRUE stage_prob = 100 // It isn't actually 100%, but is instead based on a timer cure_chance = 80 @@ -180,7 +180,7 @@ return ..() /datum/disease/zombie/wizard - spread_flags = NON_CONTAGIOUS + spread_flags = SPREAD_NON_CONTAGIOUS bypasses_immunity = TRUE spread_text = "Non Contagious" cure_text = "Anti-magic" diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index fb02c7be254..aeaadbf2803 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -107,7 +107,7 @@ var/datum/disease/D = thing if(!D.discovered) // Early-stage viruses should not show up on med HUD (though health analywers can still pick them up) continue - if((!(D.visibility_flags & HIDDEN_SCANNER)) && (D.severity != NONTHREAT)) + if((!(D.visibility_flags & VIRUS_HIDDEN_SCANNER)) && (D.severity != VIRUS_NONTHREAT)) return TRUE return FALSE diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index b169adc13d8..8ed9784337a 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -194,12 +194,13 @@ to_chat(owner, "You feel sick.") var/datum/disease/advance/A = random_virus(pick(2, 6), 6) A.carrier = TRUE - owner.ForceContractDisease(A) + owner.ForceContractDisease(A, TRUE) /obj/item/organ/internal/heart/gland/viral/proc/random_virus(max_symptoms, max_level) if(max_symptoms > VIRUS_SYMPTOM_LIMIT) max_symptoms = VIRUS_SYMPTOM_LIMIT var/datum/disease/advance/A = new /datum/disease/advance() + A.clear_symptoms() var/list/datum/symptom/possible_symptoms = list() for(var/symptom in subtypesof(/datum/symptom)) var/datum/symptom/S = symptom @@ -208,7 +209,7 @@ if(initial(S.level) <= 0) //unobtainable symptoms continue possible_symptoms += S - for(var/i in 1 to max_symptoms) + while(length(A.symptoms) < max_symptoms) var/datum/symptom/chosen_symptom = pick_n_take(possible_symptoms) if(chosen_symptom) var/datum/symptom/S = new chosen_symptom diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm index 4f3f0795a0f..faa875eccdf 100644 --- a/code/game/gamemodes/wizard/magic_tarot.dm +++ b/code/game/gamemodes/wizard/magic_tarot.dm @@ -533,7 +533,7 @@ H.apply_status_effect(STATUS_EFFECT_PANACEA) for(var/thing in H.viruses) var/datum/disease/D = thing - if(D.severity == NONTHREAT) + if(D.severity == VIRUS_NONTHREAT) continue D.cure() diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 250b77b87c8..b24f21dc4f0 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -251,7 +251,7 @@ var/found_disease = FALSE for(var/thing in occupant.viruses) var/datum/disease/D = thing - if(D.visibility_flags & HIDDEN_SCANNER || D.visibility_flags & HIDDEN_PANDEMIC) + if(D.visibility_flags & VIRUS_HIDDEN_SCANNER || D.visibility_flags & VIRUS_HIDDEN_PANDEMIC) continue if(istype(D, /datum/disease/critical)) continue @@ -441,7 +441,7 @@ var/found_disease = FALSE for(var/thing in occupant.viruses) var/datum/disease/D = thing - if(D.visibility_flags & HIDDEN_SCANNER || D.visibility_flags & HIDDEN_PANDEMIC) + if(D.visibility_flags & VIRUS_HIDDEN_SCANNER || D.visibility_flags & VIRUS_HIDDEN_PANDEMIC) continue found_disease = TRUE break diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 7806fbbcf0c..f0f58840b73 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -179,12 +179,6 @@ build_path = /obj/machinery/computer/med_data origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/pandemic - board_name = "PanD.E.M.I.C. 2200" - icon_state = "medical" - build_path = /obj/machinery/computer/pandemic - origin_tech = "programming=2;biotech=2" - /obj/item/circuitboard/scan_consolenew board_name = "DNA Machine" icon_state = "medical" diff --git a/code/game/machinery/dish_drive.dm b/code/game/machinery/dish_drive.dm index cabf1c2a342..8585dd29496 100644 --- a/code/game/machinery/dish_drive.dm +++ b/code/game/machinery/dish_drive.dm @@ -89,6 +89,7 @@ LAZYADD(dish_drive_contents, I) visible_message("[src] beams up [I]!") I.forceMove(src) + SEND_SIGNAL(src, COMSIG_ATOM_DISINFECTED) playsound(src, 'sound/items/pshoom.ogg', 15, TRUE) flick("synthesizer_beam", src) else diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm index 7c63e9dcffc..ede3ae65ca4 100644 --- a/code/game/machinery/machine_frame.dm +++ b/code/game/machinery/machine_frame.dm @@ -987,6 +987,14 @@ to destroy them and players will be able to make replacements. /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/glass = 4) +/obj/item/circuitboard/pandemic + board_name = "PanD.E.M.I.C. 2200" + icon_state = "medical" + board_type = "machine" + build_path = /obj/machinery/pandemic + req_components = list(/obj/item/stock_parts/manipulator = 1, /obj/item/stock_parts/micro_laser = 1) + origin_tech = "programming=2;biotech=2" + /obj/item/circuitboard/cell_charger board_name = "Cell Charger" icon_state = "engineering" diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 5c6ebe8189e..7beccb38d8e 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -275,11 +275,19 @@ SLIME SCANNER for(var/thing in H.viruses) var/datum/disease/D = thing - if(D.visibility_flags & HIDDEN_SCANNER) + // If the disease is incubating, or if it's stealthy and hasn't been put into a pandemic yet the scanner won't see it + if(D.incubation || (D.visibility_flags & VIRUS_HIDDEN_SCANNER && !(D.GetDiseaseID() in GLOB.detected_advanced_diseases["[user.z]"]))) continue // Snowflaking heart problems, because they are special (and common). if(istype(D, /datum/disease/critical)) - msgs += "Warning: Subject is undergoing [D.name].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]" + msgs += "Warning: Subject is undergoing [D.name].\nStage: [D.stage]/[D.max_stages].\nCure: [D.cure_text]" + continue + if(istype(D, /datum/disease/advance)) + var/datum/disease/advance/A = D + if(!(A.id in GLOB.known_advanced_diseases[num2text(user.z)])) + msgs += "Warning: Unknown viral strain detected\nStrain:[A.strain]\nStage: [A.stage]" + else + msgs += "Warning: [A.form] detected\nName: [A.name].\nStrain:[A.strain]\nType: [A.spread_text].\nStage: [A.stage]/[A.max_stages].\nPossible Cures: [A.cure_text]\nNeeded Cures: [A.cures_required]" continue msgs += "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]" @@ -395,6 +403,8 @@ SLIME SCANNER if(H.radiation > RAD_MOB_SAFE) msgs += "Subject is irradiated." + msgs += "Biological Age: [H.age]" + to_chat(user, chat_box_healthscan(msgs.Join("
"))) /obj/item/healthanalyzer/attackby__legacy__attackchain(obj/item/I, mob/user, params) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 56535a23a42..2370d2a4902 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -210,6 +210,8 @@ . = ..() extinguish() acid_level = 0 + if(temperature > VIRUS_DISINFECTION_TEMP) + SEND_SIGNAL(src, COMSIG_ATOM_DISINFECTED) /obj/singularity_pull(S, current_size) ..() diff --git a/code/game/turfs/space/space_turf.dm b/code/game/turfs/space/space_turf.dm index 761eebd017b..751bea457dc 100644 --- a/code/game/turfs/space/space_turf.dm +++ b/code/game/turfs/space/space_turf.dm @@ -15,6 +15,8 @@ atmos_mode = ATMOS_MODE_SPACE + rad_insulation_alpha = RAD_NO_INSULATION + /turf/space/Initialize(mapload) SHOULD_CALL_PARENT(FALSE) if(!istype(src, /turf/space/transit)) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7da4cb363ad..e86f94333a6 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -92,6 +92,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/cmd_admin_dress, /client/proc/cmd_admin_gib_self, /client/proc/drop_bomb, + /client/proc/disease_outbreak, /client/proc/one_click_antag, /client/proc/cmd_admin_add_freeform_ai_law, /client/proc/cmd_admin_add_random_ai_law, @@ -776,12 +777,53 @@ GLOBAL_LIST_INIT(view_logs_verbs, list( set category = "Event" set name = "Give Disease" set desc = "Gives a Disease to a mob." - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in GLOB.diseases - if(!D) return - T.ForceContractDisease(new D) + var/datum/disease/given_disease = null + + if(tgui_input_list(usr, "Create own disease", "Would you like to create your own disease?", list("Yes","No")) == "Yes") + given_disease = AdminCreateVirus(usr) + else + given_disease = tgui_input_list(usr, "ACHOO", "Choose the disease to give to that guy", GLOB.diseases) + + if(!given_disease) + return + + if(!istype(given_disease, /datum/disease/advance)) + given_disease = new given_disease + T.ForceContractDisease(given_disease) SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") + log_admin("[key_name(usr)] gave [key_name(T)] the disease [given_disease].") + message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [given_disease].") + +/client/proc/disease_outbreak() + set category = "Event" + set name = "Disease Outbreak" + set desc = "Creates a disease and infects a random player with it" + var/datum/disease/given_disease = null + if(tgui_input_list(usr, "Create own disease", "Would you like to create your own disease?", list("Yes","No")) == "Yes") + given_disease = AdminCreateVirus(usr) + else + given_disease = tgui_input_list(usr, "ACHOO", "Choose the disease to give to that guy", GLOB.diseases) + if(!given_disease) + return + + if(!istype(given_disease, /datum/disease/advance)) + given_disease = new given_disease + + for(var/thing in shuffle(GLOB.human_list)) + var/mob/living/carbon/human/H = thing + if(H.stat == DEAD || !is_station_level(H.z)) + continue + if(!H.HasDisease(given_disease)) + H.ForceContractDisease(given_disease) + break + if(istype(given_disease, /datum/disease/advance)) + var/datum/disease/advance/given_advanced_disease = given_disease + var/list/name_symptoms = list() + for(var/datum/symptom/S in given_advanced_disease.symptoms) + name_symptoms += S.name + message_admins("[key_name_admin(usr)] has triggered a custom virus outbreak of [given_advanced_disease.name]! It has these symptoms: [english_list(name_symptoms)] and these base stats [english_map(given_advanced_disease.base_properties)]") + else + message_admins("[key_name_admin(usr)] has triggered a custom virus outbreak of [given_disease.name]!") /client/proc/make_sound(obj/O in view()) // -- TLE set name = "\[Admin\] Make Sound" diff --git a/code/modules/admin/menus/antagonist_menu.dm b/code/modules/admin/menus/antagonist_menu.dm index 6d62a4c6d16..4aa3da8a83b 100644 --- a/code/modules/admin/menus/antagonist_menu.dm +++ b/code/modules/admin/menus/antagonist_menu.dm @@ -133,6 +133,28 @@ RESTRICT_TYPE(/datum/ui_module/admin/antagonist_menu) temp_list["max_health"] = player.maxHealth cached_data["security"] += list(temp_list) + cached_data["disease_carriers"] = list() + cached_data["virus_data"] = list() + for(var/datum/disease/advance/virus in GLOB.active_diseases) + var/list/temp_list = list() + var/list/temp_list2 = list() + var/mob/living/carbon/player = virus.affected_mob + if(!player?.mind) + continue + temp_list["name"] = player.mind.name + temp_list["mind_uid"] = player.mind.UID() + temp_list["ckey"] = ckey(player.mind.key) + temp_list["status"] = player.stat + temp_list["health"] = player.health + temp_list["max_health"] = player.maxHealth + temp_list["progress"] = virus.progress + temp_list["patient_zero"] = virus.carrier + temp_list["virus_name"] = virus.name + temp_list["strain"] = virus.strain + if(!("[virus.strain]" in temp_list2)) + temp_list2["[virus.strain]"] = english_list(virus.symptoms) + cached_data["disease_carriers"] += list(temp_list) + cached_data["virus_data"] += temp_list2 /datum/ui_module/admin/antagonist_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) if(..()) diff --git a/code/modules/antagonists/changeling/powers/panacea.dm b/code/modules/antagonists/changeling/powers/panacea.dm index 231af787959..f11689f1753 100644 --- a/code/modules/antagonists/changeling/powers/panacea.dm +++ b/code/modules/antagonists/changeling/powers/panacea.dm @@ -26,7 +26,7 @@ user.apply_status_effect(STATUS_EFFECT_PANACEA) for(var/thing in user.viruses) var/datum/disease/D = thing - if(D.severity == NONTHREAT) + if(D.severity == VIRUS_NONTHREAT) continue D.cure() diff --git a/code/modules/antagonists/zombie/datum_zombie.dm b/code/modules/antagonists/zombie/datum_zombie.dm index f23920429a8..a8c7229080f 100644 --- a/code/modules/antagonists/zombie/datum_zombie.dm +++ b/code/modules/antagonists/zombie/datum_zombie.dm @@ -79,8 +79,7 @@ RESTRICT_TYPE(/datum/antagonist/zombie) ADD_TRAIT(L, trait, ZOMBIE_TRAIT) if(!L.HasDisease(/datum/disease/zombie)) var/datum/disease/zombie/zomb = new /datum/disease/zombie() - zomb.stage = 7 - L.AddDisease(zomb) + L.AddDisease(zomb, FALSE, 7) if(ishuman(L)) var/mob/living/carbon/human/H = L diff --git a/code/modules/awaymissions/mob_spawn.dm b/code/modules/awaymissions/mob_spawn.dm index 7c2e127d3eb..922071bc4a8 100644 --- a/code/modules/awaymissions/mob_spawn.dm +++ b/code/modules/awaymissions/mob_spawn.dm @@ -764,8 +764,8 @@ ADD_TRAIT(H, TRAIT_NON_INFECTIOUS_ZOMBIE, ROUNDSTART_TRAIT) . = ..() for(var/datum/disease/zombie/zomb in H.viruses) - zomb.spread_flags = SPECIAL - zomb.visibility_flags = HIDDEN_PANDEMIC // This is how critical diseases block being interacted by the pandemic or copied. I hate it here. + zomb.spread_flags = SPREAD_SPECIAL + zomb.visibility_flags = VIRUS_HIDDEN_PANDEMIC // This is how critical diseases block being interacted by the pandemic or copied. I hate it here. /// these mob spawn subtypes trigger immediately (New or Initialize) and are not player controlled... since they're dead, you know? diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index c7926e986aa..67a840c607d 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -2,6 +2,7 @@ name = "clothing" integrity_failure = 80 resistance_flags = FLAMMABLE + permeability_coefficient = 0.8 /// Only these species can wear this kit. var/list/species_restricted /// Can the wearer see reagents inside transparent containers while it's equipped? @@ -244,6 +245,7 @@ gender = PLURAL icon = 'icons/obj/clothing/gloves.dmi' siemens_coefficient = 0.50 + permeability_coefficient = 0.5 body_parts_covered = HANDS slot_flags = ITEM_SLOT_GLOVES attack_verb = list("challenged") @@ -538,6 +540,7 @@ body_parts_covered = HEAD slot_flags = ITEM_SLOT_MASK put_on_delay = 4 SECONDS + permeability_coefficient = 0.7 var/adjusted_flags = null @@ -620,7 +623,7 @@ dyeable = TRUE dyeing_key = DYE_REGISTRY_SHOES - permeability_coefficient = 0.50 + permeability_coefficient = 0.4 sprite_sheets = list( "Vox" = 'icons/mob/clothing/species/vox/shoes.dmi', @@ -748,6 +751,7 @@ drop_sound = 'sound/items/handling/cloth_drop.ogg' pickup_sound = 'sound/items/handling/cloth_pickup.ogg' slot_flags = ITEM_SLOT_OUTER_SUIT + permeability_coefficient = 0.75 var/fire_resist = T0C + 100 var/blood_overlay_type = "suit" @@ -972,7 +976,7 @@ item_state = "s_suit" w_class = WEIGHT_CLASS_BULKY gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 + permeability_coefficient = 0.01 flags = STOPSPRESSUREDMAGE | THICKMATERIAL body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS allowed = list(/obj/item/flashlight, /obj/item/tank/internals) @@ -999,7 +1003,6 @@ icon = 'icons/obj/clothing/under/misc.dmi' name = "under" body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - permeability_coefficient = 0.90 slot_flags = ITEM_SLOT_JUMPSUIT armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 0, ACID = 0) equip_sound = 'sound/items/equip/jumpsuit_equip.ogg' diff --git a/code/modules/clothing/glasses/hudglasses.dm b/code/modules/clothing/glasses/hudglasses.dm index b0def2f3f61..d40de2e8954 100644 --- a/code/modules/clothing/glasses/hudglasses.dm +++ b/code/modules/clothing/glasses/hudglasses.dm @@ -1,7 +1,7 @@ /obj/item/clothing/glasses/hud name = "\improper HUD" desc = "A heads-up display that provides important info in (almost) real time." - flags = null //doesn't protect eyes because it's a monocle, duh + flags_cover = null //doesn't protect eyes because it's a monocle, duh origin_tech = "magnets=3;biotech=2" prescription_upgradable = TRUE /// The visual icons granted by wearing these glasses. diff --git a/code/modules/clothing/glasses/hudgoggles.dm b/code/modules/clothing/glasses/hudgoggles.dm index a0569502881..b8d4d1224d9 100644 --- a/code/modules/clothing/glasses/hudgoggles.dm +++ b/code/modules/clothing/glasses/hudgoggles.dm @@ -2,23 +2,28 @@ name = "security HUD goggles" desc = "Fashionable security goggles." icon_state = "sechudgoggles" + flags_cover = GLASSESCOVERSEYES /obj/item/clothing/glasses/hud/health/goggles name = "health HUD goggles" desc = "Fashionable medical goggles." icon_state = "medhudgoggles" + flags_cover = GLASSESCOVERSEYES /obj/item/clothing/glasses/hud/diagnostic/goggles name = "diagnostic HUD goggles" desc = "Fashionable diagnostic goggles." icon_state = "diaghudgoggles" + flags_cover = GLASSESCOVERSEYES /obj/item/clothing/glasses/hud/hydroponic/goggles name = "hydroponic HUD goggles" desc = "Fashionable hydroponic goggles." icon_state = "hydrohudgoggles" + flags_cover = GLASSESCOVERSEYES /obj/item/clothing/glasses/hud/skills/goggles name = "skills HUD goggles" desc = "Fashionable skills goggles." icon_state = "skillhudgoggles" + flags_cover = GLASSESCOVERSEYES diff --git a/code/modules/clothing/gloves/colored_gloves.dm b/code/modules/clothing/gloves/colored_gloves.dm index e45e6919f65..9f4db0cc59c 100644 --- a/code/modules/clothing/gloves/colored_gloves.dm +++ b/code/modules/clothing/gloves/colored_gloves.dm @@ -7,7 +7,7 @@ icon_state = "yellow" item_state = "ygloves" siemens_coefficient = 0 - permeability_coefficient = 0.05 + permeability_coefficient = 0.01 item_color = "yellow" resistance_flags = NONE @@ -21,7 +21,7 @@ icon_state = "yellow" item_state = "ygloves" siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() - permeability_coefficient = 0.05 + permeability_coefficient = 0.01 item_color="yellow" resistance_flags = NONE @@ -55,6 +55,7 @@ "Grey" = 'icons/mob/clothing/species/grey/gloves.dmi', "Kidan" = 'icons/mob/clothing/species/kidan/gloves.dmi', ) + permeability_coefficient = 0.1 /obj/item/clothing/gloves/color/black/thief pickpocket = 1 @@ -98,7 +99,7 @@ name = "insulated gloves" desc = "A pair of rubber-lined industrial gloves. They'll protect the wearer from electrical shocks." siemens_coefficient = 0 - permeability_coefficient = 0.05 + permeability_coefficient = 0.01 resistance_flags = NONE /obj/item/clothing/gloves/color/rainbow @@ -193,7 +194,7 @@ item_state = "captain" item_color = "captain" siemens_coefficient = 0 - permeability_coefficient = 0.05 + permeability_coefficient = 0.01 cold_protection = HANDS min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT heat_protection = HANDS diff --git a/code/modules/clothing/gloves/misc_gloves.dm b/code/modules/clothing/gloves/misc_gloves.dm index c8747f9a40b..f798e84ea43 100644 --- a/code/modules/clothing/gloves/misc_gloves.dm +++ b/code/modules/clothing/gloves/misc_gloves.dm @@ -7,6 +7,7 @@ transfer_prints = TRUE cold_protection = HANDS min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + permeability_coefficient = 1 strip_delay = 40 put_on_delay = 20 clipped = TRUE diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 80daa2e2920..0740bf45200 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -19,6 +19,7 @@ "Drask" = 'icons/mob/clothing/species/drask/helmet.dmi', "Grey" = 'icons/mob/clothing/species/grey/helmet.dmi' ) + permeability_coefficient = 0.4 /obj/item/clothing/head/helmet/attack_self__legacy__attackchain(mob/user) if(can_toggle && !user.incapacitated()) @@ -94,6 +95,7 @@ flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH strip_delay = 80 dog_fashion = null + permeability_coefficient = 0.01 /obj/item/clothing/head/helmet/riot/knight name = "medieval helmet" diff --git a/code/modules/clothing/head/job_hats.dm b/code/modules/clothing/head/job_hats.dm index 82c8a1d37cd..0e071424169 100644 --- a/code/modules/clothing/head/job_hats.dm +++ b/code/modules/clothing/head/job_hats.dm @@ -198,6 +198,7 @@ "Drask" = 'icons/mob/clothing/species/drask/head.dmi', "Vox" = 'icons/mob/clothing/species/vox/head.dmi' ) + permeability_coefficient = 0.01 /obj/item/clothing/head/surgery/purple name = "purple surgical cap" diff --git a/code/modules/clothing/masks/misc_masks.dm b/code/modules/clothing/masks/misc_masks.dm index bc5accf82f0..15a9b6b58a6 100644 --- a/code/modules/clothing/masks/misc_masks.dm +++ b/code/modules/clothing/masks/misc_masks.dm @@ -194,6 +194,9 @@ "Drask" = 'icons/mob/clothing/species/drask/mask.dmi' ) +/obj/item/clothing/mask/surgical/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ANTI_VIRAL, "inherent") /obj/item/clothing/mask/surgical/attack_self__legacy__attackchain(mob/user) adjustmask(user) diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index e81144c7d85..1da95515bf1 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -20,6 +20,7 @@ ) actions_types = list(/datum/action/item_action/button) adjust_flavour = "unbutton" + permeability_coefficient = 0.35 /obj/item/clothing/suit/storage/labcoat/medical name = "medical doctor's labcoat" @@ -33,6 +34,7 @@ icon_state = "labcoat_cmo_open" item_state = "labcoat_cmo_open" insert_max = 2 + permeability_coefficient = 0.2 /obj/item/clothing/suit/storage/labcoat/mad name = "mad scientist's labcoat" @@ -57,6 +59,7 @@ desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has green medical markings." icon_state = "labcoat_viro_open" item_state = "labcoat_viro_open" + permeability_coefficient = 0.2 /obj/item/clothing/suit/storage/labcoat/science name = "scientist labcoat" diff --git a/code/modules/clothing/under/jobs/medical_jumpsuits.dm b/code/modules/clothing/under/jobs/medical_jumpsuits.dm index e9f5cad37ec..fca0d58ba18 100644 --- a/code/modules/clothing/under/jobs/medical_jumpsuits.dm +++ b/code/modules/clothing/under/jobs/medical_jumpsuits.dm @@ -28,7 +28,7 @@ /obj/item/clothing/under/rank/medical/doctor name = "medical doctor's jumpsuit" - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + desc = "It's made of a special fiber that provides protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." icon_state = "medical" item_state = "medical" item_color = "medical" @@ -90,35 +90,36 @@ /obj/item/clothing/under/rank/medical/scrubs name = "blue medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + desc = "It's made of a special fiber that provides protection against biohazards. This one is in baby blue." icon_state = "scrubsblue" item_color = "scrubsblue" + permeability_coefficient = 0.1 /obj/item/clothing/under/rank/medical/scrubs/green name = "green medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + desc = "It's made of a special fiber that provides protection against biohazards. This one is in dark green." icon_state = "scrubsgreen" item_color = "scrubsgreen" /obj/item/clothing/under/rank/medical/scrubs/purple name = "purple medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + desc = "It's made of a special fiber that provides protection against biohazards. This one is in deep purple." icon_state = "scrubspurple" item_color = "scrubspurple" /obj/item/clothing/under/rank/medical/scrubs/coroner name = "coroner's scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." + desc = "It's made of a special fiber that provides protection against biohazards. This one is as dark as an emo's poetry." icon_state = "scrubsblack" item_color = "scrubsblack" /obj/item/clothing/under/rank/medical/chemist name = "chemist's jumpsuit" - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + desc = "It's made of a special fiber that gives minor protection against biohazards. It has a chemist rank stripe on it." icon_state = "chemistry" item_state = "chemistry" item_color = "chemistry" - permeability_coefficient = 0.50 + permeability_coefficient = 0.3 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 50, ACID = 95) /obj/item/clothing/under/rank/medical/chemist/skirt @@ -133,7 +134,7 @@ icon_state = "paramedic" item_state = "paramedic" item_color = "paramedic" - permeability_coefficient = 0.50 + permeability_coefficient = 0.3 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 5, FIRE = 0, ACID = 0) /obj/item/clothing/under/rank/medical/paramedic/skirt diff --git a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm index 57126c15280..7b804f7da43 100644 --- a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm +++ b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm @@ -12,6 +12,7 @@ item_color = "plasmaman" dyeable = TRUE dyeing_key = DYE_REGISTRY_PLASMAMEN + permeability_coefficient = 0.6 var/next_extinguish = 0 var/extinguish_cooldown = 10 SECONDS diff --git a/code/modules/clothing/under/jobs/plasmamen/medsci.dm b/code/modules/clothing/under/jobs/plasmamen/medsci.dm index 424540914b4..0924ea1300d 100644 --- a/code/modules/clothing/under/jobs/plasmamen/medsci.dm +++ b/code/modules/clothing/under/jobs/plasmamen/medsci.dm @@ -4,6 +4,7 @@ icon_state = "doctor_envirosuit" item_state = "doctor_envirosuit" item_color = "doctor_envirosuit" + permeability_coefficient = 0.1 /obj/item/clothing/under/plasmaman/cmo name = "chief medical officer's plasma envirosuit" @@ -11,6 +12,7 @@ icon_state = "cmo_envirosuit" item_state = "cmo_envirosuit" item_color = "cmo_envirosuit" + permeability_coefficient = 0.1 /obj/item/clothing/under/plasmaman/science name = "science plasma envirosuit" diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 9e1274e53d3..1c160c90eef 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -1,5 +1,9 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) /datum/event/disease_outbreak + // We only want the announcement to happen after the virus has spawned on station + announceWhen = -1 + // Keep the event running until we announce it + noAutoEnd = TRUE /// The type of disease that patient zero will be infected with. var/datum/disease/chosen_disease @@ -11,6 +15,8 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) var/static/list/transmissable_symptoms = list() var/static/list/diseases_minor = list() var/static/list/diseases_moderate_major = list() + var/force_disease_time = 300 + var/list/infected_players = list() /datum/event/disease_outbreak/setup() if(isemptylist(diseases_minor) && isemptylist(diseases_moderate_major)) @@ -18,49 +24,90 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) if(isemptylist(transmissable_symptoms)) populate_symptoms() var/datum/disease/virus - if(prob(50)) - switch(severity) - if(EVENT_LEVEL_MUNDANE) - virus = pick(diseases_minor) - if(EVENT_LEVEL_MODERATE) + // Either choose a type to create a normal disease from or create an advanced disease + switch(severity) + if(EVENT_LEVEL_MUNDANE) + virus = pick(diseases_minor) + if(EVENT_LEVEL_MODERATE) + if(prob(50)) virus = pick(diseases_moderate_major) else - stack_trace("Disease Outbreak: Invalid Event Level [severity]. Expected: 1-2") - virus = /datum/disease/cold - chosen_disease = new virus() - else - if(severity == EVENT_LEVEL_MODERATE) - chosen_disease = create_virus(severity * pick(2,3)) //50% chance for a major disease instead of a moderate one - else + chosen_disease = create_virus(severity * 2) + if(EVENT_LEVEL_MAJOR) chosen_disease = create_virus(severity * 2) + if(virus) + chosen_disease = new virus chosen_disease.carrier = TRUE /datum/event/disease_outbreak/start() - GLOB.current_pending_diseases += chosen_disease + GLOB.current_pending_diseases += list(list("disease" = chosen_disease, "event" = src)) for(var/mob/M as anything in GLOB.dead_mob_list) //Announce outbreak to dchat if(istype(chosen_disease, /datum/disease/advance)) - var/datum/disease/advance/temp_disease = chosen_disease.Copy() - to_chat(M, "Disease outbreak: The next new arrival is a carrier of [chosen_disease.name]! A \"[chosen_disease.severity]\" disease with the following symptoms: [english_list(temp_disease.symptoms)]") + var/datum/disease/advance/temp_disease = chosen_disease + to_chat(M, chat_box_examine("Disease outbreak: The next new arrival is a carrier of [chosen_disease.name]! A \"[chosen_disease.severity]\" disease with the following symptoms:\n[english_list(temp_disease.symptoms)]\nand the following stats:\n[english_map(temp_disease.GenerateProperties())]")) else - to_chat(M, "Disease outbreak: The next new arrival is a carrier of a \"[chosen_disease.severity]\" disease: [chosen_disease.name]!") + to_chat(M, chat_box_examine("Disease outbreak: The next new arrival is a carrier of a \"[chosen_disease.severity]\" disease: [chosen_disease.name]!")) + +/datum/event/disease_outbreak/announce() + switch(severity) + if(EVENT_LEVEL_MAJOR) + GLOB.major_announcement.Announce("Lethal viral pathogen detected aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/effects/siren-spooky.ogg', new_sound2 = 'sound/AI/outbreak_virus.ogg') + if(EVENT_LEVEL_MODERATE) + GLOB.minor_announcement.Announce("Moderate contagion detected aboard [station_name()].", new_sound = 'sound/misc/notice2.ogg', new_title = "Contagion Alert") + if(EVENT_LEVEL_MUNDANE) + GLOB.minor_announcement.Announce("Minor contagion detected aboard [station_name()].", new_sound = 'sound/misc/notice2.ogg', new_title = "Contagion Alert") + // We did our announcement, the event no longer needs to run + kill() + +/datum/event/disease_outbreak/process() + if(activeFor == force_disease_time) + for(var/list/disease_event in GLOB.current_pending_diseases) + if(chosen_disease == disease_event["disease"]) + for(var/mob/living/carbon/human/player in GLOB.player_list) + if((player.name in GLOB.crew_list) && player.ForceContractDisease(chosen_disease, TRUE, TRUE)) + GLOB.current_pending_diseases -= list(disease_event) + break + break + if(length(infected_players) > 2 && announceWhen <= 0) + announceWhen = activeFor + 180 + . = ..() //Creates a virus with a harmful effect, guaranteed to be spreadable by contact or airborne /datum/event/disease_outbreak/proc/create_virus(max_severity = 6) - var/datum/disease/advance/A = new /datum/disease/advance - A.symptoms = A.GenerateSymptomsBySeverity(max_severity - 1, max_severity, 2) //Choose "Payload" symptoms - A.AssignProperties(A.GenerateProperties()) - var/list/symptoms_to_try = transmissable_symptoms.Copy() - while(length(symptoms_to_try)) - if(A.spread_text != "Blood") - break - if(length(A.symptoms) < VIRUS_SYMPTOM_LIMIT) //Ensure the virus is spreadable by adding symptoms that boost transmission - var/datum/symptom/TS = pick_n_take(symptoms_to_try) - A.AddSymptom(new TS) - else - popleft(A.symptoms) //We have a full symptom list but are still not transmittable. Try removing one of the "payloads" + var/datum/disease/advance/A = new /datum/disease/advance(_event = UID()) + A.clear_symptoms() + // Base properties get buffs depending on severity + var/list/properties_to_buff = A.base_properties.Copy() - list("transmissibility") + for(var/i = 0, i < max_severity / 2, i++) + A.base_properties[pick(properties_to_buff)]++ + A.base_properties["stealth"] += max_severity // Stealth gets an additional bonus since most symptoms reduce it a fair bit. + var/spread_threhsold = 4 + // Chance for it to be extra spready, scales quadratically with severity + if(prob((max_severity ** 2) * 3)) + spread_threhsold = 5 + if(prob((max_severity ** 2) * 1.5)) + spread_threhsold = 6 + + // If we have extra spread include either cough or sneeze + if(spread_threhsold >= 5) + var/symptom_path = pick(/datum/symptom/cough, /datum/symptom/sneeze) + A.add_symptom_path(symptom_path) + + // Generate payload. 2-3 symptoms that actually do something + A.symptoms += A.GenerateSymptomsBySeverity(max_severity - 1, max_severity, 1) + A.symptoms += A.GenerateSymptomsBySeverity(max_severity - 3, max_severity - 1, rand(1, 2)) + + var/symptom_list = transmissable_symptoms.Copy() + + // Add symptoms that increase transmittability to fill the rest + while(length(A.symptoms) < VIRUS_SYMPTOM_LIMIT) + var/symptom_path = pick_n_take(symptom_list) + A.add_symptom_path(symptom_path) + + // Add extra transmissibility through base properties as needed + A.base_properties["transmissibility"] = max(0, spread_threhsold - (A.totaltransmissibility() - length(A.symptoms))) - A.AssignProperties(A.GenerateProperties()) A.name = pick(GLOB.alphabet_uppercase) + num2text(rand(1,9)) + pick(GLOB.alphabet_uppercase) + num2text(rand(1,9)) + pick("v", "V", "-" + num2text(GLOB.game_year), "") A.Refresh() return A @@ -71,13 +118,15 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) if(is_type_in_list(CD, disease_blacklist)) continue switch(CD.severity) - if(NONTHREAT, MINOR) + if(VIRUS_NONTHREAT, VIRUS_MINOR) diseases_minor += candidate - if(MEDIUM, HARMFUL, DANGEROUS, BIOHAZARD) + if(VIRUS_MEDIUM, VIRUS_HARMFUL, VIRUS_DANGEROUS, VIRUS_BIOHAZARD) diseases_moderate_major += candidate /datum/event/disease_outbreak/proc/populate_symptoms() - for(var/candidate in subtypesof(/datum/symptom)) + // Disease events shouldn't just resolve themselves + var/list/candidate_list = subtypesof(/datum/symptom) - /datum/symptom/heal/longevity + for(var/candidate in candidate_list) var/datum/symptom/CS = candidate - if(initial(CS.transmittable) > 1) + if(initial(CS.transmissibility) > 0) transmissable_symptoms += candidate diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 7709bd53f9f..3ce4062a661 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -209,7 +209,8 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 20, is_one_shot = TRUE), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Shadow Demon", /datum/event/spawn_slaughter/shadow, 20, is_one_shot = TRUE), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Demonic Incursion", /datum/event/demon_incursion, 20, list(ASSIGNMENT_SECURITY = 3)), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Immovable Rod", /datum/event/immovable_rod, 0, list(ASSIGNMENT_ENGINEER = 10), TRUE) + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Immovable Rod", /datum/event/immovable_rod, 0, list(ASSIGNMENT_ENGINEER = 10), TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Disease Outbreak", /datum/event/disease_outbreak, 15, list(ASSIGNMENT_MEDICAL = 4), TRUE), //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE) //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Pulse Demon Infiltration", /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE) ) diff --git a/code/modules/food_and_drinks/food_base.dm b/code/modules/food_and_drinks/food_base.dm index 62999a767ce..1d087303cbc 100644 --- a/code/modules/food_and_drinks/food_base.dm +++ b/code/modules/food_and_drinks/food_base.dm @@ -148,6 +148,16 @@ /obj/item/food/proc/On_Consume(mob/M, mob/user) if(!user) return + // Add viruses where needed + if(length(M.viruses)) + AddComponent(/datum/component/viral_contamination, M.viruses) + var/datum/reagent/blood/blood_contained = locate() in reagents.reagent_list + // Infect contained blood as well for splash reactions + if(blood_contained?.data["viruses"]) + var/list/blood_viruses = blood_contained.data["viruses"] + blood_viruses |= M.viruses.Copy() + blood_contained.data["viruses"] = blood_viruses + SEND_SIGNAL(src, COMSIG_MOB_REAGENT_EXCHANGE, M) if(!reagents.total_volume) if(M == user) to_chat(user, "You finish eating [src].") diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index f0f0c494c1c..8358c63840f 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -857,11 +857,14 @@ /obj/machinery/smartfridge/secure/chemistry/virology/preloaded/Initialize(mapload) starting_items = list( /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/tracking_agar = 1, + /obj/item/reagent_containers/glass/bottle/stable_agar = 1, /obj/item/reagent_containers/glass/bottle/cold = 1, /obj/item/reagent_containers/glass/bottle/flu_virion = 1, /obj/item/reagent_containers/glass/bottle/mutagen = 1, /obj/item/reagent_containers/glass/bottle/plasma = 1, /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1, + /obj/item/reagent_containers/glass/bottle/sterilizine, /obj/item/storage/lockbox/vials = 2 ) . = ..() @@ -877,6 +880,8 @@ /obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate/Initialize(mapload) starting_items = list( /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/tracking_agar = 1, + /obj/item/reagent_containers/glass/bottle/stable_agar = 1, /obj/item/reagent_containers/glass/bottle/cold = 1, /obj/item/reagent_containers/glass/bottle/flu_virion = 1, /obj/item/reagent_containers/glass/bottle/mutagen = 1, diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 1bd9d643cb3..aacda083452 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -27,13 +27,14 @@ for(var/thing in viruses) var/datum/disease/D = thing - if(D.IsSpreadByTouch()) - user.ContractDisease(D) + if(D.spread_flags >= SPREAD_CONTACT_GENERAL) + can_spread_disease(D, SPREAD_CONTACT_GENERAL) && user.ContractDisease(D, SPREAD_CONTACT_HANDS) for(var/thing in user.viruses) var/datum/disease/D = thing - if(D.IsSpreadByTouch()) - ContractDisease(D) + var/spread_method = min(D.spread_flags, SPREAD_CONTACT_GENERAL) + if(spread_method >= SPREAD_CONTACT_HANDS) + user.can_spread_disease(D, SPREAD_CONTACT_HANDS) && ContractDisease(D, spread_method) if(IS_HORIZONTAL(src) && length(surgeries)) if(user.a_intent == INTENT_HELP) diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm index b64c85aac29..22b4f4d8a55 100644 --- a/code/modules/mob/living/carbon/carbon_procs.dm +++ b/code/modules/mob/living/carbon/carbon_procs.dm @@ -123,6 +123,7 @@ if(blood) if(T) add_splatter_floor(T) + blood_volume = max(blood_volume - lost_nutrition, 0) if(should_confuse) adjustBruteLoss(3) else @@ -132,6 +133,11 @@ if(should_confuse) adjustToxLoss(-3) + // Try to infect the people we hit with our viruses + for(var/mob/living/carbon/to_infect in T.contents) + for(var/datum/disease/illness in viruses) + to_infect.ContractDisease(illness) + T = get_step(T, dir) if(T.is_blocked_turf()) break diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 4b828e9495d..4b56925b552 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -1142,7 +1142,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u if(!HAS_TRAIT(user, TRAIT_NON_INFECTIOUS_ZOMBIE)) if(!target.HasDisease(/datum/disease/zombie)) var/datum/disease/zombie/zomb = new /datum/disease/zombie - if(target.CanContractDisease(zomb)) // biosuit aint going to protect you buddy + if(target.can_contract_disease(zomb)) // biosuit aint going to protect you buddy target.ForceContractDisease(zomb) target.Dizzy(10 SECONDS) target.Confused(10 SECONDS) diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 3302fbc5ad9..7d4b6160065 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -406,7 +406,7 @@ return for(var/datum/disease/D as anything in C.viruses) - if(!(D.visibility_flags & HIDDEN_SCANNER && D.visibility_flags & HIDDEN_PANDEMIC) && D.severity != NONTHREAT && (D.stage > 1 || D.spread_flags & AIRBORNE)) + if((!(D.visibility_flags & VIRUS_HIDDEN_SCANNER) || (D.GetDiseaseID() in GLOB.detected_advanced_diseases["[z]"])) && D.severity != VIRUS_NONTHREAT && (D.stage > 1 || D.spread_flags & SPREAD_AIRBORNE)) return TRUE //Medbots see viruses that aren't fully hidden and have developed enough/are airborne, ignoring safe viruses /mob/living/simple_animal/bot/medbot/proc/select_medication(mob/living/carbon/C, beaker_injection) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index b5d2892c9ea..da68bba9731 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -385,7 +385,9 @@ GLOB.data_core.manifest_inject(character) AnnounceArrival(character, rank, join_message) - if(length(GLOB.current_pending_diseases) && character.ForceContractDisease(GLOB.current_pending_diseases[1], TRUE, TRUE)) + if(length(GLOB.current_pending_diseases) && character.ForceContractDisease(GLOB.current_pending_diseases[1]["disease"], TRUE, TRUE)) + var/datum/event/disease_outbreak/outbreak = GLOB.current_pending_diseases[1]["event"] + outbreak.force_disease_time = 0 popleft(GLOB.current_pending_diseases) if(GLOB.summon_guns_triggered) give_guns(character) diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index 12f45d2ceeb..62293f6897a 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -1,31 +1,106 @@ -/obj/machinery/computer/pandemic +#define ANALYSIS_DIFFICULTY_BASE 100 +#define ANALYSIS_DIFFICULTY_INCREMENT 40 +#define ANALYSIS_DURATION_BASE (0.5 MINUTES) +#define STAGE_CONTRIBUTION 40 +#define GENE_CONTRIBUTION 60 + +/// list of known advanced disease ids. If an advanced disease isn't here it will display as unknown disease on scanners +/// Initialized with the id of the flu and cold samples from the virologist's fridge in the pandemic's init +GLOBAL_LIST_EMPTY(known_advanced_diseases) +GLOBAL_LIST_EMPTY(detected_advanced_diseases) + +/obj/machinery/pandemic name = "PanD.E.M.I.C 2200" desc = "Used to work with viruses." icon = 'icons/obj/chemical.dmi' icon_state = "pandemic0" - circuit = /obj/item/circuitboard/pandemic idle_power_consumption = 20 resistance_flags = ACID_PROOF + density = TRUE + anchored = TRUE + /// The base analysis time which is later modified by samples of uniqute stages and symptom prediction + var/analysis_difficulty_base = ANALYSIS_DIFFICULTY_BASE + /// How difficult the disease is to analyze. This sets the requirements for how much you need to contribute with things like stages and symptom predictions + var/analysis_difficulty = 0 + /// The contribution of various factors towards analysis. The "max" values are only for the UI + var/analysis_contributions = list( + "Stage Data" = list("amount" = 0, "max" = 5 * STAGE_CONTRIBUTION), + "Viral Gene Data" = list("amount" = 0, "max" = 2 * GENE_CONTRIBUTION), + "Symptom Data" = list("amount" = 0, "max" = 0.5 * ANALYSIS_DIFFICULTY_BASE) + ) + /// The time at which the analysis of a disease will be done. Calculated at the begnining of analysis using analysis_difficulty and symptoms symptom_guesses. + var/analysis_time + /// How much time analysis will take. Depends on components + var/analysis_duration + /// Whether the PANDEMIC is currently analyzing an advanced disease + var/analyzing = FALSE + /// Whether to keep the report screen up. This is so it can stay persistent while the UI is closed + var/reporting = FALSE + /// Can our sample be analyzed + var/valid_sample = FALSE + /// ID of the disease being analyzed + var/analyzed_ID = "" + /// Index of the disease being analyzed + var/analyzed_index + /// List of all symptoms. Gets filled in Initialize(). + var/symptom_list = list() var/temp_html = "" var/printing = null var/wait = null var/selected_strain_index = 1 var/obj/item/reagent_containers/beaker = null + /// The current symptom predictions + var/list/predictions = list( + "No Prediction", + "No Prediction", + "No Prediction", + "No Prediction", + "No Prediction", + "No Prediction" + ) -/obj/machinery/computer/pandemic/Initialize(mapload) +/obj/machinery/pandemic/Initialize(mapload) . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/pandemic(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + RefreshParts() + GLOB.pandemics |= src + var/datum/symptom/S + symptom_list += list("No Prediction") + for(var/symptom_path in GLOB.list_symptoms) + // I don't know a way to access the name of something with only the path without creating an instance. + S = new symptom_path() + symptom_list += list(S.name) + qdel(S) + // We init the list for the Z level here so that we can know it is loaded when we do. + if(!(z in GLOB.known_advanced_diseases)) + GLOB.known_advanced_diseases += list("[z]" = list("4:origin", "24:origin")) update_icon() -/obj/machinery/computer/pandemic/Destroy() +/obj/machinery/pandemic/RefreshParts() + var/manip_rating = 0 + var/laser_rating = 0 + for(var/obj/item/stock_parts/manipulator/manip in component_parts) + manip_rating += manip.rating + for(var/obj/item/stock_parts/micro_laser/laser in component_parts) + laser_rating += laser.rating + + analysis_difficulty_base = ANALYSIS_DIFFICULTY_BASE * (9 / (manip_rating + 8)) + analysis_duration = ANALYSIS_DURATION_BASE * (1 / laser_rating) + find_analysis_requirements() + +/obj/machinery/pandemic/Destroy() GLOB.pandemics -= src return ..() -/obj/machinery/computer/pandemic/set_broken() +/obj/machinery/pandemic/proc/set_broken() stat |= BROKEN update_icon() -/obj/machinery/computer/pandemic/proc/GetViruses() +/obj/machinery/pandemic/proc/GetViruses() if(beaker && beaker.reagents) if(length(beaker.reagents.reagent_list)) var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list @@ -34,12 +109,12 @@ var/list/viruses = BL.data["viruses"] return viruses -/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index) +/obj/machinery/pandemic/proc/GetVirusByIndex(index) var/list/viruses = GetViruses() if(viruses && index > 0 && index <= length(viruses)) return viruses[index] -/obj/machinery/computer/pandemic/proc/GetResistances() +/obj/machinery/pandemic/proc/GetResistances() if(beaker && beaker.reagents) if(length(beaker.reagents.reagent_list)) var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list @@ -48,17 +123,17 @@ var/list/resistances = BL.data["resistances"] return resistances -/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index) +/obj/machinery/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) +/obj/machinery/pandemic/proc/GetVirusTypeByIndex(index) var/datum/disease/D = GetVirusByIndex(index) if(D) return D.GetDiseaseID() -/obj/machinery/computer/pandemic/proc/replicator_cooldown(waittime) +/obj/machinery/pandemic/proc/replicator_cooldown(waittime) wait = 1 update_icon() spawn(waittime) @@ -66,18 +141,18 @@ update_icon() playsound(loc, 'sound/machines/ping.ogg', 30, 1) -/obj/machinery/computer/pandemic/update_icon_state() +/obj/machinery/pandemic/update_icon_state() if(stat & BROKEN) icon_state = (beaker ? "pandemic1_b" : "pandemic0_b") return icon_state = "pandemic[(beaker)?"1":"0"][(has_power()) ? "" : "_nopower"]" -/obj/machinery/computer/pandemic/update_overlays() +/obj/machinery/pandemic/update_overlays() . = list() if(!wait) . += "waitlight" -/obj/machinery/computer/pandemic/proc/create_culture(name, bottle_type = "culture", cooldown = 50) +/obj/machinery/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 = "bottle" B.scatter_atom() @@ -85,7 +160,118 @@ B.name = "[name] [bottle_type] bottle" return B -/obj/machinery/computer/pandemic/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) +/// Find the time it would take to analyze the current disease before any symptom predictions are made +/obj/machinery/pandemic/proc/find_analysis_requirements() + if(!beaker) + return + analysis_contributions["Stage Data"]["amount"] = 0 + analysis_contributions["Viral Gene Data"]["amount"] = 0 + analysis_contributions["Symptom Data"]["amount"] = 0 + analysis_contributions["Symptom Data"]["max"] = 1 + valid_sample = TRUE + var/strains = 0 + var/list/stages = list() + var/current_strain = "" + var/stealth_init = FALSE + var/stealth = 0 + var/resistance = 0 + var/max_stages = 0 + var/known_symptoms = 0 + var/num_symptoms = 0 + var/datum/reagent/blood/blood_sample = locate() in beaker.reagents.reagent_list + var/datum/reagent/virus_genes/gene_sample = locate() in beaker.reagents.reagent_list + if(blood_sample && blood_sample.data && blood_sample.data["viruses"]) + for(var/datum/disease/advance/to_analyze in blood_sample.data["viruses"]) + // Note down number of symptoms and number of known symptoms. + // We only use these values if we find one strain, so no need to worry about overwriting + known_symptoms = length(GLOB.detected_advanced_diseases["[z]"][to_analyze.GetDiseaseID()]["known_symptoms"]) + num_symptoms = length(to_analyze.symptoms) + // Automatically analyze if the tracker stores the ID of an analyzed disease + if(to_analyze.tracker && (to_analyze.tracker in GLOB.known_advanced_diseases["[z]"])) + if(!(to_analyze.GetDiseaseID() in GLOB.known_advanced_diseases["[z]"])) + GLOB.known_advanced_diseases["[z]"] += list(to_analyze.GetDiseaseID()) + // If we know this disease there's no need to keep going. + if(to_analyze.GetDiseaseID() in GLOB.known_advanced_diseases["[z]"]) + return + // If we somehow got multiple strains we can't do analysis. + if(to_analyze.strain != current_strain || current_strain == "") + strains++ + if(strains > 1) + valid_sample = FALSE + SStgui.update_uis(src, TRUE) + return + current_strain = to_analyze.strain + // Calculate the stealth and resistance values. We only need to do this once + if(!stealth_init) + for(var/datum/symptom/S in to_analyze.symptoms) + stealth += S.stealth + resistance += S.resistance + stealth += to_analyze.base_properties["stealth"] + resistance += to_analyze.base_properties["resistance"] + max_stages = to_analyze.max_stages + stealth_init = TRUE + // If we found a unique stage count it + if(!(to_analyze.stage in stages)) + analysis_contributions["Stage Data"]["amount"] += STAGE_CONTRIBUTION + stages += to_analyze.stage + + if(gene_sample && gene_sample.data) + for(var/key in gene_sample.data) + for(var/strain_id in gene_sample.data[key]) + if(strain_id == current_strain) + analysis_contributions["Viral Gene Data"]["amount"] += GENE_CONTRIBUTION + break + + var/power_level = (stealth + resistance) + // Make sure we don't runtime if empty + if(max_stages) + analysis_difficulty = analysis_difficulty_base + ANALYSIS_DIFFICULTY_INCREMENT * (1.17 ** power_level) + else + analysis_difficulty = analysis_difficulty_base + + analysis_contributions["Symptom Data"]["amount"] = num_symptoms ? analysis_difficulty * known_symptoms / (2 * num_symptoms) : 0 + analysis_contributions["Symptom Data"]["max"] = max(analysis_difficulty / 2, 1) + + SStgui.update_uis(src, TRUE) + +/obj/machinery/pandemic/proc/start_analysis(strain_index) + var/datum/disease/advance/virus = GetVirusByIndex(strain_index) + analyzing = TRUE + analysis_time = world.time + analysis_duration + analyzed_ID = virus.GetDiseaseID() + analyzed_index = strain_index + +/obj/machinery/pandemic/proc/analyze(strain_index) + reporting = TRUE + var/datum/disease/advance/virus = GetVirusByIndex(analyzed_index) + var/symptom_names = list() + for(var/datum/symptom/current_symptom in virus.symptoms) + symptom_names += list(current_symptom.name) + for(var/name in predictions) + if(!name || name == "No Prediction") + continue + if(name in symptom_names) + GLOB.detected_advanced_diseases["[z]"][analyzed_ID]["known_symptoms"] |= list(name) + + var/found_symptoms = length(GLOB.detected_advanced_diseases["[z]"][analyzed_ID]["known_symptoms"]) + // Correct symptom symptom_guesses reduce the final analysis time by up to half of the base time. + analysis_contributions["Symptom Data"]["max"] = analysis_difficulty / 2 + analysis_contributions["Symptom Data"]["amount"] = analysis_difficulty * found_symptoms / (2 * length(virus.symptoms)) + + var/total_contribution = 0 + for(var/factor in analysis_contributions) + total_contribution += analysis_contributions[factor]["amount"] + + return (analysis_difficulty - total_contribution) <= 0 + +/obj/machinery/pandemic/process() + if(analyzing && analysis_time < world.time) + if(analyze(analyzed_index)) + GLOB.known_advanced_diseases["[z]"] += analyzed_ID + analyzing = FALSE + SStgui.update_uis(src, TRUE) + +/obj/machinery/pandemic/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) if(..()) return if(inoperable()) @@ -111,9 +297,9 @@ return var/type = virus.GetDiseaseID() if(!ispath(type)) - var/datum/disease/advance/A = GLOB.archive_diseases[type] - if(A) - D = new A.type(0, A) + var/datum/disease/advance/A = virus + if(istype(A)) + D = A.Copy() else if(type) if(type in GLOB.diseases) // Make sure this is a disease D = new type(0, null) @@ -158,6 +344,10 @@ var/obj/item/reagent_containers/glass/bottle/B = create_culture(vaccine_name, "vaccine", 200) B.reagents.add_reagent("vaccine", 15, list(vaccine_type)) + if("remove_from_database") + if(params["strain_id"] in GLOB.known_advanced_diseases["[z]"]) + GLOB.known_advanced_diseases["[z]"] -= params["strain_id"] + SStgui.update_uis(src, TRUE) if("eject_beaker") eject_beaker() update_static_data(ui.user) @@ -192,15 +382,16 @@ 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() + AD.Refresh(TRUE, FALSE, FALSE, FALSE) + var/datum/reagent/blood = locate() in beaker.reagents.reagent_list + if(blood.data && blood.data["viruses"]) + for(var/datum/disease/advance/virus in blood.data["viruses"]) + virus.AssignName(new_name) update_static_data(ui.user) if("switch_strain") var/strain_index = text2num(params["strain_index"]) @@ -212,26 +403,52 @@ atom_say("Unable to find requested strain.") return selected_strain_index = strain_index; + if("analyze_strain") + start_analysis(params["strain_index"]) + if("set_prediction") + set_predictions(text2num(params["pred_index"]), params["pred_value"]) + SStgui.update_uis(src) + if("close_report") + reporting = FALSE else return FALSE -/obj/machinery/computer/pandemic/ui_state(mob/user) +/obj/machinery/pandemic/proc/set_predictions(index, value) + predictions[index] = value + +/obj/machinery/pandemic/ui_state(mob/user) return GLOB.default_state -/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui = null) +/obj/machinery/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() -/obj/machinery/computer/pandemic/ui_data(mob/user) +/obj/machinery/pandemic/ui_data(mob/user) var/datum/reagent/blood/Blood = null + var/can_analyze = valid_sample + var/contribution_list = list() + var/contribution_sum = 0 if(beaker) var/datum/reagents/R = beaker.reagents for(var/datum/reagent/blood/B in R.reagent_list) if(B) Blood = B break + if(Blood) + if(Blood.data && Blood.data["viruses"]) + for(var/datum/disease/advance/virus in Blood.data["viruses"]) + if((virus.GetDiseaseID() in GLOB.known_advanced_diseases["[z]"])) + can_analyze = FALSE + break + for(var/factor in analysis_contributions) + contribution_list += list(list( + "factor" = factor, + "amount" = analysis_contributions[factor]["amount"], + "maxAmount" = analysis_contributions[factor]["max"], + )) + contribution_sum += analysis_contributions[factor]["amount"] var/list/data = list( "synthesisCooldown" = wait ? TRUE : FALSE, @@ -239,11 +456,21 @@ "beakerContainsBlood" = Blood ? TRUE : FALSE, "beakerContainsVirus" = length(Blood?.data["viruses"]) != 0, "selectedStrainIndex" = selected_strain_index, + "analysisTime" = analysis_time > world.time ? analysis_time - world.time : 0, + "analysisDuration" = analysis_duration, + "analysisDifficulty" = analysis_difficulty, + "analysisContributions" = contribution_list, + "totalContribution" = contribution_sum, + "canAnalyze" = can_analyze, + "analyzing" = analyzing, + "reporting" = reporting, + "symptom_names" = symptom_list, + "predictions" = predictions, ) return data -/obj/machinery/computer/pandemic/ui_static_data(mob/user) +/obj/machinery/pandemic/ui_static_data(mob/user) var/list/data = list() . = data @@ -256,59 +483,58 @@ break var/list/strains = list() - for(var/datum/disease/D in GetViruses()) - if(D.visibility_flags & HIDDEN_PANDEMIC) + for(var/datum/disease/blood_disease in GetViruses()) + if(!(blood_disease.GetDiseaseID() in GLOB.detected_advanced_diseases["[z]"])) + GLOB.detected_advanced_diseases["[z]"] += list(blood_disease.GetDiseaseID() = list("known_symptoms" = list())) + if(blood_disease.visibility_flags & VIRUS_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, - )) - + var/list/symptoms = blood_disease.get_pandemic_symptoms(z) + var/list/base_stats = blood_disease.get_pandemic_base_stats() + var/known = blood_disease.is_known(z) strains += list(list( - "commonName" = D.name, - "description" = D.desc, + "commonName" = known ? blood_disease.name : "Unknown strain", + "description" = known ? blood_disease.desc : "Unknown strain", + "strainID" = blood_disease.get_strain_id(), + "strainFullID" = blood_disease.get_full_strain_id(), + "diseaseID" = blood_disease.get_ui_id(), + "sample_stage" = blood_disease.get_stage(), + "known" = known, "bloodDNA" = Blood.data["blood_DNA"], "bloodType" = Blood.data["blood_type"], - "diseaseAgent" = D.agent, - "possibleTreatments" = D.cure_text, - "transmissionRoute" = D.spread_text, + "diseaseAgent" = blood_disease.agent, + "possibleCures" = known ? blood_disease.cure_text : "Unknown strain", + "RequiredCures" = "[blood_disease.get_required_cures()]", + "Stabilized" = blood_disease.is_stabilized(), + "StrainTracker" = blood_disease.get_tracker(), + "transmissionRoute" = known ? blood_disease.spread_text : "Unknown strain", "symptoms" = symptoms, - "isAdvanced" = istype(D, /datum/disease/advance), + "baseStats" = base_stats, + "isAdvanced" = istype(blood_disease, /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) + var/datum/disease/resisted_disease = GLOB.archive_diseases[resistance] + if(resisted_disease) + resistances += list(resisted_disease.name) else if(resistance) - var/datum/disease/D = new resistance(0, null) - if(D) - resistances += list(D.name) + var/datum/disease/resistance_disease = new resistance(0, null) + if(resistance_disease) + resistances += list(resistance_disease.name) data["resistances"] = resistances + data["analysis_difficulty"] = analysis_difficulty -/obj/machinery/computer/pandemic/proc/eject_beaker() +/obj/machinery/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) +/obj/machinery/pandemic/proc/print_form(datum/disease/advance/D, mob/living/user) D = GLOB.archive_diseases[D.GetDiseaseID()] if(!(printing) && D) var/reason = tgui_input_text(user,"Enter a reason for the release", "Write", multiline = TRUE) @@ -350,7 +576,7 @@ P.name = "Releasing Virus - [D.name]" printing = null -/obj/machinery/computer/pandemic/proc/print_goal_orders() +/obj/machinery/pandemic/proc/print_goal_orders() if(stat & (BROKEN|NOPOWER)) return @@ -370,18 +596,18 @@ P.info = info_text.Join("") P.update_icon() -/obj/machinery/computer/pandemic/attack_ai(mob/user) +/obj/machinery/pandemic/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/computer/pandemic/attack_hand(mob/user) +/obj/machinery/pandemic/attack_hand(mob/user) if(..()) return ui_interact(user) -/obj/machinery/computer/pandemic/attack_ghost(mob/user) +/obj/machinery/pandemic/attack_ghost(mob/user) ui_interact(user) -/obj/machinery/computer/pandemic/item_interaction(mob/living/user, obj/item/used, list/modifiers) +/obj/machinery/pandemic/item_interaction(mob/living/user, obj/item/used, list/modifiers) if(default_unfasten_wrench(user, used, time = 4 SECONDS)) power_change() return @@ -395,17 +621,33 @@ return ITEM_INTERACT_COMPLETE beaker = used - beaker.loc = src + beaker.forceMove(src) to_chat(user, "You add the beaker to the machine.") - SStgui.update_uis(src, TRUE) icon_state = "pandemic1" - + var/datum/reagent/blood/inserted = locate() in beaker.reagents.reagent_list + if(inserted && inserted.data && inserted.data["viruses"]) + for(var/datum/disease/advance/virus in inserted.data["viruses"]) + if(!(virus.GetDiseaseID() in GLOB.detected_advanced_diseases["[z]"])) + GLOB.detected_advanced_diseases["[z]"] += list(virus.GetDiseaseID() = list("known_symptoms" = list())) + log_admin("[key_name(user)] inserted disease [virus] of strain [virus.strain] with the following symptoms: [english_list(virus.symptoms)] into [src] at these coords x: [x] y: [y] z: [z]") + find_analysis_requirements() + SStgui.update_uis(src, TRUE) return ITEM_INTERACT_COMPLETE else return ..() -/obj/machinery/computer/pandemic/screwdriver_act(mob/user, obj/item/I) +/obj/machinery/pandemic/screwdriver_act(mob/living/user, obj/item/I) + if(!default_deconstruction_screwdriver(user, icon_state, icon_state, I)) + return FALSE if(beaker) eject_beaker() - return TRUE - return ..() + return TRUE + +/obj/machinery/pandemic/crowbar_act(mob/living/user, obj/item/I) + return default_deconstruction_crowbar(user, I) + +#undef ANALYSIS_DIFFICULTY_BASE +#undef ANALYSIS_DIFFICULTY_INCREMENT +#undef ANALYSIS_DURATION_BASE +#undef STAGE_CONTRIBUTION +#undef GENE_CONTRIBUTION diff --git a/code/modules/reagents/chemistry/reagents/admin_reagents.dm b/code/modules/reagents/chemistry/reagents/admin_reagents.dm index 87622d97419..8a01dcc9ff8 100644 --- a/code/modules/reagents/chemistry/reagents/admin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/admin_reagents.dm @@ -52,7 +52,7 @@ M.SetJitter(0) for(var/thing in M.viruses) var/datum/disease/D = thing - if(D.severity == NONTHREAT) + if(D.severity == VIRUS_NONTHREAT) continue D.cure(0) ..() diff --git a/code/modules/reagents/chemistry/reagents/disease.dm b/code/modules/reagents/chemistry/reagents/disease.dm index 8bfd678d0dd..6f549d5f54c 100644 --- a/code/modules/reagents/chemistry/reagents/disease.dm +++ b/code/modules/reagents/chemistry/reagents/disease.dm @@ -155,6 +155,12 @@ color = "#899613" // rgb: 137, 150, 19 taste_description = "watery milk" +/datum/reagent/consumable/virus_food/on_mob_life(mob/living/M) + . = ..() + for(var/datum/disease/advance/virus in M.viruses) + if(prob(3)) + virus.progress += 20 + /datum/reagent/mutagen/mutagenvirusfood name = "Mutagenic agar" id = "mutagenvirusfood" @@ -183,3 +189,32 @@ name = "Weakened virus plasma" id = "weakplasmavirusfood" color = "#CEC3C6" // rgb: 206,195,198 + +/datum/reagent/consumable/stable_agar + name = "Stable agar" + id = "stable_agar" + color = "#50BDB0" // rgb: 80,206,192 + +/datum/reagent/consumable/tracking_agar + name = "Tracking agar" + id = "tracking_agar" + color = "#2c8852" // rgb: 45,136,82 + +/datum/reagent/virus_genes + name = "Viral Genetic Material" + id = "virus_genes" + color = "#e0717a" + +/// Depth 1 copy of the lists +/datum/reagent/virus_genes/on_merge(list/_data) + var/list/result = list() + for(var/key in (data + _data)) + result[key] = data[key] + if(_data[key]) + result[key] += _data[key] + data = result + +/datum/reagent/viral_eraser + name = "Viral Eraser" + id = "viral_eraser" + color = "#ffe600" // rgb: 45,136,82 diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index f4b90e799cd..e2f5f389e80 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -29,6 +29,35 @@ var/update_flags = STATUS_UPDATE_HEALTH return ..() | update_flags +/datum/reagent/medicine/sterilizine + name = "Sterilizine" + id = "sterilizine" + description = "Sterilizes wounds in preparation for surgery." + reagent_state = LIQUID + color = "#C8A5DC" // rgb: 200, 165, 220 + taste_description = "antiseptic" + goal_difficulty = REAGENT_GOAL_EASY + + //makes you squeaky clean +/datum/reagent/medicine/sterilizine/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) + M.germ_level -= min(volume*20, M.germ_level) + +/datum/reagent/medicine/sterilizine/reaction_obj(obj/O, volume) + O.germ_level -= min(volume*20, O.germ_level) + SEND_SIGNAL(O, COMSIG_ATOM_DISINFECTED) + +/datum/reagent/medicine/sterilizine/reaction_turf(turf/T, volume) + T.germ_level -= min(volume*20, T.germ_level) + +/datum/reagent/medicine/sterilizine/reaction_temperature(exposed_temperature, exposed_volume) + // Sterilize the container + for(var/datum/reagent/to_disinfect in holder.reagent_list) + if(to_disinfect.data && to_disinfect.data["viruses"]) + to_disinfect?.data["viruses"] = list() + if(isobj(holder.my_atom)) + SEND_SIGNAL(holder.my_atom, COMSIG_ATOM_DISINFECTED) + /datum/reagent/medicine/synaptizine name = "Synaptizine" id = "synaptizine" diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm index 724e60f84ce..74e1cccf5c7 100644 --- a/code/modules/reagents/chemistry/reagents/water.dm +++ b/code/modules/reagents/chemistry/reagents/water.dm @@ -106,7 +106,7 @@ for(var/thing in data["viruses"]) var/datum/disease/D = thing - if(D.spread_flags & SPECIAL || D.spread_flags & NON_CONTAGIOUS) + if(D.spread_flags & SPREAD_SPECIAL || D.spread_flags & SPREAD_NON_CONTAGIOUS) continue if(method == REAGENT_TOUCH) @@ -121,6 +121,13 @@ C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL) ..() +/datum/reagent/blood/reaction_temperature(exposed_temperature, exposed_volume) + // If the blood goes above 60C kill all viruses + if(exposed_temperature > VIRUS_DISINFECTION_TEMP) + data["viruses"] = list() + ..() + + /datum/reagent/blood/on_new(list/data) if(istype(data)) SetViruses(src, data) @@ -151,15 +158,38 @@ // Stop issues with the list changing during mixing. var/list/to_mix = list() + var/list/disease_ids = list() + var/list/stages = list() for(var/datum/disease/advance/AD in mix1) - to_mix += AD - for(var/datum/disease/advance/AD in mix2) - to_mix += AD + if(!(AD.GetDiseaseID() in disease_ids)) + disease_ids += AD.GetDiseaseID() + stages[AD.GetDiseaseID()] = list(AD.stage) + to_mix += AD + if(!(AD.stage in stages[AD.GetDiseaseID()])) + stages[AD.GetDiseaseID()] += list(AD.stage) + to_mix += AD - var/datum/disease/advance/AD = Advance_Mix(to_mix) - if(AD) - var/list/preserve = list(AD) + for(var/datum/disease/advance/AD in mix2) + if(!(AD.GetDiseaseID() in disease_ids)) + disease_ids += AD.GetDiseaseID() + stages[AD.GetDiseaseID()] = list(AD.stage) + to_mix += AD + if(!(AD.stage in stages[AD.GetDiseaseID()])) + stages[AD.GetDiseaseID()] += list(AD.stage) + to_mix += AD + + var/list/result_diseases = list() + if(length(disease_ids) == 1) + for(var/datum/disease/advance/AD in to_mix) + result_diseases += AD.Copy() + else + var/datum/disease/advance/result_virus = Advance_Mix(to_mix) + if(istype(result_virus)) + result_diseases = list(result_virus) + + if(length(result_diseases)) + var/list/preserve = result_diseases for(var/D in data["viruses"]) if(!istype(D, /datum/disease/advance)) preserve += D @@ -187,6 +217,19 @@ blood_prop = new(T) blood_prop.blood_DNA["UNKNOWN DNA STRUCTURE"] = "X*" +/// If irradiated by gamma radiation and there are advanced viruses in the blood become a sample of viral genetic data +/datum/reagent/blood/reaction_radiation(amount, emission_type) + if(emission_type == GAMMA_RAD && amount > 100) + if(data && data["viruses"]) + var/list/strains = list("radiation" = list()) + for(var/datum/disease/advance/virus in data["viruses"]) + strains["radiation"] += virus.strain + if(length(strains["radiation"])) + var/blood_volume = volume + holder.remove_reagent(id, blood_volume) + holder.add_reagent("virus_genes", blood_volume, strains) + + /datum/reagent/vaccine //data must contain virus type name = "Vaccine" @@ -199,7 +242,7 @@ for(var/thing in M.viruses) var/datum/disease/D = thing if(D.GetDiseaseID() in data) - D.cure() + D.make_resistant(M) M.resistances |= data /datum/reagent/vaccine/on_merge(list/incoming_data) diff --git a/code/modules/reagents/chemistry/reagents_datum.dm b/code/modules/reagents/chemistry/reagents_datum.dm index 9249da145d5..9a5f640fb2c 100644 --- a/code/modules/reagents/chemistry/reagents_datum.dm +++ b/code/modules/reagents/chemistry/reagents_datum.dm @@ -62,7 +62,12 @@ data.Cut() data = null -/datum/reagent/proc/reaction_temperature(exposed_temperature, exposed_volume) //By default we do nothing. +/// By default do nothing +/datum/reagent/proc/reaction_temperature(exposed_temperature, exposed_volume) + return + +/// By default do nothing +/datum/reagent/proc/reaction_radiation(amount, emission_type) return /** diff --git a/code/modules/reagents/chemistry/reagents_holder.dm b/code/modules/reagents/chemistry/reagents_holder.dm index c6a46cead10..0fb8603abd5 100644 --- a/code/modules/reagents/chemistry/reagents_holder.dm +++ b/code/modules/reagents/chemistry/reagents_holder.dm @@ -35,7 +35,8 @@ var/list/addiction_threshold_accumulated = list() var/flags -/datum/reagents/New(maximum = 100, temperature_minimum, temperature_maximum) +/datum/reagents/New(maximum = 100, temperature_minimum, temperature_maximum, atom/_my_atom = null) + my_atom = _my_atom maximum_volume = maximum if(temperature_minimum) temperature_min = temperature_minimum @@ -63,9 +64,10 @@ var/datum/chemical_reaction/D = new path() var/list/reaction_ids = list() - if(D && length(D.required_reagents)) - for(var/reaction in D.required_reagents) - reaction_ids += reaction + if(D && (length(D.required_reagents) || length(D.required_catalysts))) + var/list/all_requirements = D.required_reagents + D.required_catalysts + for(var/reagent_id in all_requirements) + reaction_ids += reagent_id // Create filters based on each reagent id in the required reagents list for(var/id in reaction_ids) @@ -73,6 +75,9 @@ GLOB.chemical_reactions_list[id] = list() GLOB.chemical_reactions_list[id] += D break // Don't bother adding ourselves to other reagent ids, it is redundant. + if(my_atom) + RegisterSignal(my_atom, COMSIG_ATOM_RAD_ACT, PROC_REF(radiation_react)) + ADD_TRAIT(my_atom, TRAIT_ABSORB_RADS, "reagents_holder_[UID()]") /** * Removes reagents from the holder until the passed amount is matched. @@ -171,11 +176,16 @@ if(!O.reagents) return R = O.reagents + if(isliving(my_atom)) + var/atom/thing = target + SEND_SIGNAL(thing, COMSIG_MOB_REAGENT_EXCHANGE, my_atom) else if(isliving(target)) var/mob/living/M = target if(!M.reagents) return R = M.reagents + if(isobj(my_atom)) + SEND_SIGNAL(my_atom, COMSIG_MOB_REAGENT_EXCHANGE, target) else if(istype(target, /datum/reagents)) R = target else @@ -186,7 +196,6 @@ var/trans_data = null for(var/A in reagent_list) var/datum/reagent/current_reagent = A - var/current_reagent_transfer = current_reagent.volume * part if(preserve_data) trans_data = copy_data(current_reagent) @@ -199,6 +208,8 @@ if(!no_react) R.handle_reactions() handle_reactions() + R.temperature_react() + temperature_react() return amount /datum/reagents/proc/copy_to(obj/target, amount = 1, multiplier = 1, preserve_data = TRUE, safety = FALSE) @@ -215,12 +226,15 @@ var/current_reagent_transfer = current_reagent.volume * part if(preserve_data) trans_data = copy_data(current_reagent) + R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data) update_total() R.update_total() R.handle_reactions() handle_reactions() + R.temperature_react() + temperature_react() return amount /datum/reagents/proc/set_reagent_temp(new_temp = T0C, react = TRUE) @@ -230,10 +244,17 @@ handle_reactions() /datum/reagents/proc/temperature_react() //Calls the temperature reaction procs without changing the temp. + if(chem_temp > VIRUS_DISINFECTION_TEMP && isobj(my_atom)) + SEND_SIGNAL(my_atom, COMSIG_ATOM_DISINFECTED) for(var/A in reagent_list) var/datum/reagent/current_reagent = A current_reagent.reaction_temperature(chem_temp, 100) +/datum/reagents/proc/radiation_react(atom/source, amount, emission_type) + SIGNAL_HANDLER // COMSIG_ATOM_RAD_ACT + for(var/datum/reagent/current_reagent in reagent_list) + current_reagent.reaction_radiation(amount, emission_type) + /datum/reagents/proc/temperature_reagents(exposed_temperature, divisor = 35, change_cap = 15) //This is what you use to change the temp of a reagent holder. //Do not manually change the reagent unless you know what youre doing. var/difference = abs(chem_temp - exposed_temperature) @@ -275,6 +296,8 @@ if(get_reagent_amount(reagent) < amount) amount = get_reagent_amount(reagent) amount = min(amount, R.maximum_volume - R.total_volume) + if(isliving(my_atom)) + SEND_SIGNAL(target,COMSIG_MOB_REAGENT_EXCHANGE , my_atom) var/trans_data = null for(var/A in reagent_list) var/datum/reagent/current_reagent = A @@ -288,6 +311,7 @@ update_total() R.update_total() R.handle_reactions() + R.temperature_react() return amount @@ -492,7 +516,7 @@ var/preserved_data = null for(var/B in C.required_reagents) if(!preserved_data) - preserved_data = get_data(B) + preserved_data = copy_data_by_id(B) remove_reagent(B, (multiplier * C.required_reagents[B]), safety = TRUE) var/created_volume = C.result_amount*multiplier @@ -690,6 +714,9 @@ if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. if(amount <= 0) return FALSE + // Contaminate the container with viruses if the reagent has any. We do this here rather than a reaction because it only depends on the data, and not necessarily the reagent. + if(data && length(data["viruses"]) && isobj(my_atom)) + my_atom.AddComponent(/datum/component/viral_contamination, data["viruses"]) chem_temp = clamp((chem_temp * total_volume + reagtemp * amount) / (total_volume + amount), temperature_min, temperature_max) //equalize with new chems var/list/cached_reagents = reagent_list @@ -888,6 +915,11 @@ if(R.id == reagent_id) R.data = new_data +/datum/reagents/proc/copy_data_by_id(reagent_id) + for(var/datum/reagent/candidate in reagent_list) + if(candidate.id == reagent_id) + return(copy_data(candidate)) + /datum/reagents/proc/copy_data(datum/reagent/current_reagent) if(!current_reagent || !current_reagent.data) return null @@ -966,8 +998,7 @@ // Convenience proc to create a reagents holder for an atom // Max vol is maximum volume of holder /atom/proc/create_reagents(max_vol, temperature_minimum, temperature_maximum) - reagents = new /datum/reagents(max_vol, temperature_minimum, temperature_maximum) - reagents.my_atom = src + reagents = new /datum/reagents(max_vol, temperature_minimum, temperature_maximum, src) /proc/get_random_reagent_id() // Returns a random reagent ID minus blacklisted reagents var/static/list/random_reagents @@ -1012,8 +1043,11 @@ reagent_list = null QDEL_LIST_CONTENTS(addiction_list) addiction_list = null - if(my_atom && my_atom.reagents == src) - my_atom.reagents = null + if(my_atom) + UnregisterSignal(my_atom, COMSIG_ATOM_RAD_ACT) + REMOVE_TRAIT(my_atom, TRAIT_ABSORB_RADS, "reagents_holder_[UID()]") + if(my_atom.reagents == src) + my_atom.reagents = null my_atom = null #undef ADDICTION_TIME diff --git a/code/modules/reagents/chemistry/recipes/medicine_reactions.dm b/code/modules/reagents/chemistry/recipes/medicine_reactions.dm index e41efaaaff1..74db4cc4ddd 100644 --- a/code/modules/reagents/chemistry/recipes/medicine_reactions.dm +++ b/code/modules/reagents/chemistry/recipes/medicine_reactions.dm @@ -36,6 +36,13 @@ required_reagents = list("carpotoxin" = 1, "spaceacillin" = 1, "copper" = 1) result_amount = 3 +/datum/chemical_reaction/sterilizine + name = "Sterilizine" + id = "sterilizine" + result = "sterilizine" + required_reagents = list("antihol" = 2, "chlorine" = 1) + result_amount = 3 + /datum/chemical_reaction/charcoal name = "Charcoal" id = "charcoal" diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 1ef49b5c1ed..e8b5e7107ab 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -414,6 +414,20 @@ required_reagents = list("salglu_solution" = 1, "mutagenvirusfood" = 1) result_amount = 2 +/datum/chemical_reaction/virus_food_mutadone + name = "stable agar" + id = "stable_agar" + result = "stable_agar" + required_reagents = list("mutadone" = 1, "virusfood" = 1) + result_amount = 2 + +/datum/chemical_reaction/virus_food_tracker + name = "Tracking agar" + id = "tracking_agar" + result = "tracking_agar" + required_reagents = list("fluorosurfactant" = 1, "virusfood" = 1) + result_amount = 2 + /datum/chemical_reaction/mix_virus name = "Mix Virus" id = "mixvirus" @@ -499,6 +513,43 @@ if(D) D.Devolve() +/datum/chemical_reaction/mix_virus/stabilize_virus + name = "Stabilize Virus" + id = "stabilize_virus" + required_reagents = list("stable_agar" = 1) + +/datum/chemical_reaction/mix_virus/stabilize_virus/on_reaction(datum/reagents/holder, created_volume) + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list + if(B && B.data) + var/datum/disease/advance/D = locate(/datum/disease/advance) in B.data["viruses"] + if(istype(D)) + D.evolution_chance = 0 + +/datum/chemical_reaction/mix_virus/track_virus + name = "Track Virus" + id = "track_virus" + required_reagents = list("tracking_agar" = 1) + +/datum/chemical_reaction/mix_virus/track_virus/on_reaction(datum/reagents/holder, created_volume) + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list + if(B && B.data) + var/datum/disease/advance/D = locate(/datum/disease/advance) in B.data["viruses"] + if(istype(D)) + D.tracker = D.GetDiseaseID() + +/datum/chemical_reaction/mix_virus/clear_virus + name = "Clear Virus" + id = "clear_virus" + required_reagents = list("viral_eraser" = 10) + +/datum/chemical_reaction/mix_virus/clear_virus/on_reaction(datum/reagents/holder, created_volume) + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list + if(B && B.data) + var/datum/disease/advance/D = locate(/datum/disease/advance) in B.data["viruses"] + if(istype(D)) + D.tracker = "" + D.evolution_chance = VIRUS_EVOLUTION_CHANCE + // Someday, maybe add some version of doing science on patient zero to discover the recipees. /datum/chemical_reaction/zombie name = "Anti-Plague Sequence Alpha" @@ -575,7 +626,7 @@ result = "zombiecure4" cure_level = 4 amt_req_cures = 2 - required_symptom = /datum/symptom/heal/metabolism + required_symptom = /datum/symptom/heal /datum/chemical_reaction/zombie/four/get_possible_cures() return list("colorful_reagent", "bacchus_blessing", "pen_acid", "glyphosate", "lazarus_reagent", "omnizine", "sarin", "ants", "clf3", "sorium", "????", "aranesp") diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index bb712538c65..047fa52f653 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -512,6 +512,28 @@ /datum/chemical_reaction/slimemutate2/on_reaction(datum/reagents/holder) SSblackbox.record_feedback("tally", "slime_cores_used", 1, type) +/datum/chemical_reaction/viral_gene_extraction + name = "Virus Gene Extraction" + id = "virus_gene_extraction" + result = "virus_genes" + required_reagents = list("blood" = 1) + result_amount = 1 + required_other = TRUE + required_container = /obj/item/slime_extract/black + +/datum/chemical_reaction/viral_gene_extraction/on_reaction(datum/reagents/holder) + SSblackbox.record_feedback("tally", "slime_cores_used", 1, type) + var/obj/item/reagent_containers/glass/bottle/result_bottle = new(get_turf(holder.my_atom)) + holder.trans_to(result_bottle, holder.total_volume) + var/datum/reagent/virus_genes/result_genes = locate() in result_bottle.reagents.reagent_list + var/list/strains = list("slime" = list()) + if(result_genes.data && result_genes.data["viruses"]) + for(var/datum/disease/advance/advanced_virus in result_genes.data["viruses"]) + strains["slime"] += list(advanced_virus.strain) + result_bottle.name = "[advanced_virus.strain] Strain Viral Genetic Matter" + break + result_genes.data = strains + //Oil /datum/chemical_reaction/slime_explosion name = "Slime Explosion" diff --git a/code/modules/reagents/reagent_containers/chemical_bottle.dm b/code/modules/reagents/reagent_containers/chemical_bottle.dm index 0a3cd6f40e0..4300c6207ad 100644 --- a/code/modules/reagents/reagent_containers/chemical_bottle.dm +++ b/code/modules/reagents/reagent_containers/chemical_bottle.dm @@ -302,6 +302,26 @@ desc = "A small bottle. Contains H0NI<42 virion culture in synthblood medium." spawned_disease = /datum/disease/pierrot_throat +/obj/item/reagent_containers/glass/bottle/tracking_agar + name = "Tracking agar bottle" + desc = "A small bottle. Contains tracking agar which allows modifying a virus without needing to reanalyze it" + list_reagents = list("tracking_agar" = 50) + +/obj/item/reagent_containers/glass/bottle/stable_agar + name = "Stable agar bottle" + desc = "A small bottle. Contains stable agar which stop a virus from being able to mutate on its own" + list_reagents = list("stable_agar" = 50) + +/obj/item/reagent_containers/glass/bottle/sterilizine + name = "Sterilizine bottle" + desc = "A small bottle. Contains sterilizine, which is used to sterilize beakers and syringes" + list_reagents = list("sterilizine" = 50) + +/obj/item/reagent_containers/glass/bottle/viral_eraser + name = "Viral eraser bottle" + desc = "A small bottle. Contains a small amount of viral eraser which removes all tracking and stabilizing from a virus" + list_reagents = list("viral_eraser" = 10) + /obj/item/reagent_containers/glass/bottle/cold name = "Rhinovirus culture bottle" desc = "A small bottle. Contains XY-rhinovirus culture in synthblood medium." diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 6bcd97afdee..2b136c02963 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -63,11 +63,20 @@ add_attack_logs(user, target, "Fed with [name] containing [contained]", !!target.ckey ? null : ATKLOG_ALL) else to_chat(user, "You swallow a gulp of [src].") - var/fraction = min(5 / reagents.total_volume, 1) reagents.reaction(target, REAGENT_INGEST, fraction) addtimer(CALLBACK(reagents, TYPE_PROC_REF(/datum/reagents, trans_to), target, 5), 5) playsound(target.loc,'sound/items/drink.ogg', rand(10,50), TRUE) + // Add viruses where needed + if(length(target.viruses)) + AddComponent(/datum/component/viral_contamination, target.viruses) + // Infect contained blood as well for splash reactions + var/datum/reagent/blood/blood_contained = locate() in reagents.reagent_list + if(blood_contained?.data["viruses"]) + var/list/blood_viruses = blood_contained.data["viruses"] + blood_viruses |= target.viruses.Copy() + blood_contained.data["viruses"] = blood_viruses + SEND_SIGNAL(src, COMSIG_MOB_REAGENT_EXCHANGE, target) /obj/item/reagent_containers/glass/normal_act(atom/target, mob/living/user) if(!check_allowed_items(target, target_self = TRUE) || !is_open_container() || !reagents) diff --git a/code/modules/reagents/reagent_containers/iv_bag.dm b/code/modules/reagents/reagent_containers/iv_bag.dm index df7ee4f5a25..31129f2fcb8 100644 --- a/code/modules/reagents/reagent_containers/iv_bag.dm +++ b/code/modules/reagents/reagent_containers/iv_bag.dm @@ -54,6 +54,8 @@ /obj/item/reagent_containers/iv_bag/proc/begin_processing(mob/target) injection_target = target + if(target.viruses) + AddComponent(/datum/component/viral_contamination, target.viruses) RegisterSignal(injection_target, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) START_PROCESSING(SSobj, src) update_iv_type() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 84b8aa51b11..36846c4f05b 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -188,6 +188,10 @@ var/fraction = min(amount_per_transfer_from_this / reagents.total_volume, 1) reagents.reaction(target, REAGENT_INGEST, fraction) reagents.trans_to(target, amount_per_transfer_from_this) + if(iscarbon(target)) + var/mob/living/carbon/carbon_target = target + if(length(carbon_target.viruses)) + AddComponent(/datum/component/viral_contamination, carbon_target.viruses) to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The syringe now contains [reagents.total_volume] units.") if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) mode = SYRINGE_DRAW diff --git a/code/modules/supply/supply_packs/pack_medical.dm b/code/modules/supply/supply_packs/pack_medical.dm index 6c35eed849b..cc6af04e870 100644 --- a/code/modules/supply/supply_packs/pack_medical.dm +++ b/code/modules/supply/supply_packs/pack_medical.dm @@ -173,3 +173,32 @@ contains = list(/obj/item/reagent_containers/glass/bottle/reagent/omnizine) cost = 300 containername = "omnizine shipment crate" + +/datum/supply_packs/medical/biosuits + name = "Biosuit 4-pack" + contains = list( + /obj/item/clothing/suit/bio_suit, + /obj/item/clothing/suit/bio_suit, + /obj/item/clothing/suit/bio_suit, + /obj/item/clothing/suit/bio_suit, + /obj/item/clothing/head/bio_hood, + /obj/item/clothing/head/bio_hood, + /obj/item/clothing/head/bio_hood, + /obj/item/clothing/head/bio_hood, + ) + cost = 400 + containername = "biosuit shipment crate" + +/datum/supply_packs/medical/surgical_masks + name = "Sterile Mask And Goggles 4-pack" + contains = list(/obj/item/clothing/mask/surgical, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/glasses/goggles, + /obj/item/clothing/glasses/goggles, + /obj/item/clothing/glasses/goggles, + /obj/item/clothing/glasses/goggles, + ) + cost = 200 + containername = "sterile mask shipment crate" diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index f8aa52a60b2..24f3d6a706e 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -1,5 +1,5 @@ /**************************************************** - BLOOD SYSTEM + MARK: BLOOD SYSTEM ****************************************************/ #define EXOTIC_BLEED_MULTIPLIER 4 //Multiplies the actually bled amount by this number for the purposes of turf reaction calculations. @@ -134,7 +134,7 @@ bleed_rate = 0 /**************************************************** - BLOOD TRANSFERS + MARK: BLOOD TRANSFERS ****************************************************/ //Gets blood from mob to a container or other mob, preserving all data in it. @@ -153,6 +153,8 @@ blood_volume -= amount + SEND_SIGNAL(AM, COMSIG_MOB_REAGENT_EXCHANGE, src) + var/list/blood_data = get_blood_data(blood_id) if(iscarbon(AM)) @@ -162,7 +164,7 @@ if(blood_data["viruses"]) for(var/thing in blood_data["viruses"]) var/datum/disease/D = thing - if((D.spread_flags & SPECIAL) || (D.spread_flags & NON_CONTAGIOUS)) + if((D.spread_flags & SPREAD_SPECIAL) || (D.spread_flags & SPREAD_NON_CONTAGIOUS)) continue C.ForceContractDisease(D) if(!(blood_data?["blood_type"] in get_safe_blood(C.dna.blood_type)) || C.dna.species.name != blood_data["species"] && (blood_data["species_only"] || C.dna.species.own_species_blood)) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index fda729e751b..b8871bafdd8 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -483,6 +483,8 @@ if(SURGERY_BLOODSPREAD_FULLBODY) target.visible_message("A spray of blood coats [user].") H.bloody_body(target) + SEND_SIGNAL(tool, COMSIG_MOB_REAGENT_EXCHANGE, target) + tool.AddComponent(/datum/component/viral_contamination, target.viruses) /** * Finish a surgery step, performing anything that runs on the tail-end of a successful surgery. diff --git a/code/modules/virology/virology_goals.dm b/code/modules/virology/virology_goals.dm index e95a24da031..2f1c1c7ae55 100644 --- a/code/modules/virology/virology_goals.dm +++ b/code/modules/virology/virology_goals.dm @@ -4,9 +4,9 @@ GLOBAL_LIST_EMPTY(archived_virology_goals) /datum/virology_goal var/name = "Generic Virology Goal" - /// The amount of units currently already delivered - var/delivered_amount = 0 - /// The amount of units of the required virus that must be delivered for the completion of this goal + /// The amount of units currently already delivered + var/delivered_amount = 0 + /// The amount of units of the required virus that must be delivered for the completion of this goal var/delivery_goal = 15 var/completed = FALSE @@ -80,7 +80,7 @@ GLOBAL_LIST_EMPTY(archived_virology_goals) return GLOB.archived_virology_goals += GLOB.virology_goals GLOB.virology_goals = list(new /datum/virology_goal/property_symptom, new /datum/virology_goal/virus, new /datum/virology_goal/virus/stealth) - for(var/obj/machinery/computer/pandemic/P in GLOB.pandemics) + for(var/obj/machinery/pandemic/P in GLOB.pandemics) P.print_goal_orders() /datum/virology_goal/Destroy() @@ -112,7 +112,7 @@ GLOBAL_LIST_EMPTY(archived_virology_goals) var/datum/symptom/S = new type() goal_symptom = S.type goal_symptom_name = S.name - goal_property = pick("resistance", "stealth", "stage rate", "transmittable") + goal_property = pick("resistance", "stealth", "stage rate", "transmissibility") goal_property_value = rand(-8, 6) switch(goal_property) if("resistance") @@ -121,8 +121,8 @@ GLOBAL_LIST_EMPTY(archived_virology_goals) goal_property_value += S.stealth if("stage rate") goal_property_value += S.stage_speed - if("transmittable") - goal_property_value += S.transmittable + if("transmissibility") + goal_property_value += S.transmissibility qdel(S) times_looped++ while(check_for_duplicate() && times_looped < MAX_LOOPS) diff --git a/paradise.dme b/paradise.dme index 84c1f0324b9..2fa167220dc 100644 --- a/paradise.dme +++ b/paradise.dme @@ -59,6 +59,7 @@ #include "code\__DEFINES\cult_defines.dm" #include "code\__DEFINES\departments_defines.dm" #include "code\__DEFINES\directions.dm" +#include "code\__DEFINES\disease_defines.dm" #include "code\__DEFINES\dna.dm" #include "code\__DEFINES\dye_keys.dm" #include "code\__DEFINES\economy_defines.dm" @@ -166,6 +167,7 @@ #include "code\__DEFINES\dcs\dcs_flags.dm" #include "code\__DEFINES\dcs\dcs_helpers.dm" #include "code\__DEFINES\dcs\dept_signals.dm" +#include "code\__DEFINES\dcs\disease_signals.dm" #include "code\__DEFINES\dcs\global_signals.dm" #include "code\__DEFINES\dcs\item_signals.dm" #include "code\__DEFINES\dcs\machinery_signals.dm" @@ -567,6 +569,7 @@ #include "code\datums\components\tilted.dm" #include "code\datums\components\tippable.dm" #include "code\datums\components\two_handed.dm" +#include "code\datums\components\viral_contamination.dm" #include "code\datums\components\zombie_regen.dm" #include "code\datums\components\pet_commands\obeys_commands.dm" #include "code\datums\components\pet_commands\pet_command.dm" diff --git a/sound/AI/outbreak_virus.ogg b/sound/AI/outbreak_virus.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c98cf8ea0c429d6f64201d0a4130c7eda9052f26 GIT binary patch literal 89732 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 A9RL6T literal 0 HcmV?d00001 diff --git a/tgui/packages/tgui/interfaces/PanDEMIC.tsx b/tgui/packages/tgui/interfaces/PanDEMIC.tsx index b2599ab9c2e..c03aa083a1f 100644 --- a/tgui/packages/tgui/interfaces/PanDEMIC.tsx +++ b/tgui/packages/tgui/interfaces/PanDEMIC.tsx @@ -1,30 +1,65 @@ import { ReactNode } from 'react'; -import { Button, Flex, LabeledList, NoticeBox, Section, Stack, Table, Tabs } from 'tgui-core/components'; +import { + Button, + Dimmer, + Dropdown, + Flex, + Icon, + LabeledList, + Modal, + NoticeBox, + ProgressBar, + Section, + Stack, + Table, + Tabs, +} from 'tgui-core/components'; import { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../backend'; import { Window } from '../layouts'; +interface BaseStats { + resistance: number; + stealth: number; + stageSpeed: number; + transmissibility: number; +} interface PathogenSymptom { name: string; stealth: number; resistance: number; stageSpeed: number; transmissibility: number; + guess: string; } interface PathogenStrain { commonName?: string; description?: string; + strainID?: string; + strainFullID?: string; + diseaseID?: string; + sample_stage?: number; + known?: BooleanLike; bloodDNA?: string; bloodType?: string; diseaseAgent: string; - possibleTreatments?: string; + possibleCures?: string; transmissionRoute?: string; symptoms: PathogenSymptom[]; + baseStats: BaseStats; isAdvanced: BooleanLike; + RequiredCures: string; + Stabilized: BooleanLike; + StrainTracker: string; } +interface Contribution { + factor: string; + amount: number; + maxAmount: number; +} interface PanDEMICData { synthesisCooldown: BooleanLike; beakerLoaded: BooleanLike; @@ -33,12 +68,21 @@ interface PanDEMICData { selectedStrainIndex: number; strains: PathogenStrain[]; resistances: string[]; + analysisDuration: number; + analysisTime: number; + canAnalyze: boolean; + analyzing: boolean; + reporting: boolean; + analysisDifficulty: number; + analysisContributions: Contribution[]; + totalContribution: number; + symptom_names: string[]; + predictions: string[]; } export const PanDEMIC = (props) => { const { data } = useBackend(); - const { beakerLoaded, beakerContainsBlood, beakerContainsVirus, resistances = [] } = data; - + const { reporting, analyzing, beakerLoaded, beakerContainsBlood, beakerContainsVirus, resistances = [] } = data; let emptyPlaceholder; if (!beakerLoaded) { emptyPlaceholder = <>No container loaded.; @@ -49,9 +93,10 @@ export const PanDEMIC = (props) => { } return ( - + + {emptyPlaceholder && !beakerContainsVirus ? (
}> {emptyPlaceholder} @@ -66,6 +111,55 @@ export const PanDEMIC = (props) => { ); }; +const AnalysisDimmer = (props) => { + const { operating } = props; + const { act, data } = useBackend(); + const { analyzing, analysisTime, analysisDuration, analysisDifficulty, totalContribution, analysisContributions } = + data; + const analysisReport = ( + <> + {Math.ceil(analysisDifficulty)} + + {analysisContributions.map((contribution, i) => ( + + {Math.floor(contribution.amount)} + + ))} + {Math.floor(totalContribution)} + + {analysisDifficulty - totalContribution < 0 ? 'Disease Analyzed Successfully' : 'Analysis Failed'} + + + ); + if (operating) { + if (analyzing) { + return ( + + + + +
+ Analyzing {Math.floor(100 - (100 * analysisTime) / analysisDuration)}% +
+
+
+ ); + } + return ( + +
+ + {analysisReport} + + +
+
+ ); + } +}; + const CommonCultureActions = (props) => { const { act, data } = useBackend(); const { beakerLoaded } = data; @@ -86,16 +180,22 @@ const CommonCultureActions = (props) => { const StrainInformation = (props: { strain: PathogenStrain; strainIndex: number }) => { const { act, data } = useBackend(); - const { beakerContainsVirus } = data; + const { analysisDifficulty, analysisContributions, beakerContainsVirus, analyzing, canAnalyze } = data; const { commonName, description, + strainID, + sample_stage, + known, diseaseAgent, bloodDNA, bloodType, - possibleTreatments, + possibleCures, transmissionRoute, isAdvanced, + RequiredCures, + Stabilized, + StrainTracker, } = props.strain; const bloodInformation = ( @@ -119,6 +219,31 @@ const StrainInformation = (props: { strain: PathogenStrain; strainIndex: number return {bloodInformation}; } + const analysisInformation = ( + <> + {Math.ceil(analysisDifficulty)} + {analysisContributions.map((contribution, i) => ( + + + {contribution.amount} + + + ))} + + ); + let nameButtons; if (isAdvanced) { if (commonName !== undefined && commonName !== null && commonName !== 'Unknown') { @@ -138,29 +263,71 @@ const StrainInformation = (props: { strain: PathogenStrain; strainIndex: number nameButtons = (